#
tokens: 12242/50000 19/19 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .env.example
├── package-lock.json
├── package.json
├── README.md
└── src
    ├── index.js
    ├── resources
    │   └── api-docs.js
    ├── server.js
    ├── tools
    │   ├── account.js
    │   ├── chat.js
    │   ├── contacts.js
    │   ├── meetings.js
    │   ├── phone.js
    │   ├── recordings.js
    │   ├── reports.js
    │   ├── users.js
    │   ├── webhooks.js
    │   ├── webinars.js
    │   └── zoom-rooms.js
    └── utils
        ├── api.js
        └── auth.js
```

# Files

--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------

```
# Zoom API Credentials
    ZOOM_CLIENT_ID=your_client_id
    ZOOM_CLIENT_SECRET=your_client_secret
    ZOOM_ACCOUNT_ID=your_account_id

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# Zoom API MCP Server

    A comprehensive Model Context Protocol (MCP) server for interacting with the Zoom API.

    ## Features

    - Complete coverage of Zoom API endpoints
    - OAuth 2.0 authentication
    - Structured tools with proper validation
    - API documentation resources
    - Error handling and response formatting

    ## Getting Started

    ### Prerequisites

    - Node.js 16+
    - Zoom API credentials (Client ID, Client Secret, Account ID)

    ### Installation

    1. Clone the repository
    2. Install dependencies:
       ```
       npm install
       ```
    3. Create a `.env` file with your Zoom API credentials:
       ```
       ZOOM_CLIENT_ID=your_client_id
       ZOOM_CLIENT_SECRET=your_client_secret
       ZOOM_ACCOUNT_ID=your_account_id
       ```

    ### Running the Server

    ```
    npm run dev
    ```

    ### Testing with MCP Inspector

    ```
    npm run inspect
    ```

    ## API Categories

    - **Meetings**: Create, read, update, and delete meetings
    - **Users**: Manage users in your Zoom account
    - **Webinars**: Create and manage webinars
    - **Account**: Manage account settings and profile
    - **Chat**: Manage Zoom Chat channels and messages
    - **Phone**: Manage Zoom Phone users and numbers
    - **Contacts**: Manage contacts
    - **Recordings**: Access and manage cloud recordings
    - **Reports**: Generate various reports
    - **Webhooks**: Set up event notifications
    - **Zoom Rooms**: Manage Zoom Rooms

    ## Resources

    Access API documentation through resources:

    ```
    zoom-api://overview
    zoom-api://meetings
    zoom-api://users
    ```

    ## Authentication

    The server handles OAuth 2.0 authentication automatically using the Server-to-Server OAuth app credentials.

```

--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------

```javascript
#!/usr/bin/env node
    import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
    import { server } from './server.js';
    import dotenv from 'dotenv';

    // Load environment variables
    dotenv.config();

    console.log('Starting Zoom API MCP server...');

    // Start receiving messages on stdin and sending messages on stdout
    const transport = new StdioServerTransport();
    await server.connect(transport);

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
      "name": "zoom-mcp-server",
      "version": "1.0.0",
      "type": "module",
      "description": "MCP Server for Zoom API",
      "main": "src/index.js",
      "bin": {
        "zoom-mcp-server": "src/index.js"
      },
      "scripts": {
        "start": "node src/index.js",
        "dev": "node --watch src/index.js",
        "inspect": "npx -y @modelcontextprotocol/inspector node src/index.js"
      },
      "dependencies": {
        "@modelcontextprotocol/sdk": "^1.0.0",
        "axios": "^1.6.2",
        "dotenv": "^16.3.1",
        "zod": "^3.22.4"
      }
    }

```

--------------------------------------------------------------------------------
/src/utils/api.js:
--------------------------------------------------------------------------------

```javascript
import axios from 'axios';
    import { getAccessToken } from './auth.js';

    const BASE_URL = 'https://api.zoom.us/v2';

    // Create an axios instance for Zoom API
    export const zoomApi = axios.create({
      baseURL: BASE_URL,
      headers: {
        'Content-Type': 'application/json'
      }
    });

    // Request interceptor to add authorization header
    zoomApi.interceptors.request.use(async (config) => {
      const token = await getAccessToken();
      config.headers.Authorization = `Bearer ${token}`;
      return config;
    });

    // Response interceptor to handle errors
    zoomApi.interceptors.response.use(
      (response) => response,
      (error) => {
        console.error('API Error:', error.response?.data || error.message);
        return Promise.reject(error);
      }
    );

    // Helper function to handle API responses
    export const handleApiResponse = (response) => {
      return {
        content: [{ 
          type: "text", 
          text: JSON.stringify(response.data, null, 2)
        }]
      };
    };

    // Helper function to handle API errors
    export const handleApiError = (error) => {
      const errorMessage = error.response?.data?.message || error.message;
      return {
        content: [{ 
          type: "text", 
          text: `Error: ${errorMessage}`
        }],
        isError: true
      };
    };

```

--------------------------------------------------------------------------------
/src/utils/auth.js:
--------------------------------------------------------------------------------

```javascript
import axios from 'axios';

    // Cache for the access token
    let accessToken = null;
    let tokenExpiry = 0;

    // Get access token using client credentials flow
    export const getAccessToken = async () => {
      // Check if we have a valid token
      if (accessToken && tokenExpiry > Date.now()) {
        return accessToken;
      }

      try {
        const clientId = process.env.ZOOM_CLIENT_ID;
        const clientSecret = process.env.ZOOM_CLIENT_SECRET;
        const accountId = process.env.ZOOM_ACCOUNT_ID;

        if (!clientId || !clientSecret || !accountId) {
          throw new Error('Missing Zoom API credentials. Please set ZOOM_CLIENT_ID, ZOOM_CLIENT_SECRET, and ZOOM_ACCOUNT_ID in .env file.');
        }

        // Get new token
        const response = await axios.post(
          'https://zoom.us/oauth/token',
          null,
          {
            params: {
              grant_type: 'account_credentials',
              account_id: accountId
            },
            headers: {
              'Authorization': `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`
            }
          }
        );

        accessToken = response.data.access_token;
        // Set expiry time with a small buffer
        tokenExpiry = Date.now() + (response.data.expires_in * 1000) - 60000;
        
        return accessToken;
      } catch (error) {
        console.error('Error getting access token:', error.response?.data || error.message);
        throw error;
      }
    };

```

--------------------------------------------------------------------------------
/src/server.js:
--------------------------------------------------------------------------------

```javascript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
    import { meetingsTools } from './tools/meetings.js';
    import { usersTools } from './tools/users.js';
    import { webinarsTools } from './tools/webinars.js';
    import { accountTools } from './tools/account.js';
    import { chatTools } from './tools/chat.js';
    import { phoneTools } from './tools/phone.js';
    import { contactsTools } from './tools/contacts.js';
    import { recordingsTools } from './tools/recordings.js';
    import { reportsTools } from './tools/reports.js';
    import { webhooksTools } from './tools/webhooks.js';
    import { zoomRoomsTools } from './tools/zoom-rooms.js';
    import { apiDocs } from './resources/api-docs.js';

    // Create an MCP server for Zoom API
    const server = new McpServer({
      name: "Zoom API",
      version: "1.0.0",
      description: "MCP Server for interacting with the Zoom API"
    });

    // Register all tools
    const registerTools = (toolsArray) => {
      toolsArray.forEach(tool => {
        server.tool(
          tool.name,
          tool.schema,
          tool.handler,
          { description: tool.description }
        );
      });
    };

    // Register all resources
    const registerResources = (resourcesArray) => {
      resourcesArray.forEach(resource => {
        server.resource(
          resource.name,
          resource.template,
          resource.handler
        );
      });
    };

    // Register all tools
    registerTools(meetingsTools);
    registerTools(usersTools);
    registerTools(webinarsTools);
    registerTools(accountTools);
    registerTools(chatTools);
    registerTools(phoneTools);
    registerTools(contactsTools);
    registerTools(recordingsTools);
    registerTools(reportsTools);
    registerTools(webhooksTools);
    registerTools(zoomRoomsTools);

    // Register all resources
    registerResources(apiDocs);

    export { server };

```

--------------------------------------------------------------------------------
/src/tools/contacts.js:
--------------------------------------------------------------------------------

```javascript
import { z } from 'zod';
    import { zoomApi, handleApiResponse, handleApiError } from '../utils/api.js';

    export const contactsTools = [
      {
        name: "list_contacts",
        description: "List contacts",
        schema: {
          page_size: z.number().min(1).max(300).optional().describe("Number of records returned"),
          next_page_token: z.string().optional().describe("Next page token"),
          status: z.enum(["active", "inactive", "pending"]).optional().describe("Contact status")
        },
        handler: async ({ page_size, next_page_token, status }) => {
          try {
            const params = {};
            if (page_size) params.page_size = page_size;
            if (next_page_token) params.next_page_token = next_page_token;
            if (status) params.status = status;
            
            const response = await zoomApi.get('/contacts', { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "get_contact",
        description: "Get a contact's information",
        schema: {
          contact_id: z.string().describe("The contact ID")
        },
        handler: async ({ contact_id }) => {
          try {
            const response = await zoomApi.get(`/contacts/${contact_id}`);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "search_company_contacts",
        description: "Search company contacts",
        schema: {
          query_string: z.string().describe("Search query string"),
          page_size: z.number().min(1).max(300).optional().describe("Number of records returned"),
          next_page_token: z.string().optional().describe("Next page token")
        },
        handler: async ({ query_string, page_size, next_page_token }) => {
          try {
            const params = { query_string };
            if (page_size) params.page_size = page_size;
            if (next_page_token) params.next_page_token = next_page_token;
            
            const response = await zoomApi.get('/contacts/search', { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      }
    ];

```

--------------------------------------------------------------------------------
/src/tools/phone.js:
--------------------------------------------------------------------------------

```javascript
import { z } from 'zod';
    import { zoomApi, handleApiResponse, handleApiError } from '../utils/api.js';

    export const phoneTools = [
      {
        name: "list_phone_users",
        description: "List phone users",
        schema: {
          page_size: z.number().min(1).max(300).optional().describe("Number of records returned"),
          next_page_token: z.string().optional().describe("Next page token"),
          site_id: z.string().optional().describe("Site ID")
        },
        handler: async ({ page_size, next_page_token, site_id }) => {
          try {
            const params = {};
            if (page_size) params.page_size = page_size;
            if (next_page_token) params.next_page_token = next_page_token;
            if (site_id) params.site_id = site_id;
            
            const response = await zoomApi.get('/phone/users', { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "get_phone_user",
        description: "Get a phone user's information",
        schema: {
          user_id: z.string().describe("The user ID or email address")
        },
        handler: async ({ user_id }) => {
          try {
            const response = await zoomApi.get(`/phone/users/${user_id}`);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "update_phone_user",
        description: "Update a phone user's information",
        schema: {
          user_id: z.string().describe("The user ID or email address"),
          extension_number: z.string().optional().describe("Extension number"),
          site_id: z.string().optional().describe("Site ID"),
          policy_id: z.string().optional().describe("Policy ID")
        },
        handler: async ({ user_id, ...userData }) => {
          try {
            const response = await zoomApi.patch(`/phone/users/${user_id}`, userData);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "list_phone_numbers",
        description: "List phone numbers",
        schema: {
          type: z.enum(["assigned", "unassigned", "all"]).optional().describe("Phone number type"),
          page_size: z.number().min(1).max(300).optional().describe("Number of records returned"),
          next_page_token: z.string().optional().describe("Next page token")
        },
        handler: async ({ type, page_size, next_page_token }) => {
          try {
            const params = {};
            if (type) params.type = type;
            if (page_size) params.page_size = page_size;
            if (next_page_token) params.next_page_token = next_page_token;
            
            const response = await zoomApi.get('/phone/numbers', { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      }
    ];

```

--------------------------------------------------------------------------------
/src/tools/webhooks.js:
--------------------------------------------------------------------------------

```javascript
import { z } from 'zod';
    import { zoomApi, handleApiResponse, handleApiError } from '../utils/api.js';

    export const webhooksTools = [
      {
        name: "list_webhooks",
        description: "List webhooks",
        schema: {},
        handler: async () => {
          try {
            const response = await zoomApi.get('/webhooks');
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "create_webhook",
        description: "Create a webhook",
        schema: {
          url: z.string().url().describe("Webhook URL"),
          event_types: z.array(z.string()).describe("Event types to subscribe to"),
          authorization_header: z.string().optional().describe("Authorization header"),
          description: z.string().optional().describe("Webhook description")
        },
        handler: async (webhookData) => {
          try {
            const response = await zoomApi.post('/webhooks', webhookData);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "get_webhook",
        description: "Get a webhook's information",
        schema: {
          webhook_id: z.string().describe("The webhook ID")
        },
        handler: async ({ webhook_id }) => {
          try {
            const response = await zoomApi.get(`/webhooks/${webhook_id}`);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "update_webhook",
        description: "Update a webhook's information",
        schema: {
          webhook_id: z.string().describe("The webhook ID"),
          url: z.string().url().optional().describe("Webhook URL"),
          event_types: z.array(z.string()).optional().describe("Event types to subscribe to"),
          authorization_header: z.string().optional().describe("Authorization header"),
          description: z.string().optional().describe("Webhook description"),
          status: z.enum(["active", "inactive"]).optional().describe("Webhook status")
        },
        handler: async ({ webhook_id, ...webhookData }) => {
          try {
            const response = await zoomApi.patch(`/webhooks/${webhook_id}`, webhookData);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "delete_webhook",
        description: "Delete a webhook",
        schema: {
          webhook_id: z.string().describe("The webhook ID")
        },
        handler: async ({ webhook_id }) => {
          try {
            const response = await zoomApi.delete(`/webhooks/${webhook_id}`);
            return {
              content: [{ 
                type: "text", 
                text: "Webhook deleted successfully"
              }]
            };
          } catch (error) {
            return handleApiError(error);
          }
        }
      }
    ];

```

--------------------------------------------------------------------------------
/src/tools/reports.js:
--------------------------------------------------------------------------------

```javascript
import { z } from 'zod';
    import { zoomApi, handleApiResponse, handleApiError } from '../utils/api.js';

    export const reportsTools = [
      {
        name: "get_daily_report",
        description: "Get daily usage report",
        schema: {
          year: z.number().describe("Year"),
          month: z.number().min(1).max(12).describe("Month")
        },
        handler: async ({ year, month }) => {
          try {
            const response = await zoomApi.get(`/report/daily`, {
              params: { year, month }
            });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "get_meeting_participants_report",
        description: "Get meeting participants report",
        schema: {
          meeting_id: z.string().describe("The meeting ID"),
          page_size: z.number().min(1).max(300).optional().describe("Number of records returned"),
          next_page_token: z.string().optional().describe("Next page token")
        },
        handler: async ({ meeting_id, page_size, next_page_token }) => {
          try {
            const params = {};
            if (page_size) params.page_size = page_size;
            if (next_page_token) params.next_page_token = next_page_token;
            
            const response = await zoomApi.get(`/report/meetings/${meeting_id}/participants`, { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "get_meeting_details_report",
        description: "Get meeting details report",
        schema: {
          meeting_id: z.string().describe("The meeting ID")
        },
        handler: async ({ meeting_id }) => {
          try {
            const response = await zoomApi.get(`/report/meetings/${meeting_id}`);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "get_webinar_participants_report",
        description: "Get webinar participants report",
        schema: {
          webinar_id: z.string().describe("The webinar ID"),
          page_size: z.number().min(1).max(300).optional().describe("Number of records returned"),
          next_page_token: z.string().optional().describe("Next page token")
        },
        handler: async ({ webinar_id, page_size, next_page_token }) => {
          try {
            const params = {};
            if (page_size) params.page_size = page_size;
            if (next_page_token) params.next_page_token = next_page_token;
            
            const response = await zoomApi.get(`/report/webinars/${webinar_id}/participants`, { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "get_webinar_details_report",
        description: "Get webinar details report",
        schema: {
          webinar_id: z.string().describe("The webinar ID")
        },
        handler: async ({ webinar_id }) => {
          try {
            const response = await zoomApi.get(`/report/webinars/${webinar_id}`);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      }
    ];

```

--------------------------------------------------------------------------------
/src/tools/recordings.js:
--------------------------------------------------------------------------------

```javascript
import { z } from 'zod';
    import { zoomApi, handleApiResponse, handleApiError } from '../utils/api.js';

    export const recordingsTools = [
      {
        name: "list_recordings",
        description: "List all recordings for a user",
        schema: {
          user_id: z.string().describe("The user ID or email address"),
          page_size: z.number().min(1).max(300).optional().describe("Number of records returned"),
          next_page_token: z.string().optional().describe("Next page token"),
          from: z.string().optional().describe("Start date in 'yyyy-MM-dd' format"),
          to: z.string().optional().describe("End date in 'yyyy-MM-dd' format")
        },
        handler: async ({ user_id, page_size, next_page_token, from, to }) => {
          try {
            const params = {};
            if (page_size) params.page_size = page_size;
            if (next_page_token) params.next_page_token = next_page_token;
            if (from) params.from = from;
            if (to) params.to = to;
            
            const response = await zoomApi.get(`/users/${user_id}/recordings`, { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "get_meeting_recordings",
        description: "Get recordings for a specific meeting",
        schema: {
          meeting_id: z.string().describe("The meeting ID")
        },
        handler: async ({ meeting_id }) => {
          try {
            const response = await zoomApi.get(`/meetings/${meeting_id}/recordings`);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "delete_meeting_recordings",
        description: "Delete recordings for a specific meeting",
        schema: {
          meeting_id: z.string().describe("The meeting ID"),
          action: z.enum(["trash", "delete"]).optional().describe("Delete action (trash: move to trash, delete: delete permanently)")
        },
        handler: async ({ meeting_id, action }) => {
          try {
            const params = {};
            if (action) params.action = action;
            
            const response = await zoomApi.delete(`/meetings/${meeting_id}/recordings`, { params });
            return {
              content: [{ 
                type: "text", 
                text: "Meeting recordings deleted successfully"
              }]
            };
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "delete_recording_file",
        description: "Delete a specific recording file",
        schema: {
          meeting_id: z.string().describe("The meeting ID"),
          recording_id: z.string().describe("The recording ID"),
          action: z.enum(["trash", "delete"]).optional().describe("Delete action (trash: move to trash, delete: delete permanently)")
        },
        handler: async ({ meeting_id, recording_id, action }) => {
          try {
            const params = {};
            if (action) params.action = action;
            
            const response = await zoomApi.delete(`/meetings/${meeting_id}/recordings/${recording_id}`, { params });
            return {
              content: [{ 
                type: "text", 
                text: "Recording file deleted successfully"
              }]
            };
          } catch (error) {
            return handleApiError(error);
          }
        }
      }
    ];

```

--------------------------------------------------------------------------------
/src/tools/account.js:
--------------------------------------------------------------------------------

```javascript
import { z } from 'zod';
    import { zoomApi, handleApiResponse, handleApiError } from '../utils/api.js';

    export const accountTools = [
      {
        name: "get_account_settings",
        description: "Get account settings",
        schema: {
          option: z.enum(["meeting_authentication", "recording_authentication", "security"]).optional().describe("Setting option to query")
        },
        handler: async ({ option }) => {
          try {
            const params = {};
            if (option) params.option = option;
            
            const response = await zoomApi.get('/accounts/me/settings', { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "update_account_settings",
        description: "Update account settings",
        schema: {
          settings: z.object({}).passthrough().describe("Account settings to update")
        },
        handler: async ({ settings }) => {
          try {
            const response = await zoomApi.patch('/accounts/me/settings', settings);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "get_account_profile",
        description: "Get account profile information",
        schema: {},
        handler: async () => {
          try {
            const response = await zoomApi.get('/accounts/me');
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "list_sub_accounts",
        description: "List sub accounts",
        schema: {
          page_size: z.number().min(1).max(300).optional().describe("Number of records returned"),
          page_number: z.number().min(1).optional().describe("Page number")
        },
        handler: async ({ page_size, page_number }) => {
          try {
            const params = {};
            if (page_size) params.page_size = page_size;
            if (page_number) params.page_number = page_number;
            
            const response = await zoomApi.get('/accounts', { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "create_sub_account",
        description: "Create a sub account",
        schema: {
          first_name: z.string().describe("First name of the account owner"),
          last_name: z.string().describe("Last name of the account owner"),
          email: z.string().email().describe("Email address of the account owner"),
          password: z.string().describe("Password"),
          phone_country: z.string().optional().describe("Country for phone"),
          phone_number: z.string().optional().describe("Phone number"),
          company_name: z.string().describe("Company name"),
          address: z.string().optional().describe("Address"),
          city: z.string().optional().describe("City"),
          state: z.string().optional().describe("State/Province"),
          zip: z.string().optional().describe("ZIP/Postal Code"),
          country: z.string().describe("Country")
        },
        handler: async (accountData) => {
          try {
            const response = await zoomApi.post('/accounts', accountData);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      }
    ];

```

--------------------------------------------------------------------------------
/src/tools/chat.js:
--------------------------------------------------------------------------------

```javascript
import { z } from 'zod';
    import { zoomApi, handleApiResponse, handleApiError } from '../utils/api.js';

    export const chatTools = [
      {
        name: "list_channels",
        description: "List channels",
        schema: {
          page_size: z.number().min(1).max(300).optional().describe("Number of records returned"),
          next_page_token: z.string().optional().describe("Next page token")
        },
        handler: async ({ page_size, next_page_token }) => {
          try {
            const params = {};
            if (page_size) params.page_size = page_size;
            if (next_page_token) params.next_page_token = next_page_token;
            
            const response = await zoomApi.get('/chat/users/me/channels', { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "create_channel",
        description: "Create a channel",
        schema: {
          name: z.string().describe("Channel name"),
          type: z.number().min(1).max(2).describe("Channel type (1: Private, 2: Public)"),
          members: z.array(z.object({
            email: z.string().email().describe("Member email address")
          })).optional().describe("Channel members")
        },
        handler: async (channelData) => {
          try {
            const response = await zoomApi.post('/chat/users/me/channels', channelData);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "get_channel",
        description: "Get a channel's information",
        schema: {
          channel_id: z.string().describe("The channel ID")
        },
        handler: async ({ channel_id }) => {
          try {
            const response = await zoomApi.get(`/chat/channels/${channel_id}`);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "update_channel",
        description: "Update a channel's information",
        schema: {
          channel_id: z.string().describe("The channel ID"),
          name: z.string().optional().describe("Channel name")
        },
        handler: async ({ channel_id, ...channelData }) => {
          try {
            const response = await zoomApi.patch(`/chat/channels/${channel_id}`, channelData);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "delete_channel",
        description: "Delete a channel",
        schema: {
          channel_id: z.string().describe("The channel ID")
        },
        handler: async ({ channel_id }) => {
          try {
            const response = await zoomApi.delete(`/chat/channels/${channel_id}`);
            return {
              content: [{ 
                type: "text", 
                text: "Channel deleted successfully"
              }]
            };
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "send_channel_message",
        description: "Send a message to a channel",
        schema: {
          channel_id: z.string().describe("The channel ID"),
          message: z.string().describe("Message content")
        },
        handler: async ({ channel_id, message }) => {
          try {
            const response = await zoomApi.post(`/chat/users/me/channels/${channel_id}/messages`, {
              message
            });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      }
    ];

```

--------------------------------------------------------------------------------
/src/tools/zoom-rooms.js:
--------------------------------------------------------------------------------

```javascript
import { z } from 'zod';
    import { zoomApi, handleApiResponse, handleApiError } from '../utils/api.js';

    export const zoomRoomsTools = [
      {
        name: "list_zoom_rooms",
        description: "List Zoom Rooms",
        schema: {
          page_size: z.number().min(1).max(300).optional().describe("Number of records returned"),
          page_number: z.number().min(1).optional().describe("Page number"),
          location_id: z.string().optional().describe("Location ID")
        },
        handler: async ({ page_size, page_number, location_id }) => {
          try {
            const params = {};
            if (page_size) params.page_size = page_size;
            if (page_number) params.page_number = page_number;
            if (location_id) params.location_id = location_id;
            
            const response = await zoomApi.get('/rooms', { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "get_zoom_room",
        description: "Get a Zoom Room's information",
        schema: {
          room_id: z.string().describe("The Zoom Room ID")
        },
        handler: async ({ room_id }) => {
          try {
            const response = await zoomApi.get(`/rooms/${room_id}`);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "update_zoom_room",
        description: "Update a Zoom Room's information",
        schema: {
          room_id: z.string().describe("The Zoom Room ID"),
          name: z.string().optional().describe("Room name"),
          location_id: z.string().optional().describe("Location ID"),
          calendar_resource_id: z.string().optional().describe("Calendar resource ID"),
          room_passcode: z.string().optional().describe("Room passcode")
        },
        handler: async ({ room_id, ...roomData }) => {
          try {
            const response = await zoomApi.patch(`/rooms/${room_id}`, roomData);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "list_zoom_room_locations",
        description: "List Zoom Room locations",
        schema: {
          page_size: z.number().min(1).max(300).optional().describe("Number of records returned"),
          page_number: z.number().min(1).optional().describe("Page number"),
          parent_location_id: z.string().optional().describe("Parent location ID")
        },
        handler: async ({ page_size, page_number, parent_location_id }) => {
          try {
            const params = {};
            if (page_size) params.page_size = page_size;
            if (page_number) params.page_number = page_number;
            if (parent_location_id) params.parent_location_id = parent_location_id;
            
            const response = await zoomApi.get('/rooms/locations', { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "create_zoom_room_location",
        description: "Create a Zoom Room location",
        schema: {
          name: z.string().describe("Location name"),
          parent_location_id: z.string().optional().describe("Parent location ID"),
          type: z.enum(["country", "state", "city", "campus", "building", "floor"]).describe("Location type")
        },
        handler: async (locationData) => {
          try {
            const response = await zoomApi.post('/rooms/locations', locationData);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      }
    ];

```

--------------------------------------------------------------------------------
/src/tools/users.js:
--------------------------------------------------------------------------------

```javascript
import { z } from 'zod';
    import { zoomApi, handleApiResponse, handleApiError } from '../utils/api.js';

    export const usersTools = [
      {
        name: "list_users",
        description: "List users on the account",
        schema: {
          status: z.enum(["active", "inactive", "pending"]).optional().describe("User status"),
          page_size: z.number().min(1).max(300).optional().describe("Number of records returned"),
          page_number: z.number().min(1).optional().describe("Page number"),
          role_id: z.string().optional().describe("Role ID")
        },
        handler: async ({ status, page_size, page_number, role_id }) => {
          try {
            const params = {};
            if (status) params.status = status;
            if (page_size) params.page_size = page_size;
            if (page_number) params.page_number = page_number;
            if (role_id) params.role_id = role_id;
            
            const response = await zoomApi.get('/users', { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "create_user",
        description: "Create a new user on the account",
        schema: {
          action: z.enum(["create", "autoCreate", "custCreate", "ssoCreate"]).describe("Action to create user"),
          user_info: z.object({
            email: z.string().email().describe("User email address"),
            type: z.number().min(1).max(99).describe("User type (1: Basic, 2: Licensed, 3: On-prem)"),
            first_name: z.string().optional().describe("User's first name"),
            last_name: z.string().optional().describe("User's last name"),
            password: z.string().optional().describe("User password")
          }).describe("User information")
        },
        handler: async ({ action, user_info }) => {
          try {
            const response = await zoomApi.post('/users', { action, user_info });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "get_user",
        description: "Get a user's information",
        schema: {
          user_id: z.string().describe("The user ID or email address")
        },
        handler: async ({ user_id }) => {
          try {
            const response = await zoomApi.get(`/users/${user_id}`);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "update_user",
        description: "Update a user's information",
        schema: {
          user_id: z.string().describe("The user ID or email address"),
          first_name: z.string().optional().describe("User's first name"),
          last_name: z.string().optional().describe("User's last name"),
          type: z.number().min(1).max(99).optional().describe("User type"),
          pmi: z.string().optional().describe("Personal Meeting ID"),
          use_pmi: z.boolean().optional().describe("Use Personal Meeting ID for instant meetings"),
          timezone: z.string().optional().describe("User timezone"),
          dept: z.string().optional().describe("Department")
        },
        handler: async ({ user_id, ...userData }) => {
          try {
            const response = await zoomApi.patch(`/users/${user_id}`, userData);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "delete_user",
        description: "Delete a user",
        schema: {
          user_id: z.string().describe("The user ID or email address"),
          action: z.enum(["delete", "disassociate"]).describe("Delete action (delete: permanently delete, disassociate: disassociate from account)")
        },
        handler: async ({ user_id, action }) => {
          try {
            const response = await zoomApi.delete(`/users/${user_id}`, { 
              params: { action }
            });
            return {
              content: [{ 
                type: "text", 
                text: "User deleted successfully"
              }]
            };
          } catch (error) {
            return handleApiError(error);
          }
        }
      }
    ];

```

--------------------------------------------------------------------------------
/src/resources/api-docs.js:
--------------------------------------------------------------------------------

```javascript
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';

    // API documentation resources
    export const apiDocs = [
      {
        name: "api_docs",
        template: new ResourceTemplate("zoom-api://{category}", { list: undefined }),
        handler: async (uri, { category }) => {
          const docs = getApiDocs(category);
          
          return {
            contents: [{
              uri: uri.href,
              text: docs
            }]
          };
        }
      }
    ];

    // Helper function to get API documentation
    function getApiDocs(category) {
      const categories = {
        "overview": `# Zoom API Overview

The Zoom API is a REST API that allows developers to access information from Zoom and to update information in Zoom. The API is available to all Zoom accounts.

## Authentication

Zoom API uses OAuth 2.0 for authentication. You need to create a Server-to-Server OAuth app in the Zoom App Marketplace to get your credentials.

## Rate Limits

Zoom implements rate limits to protect against overuse and abuse of the API. The rate limits are based on the number of requests per second.

## Base URL

All API requests should be made to: \`https://api.zoom.us/v2/\``,

        "meetings": `# Zoom Meetings API

The Meetings API allows you to create, read, update, and delete Zoom meetings.

## Endpoints

- GET /users/{userId}/meetings - List a user's meetings
- POST /users/{userId}/meetings - Create a meeting for a user
- GET /meetings/{meetingId} - Get a meeting
- PATCH /meetings/{meetingId} - Update a meeting
- DELETE /meetings/{meetingId} - Delete a meeting
- GET /report/meetings/{meetingId}/participants - Get meeting participants report`,

        "users": `# Zoom Users API

The Users API allows you to manage users in your Zoom account.

## Endpoints

- GET /users - List users
- POST /users - Create a user
- GET /users/{userId} - Get a user
- PATCH /users/{userId} - Update a user
- DELETE /users/{userId} - Delete a user`,

        "webinars": `# Zoom Webinars API

The Webinars API allows you to create, read, update, and delete Zoom webinars.

## Endpoints

- GET /users/{userId}/webinars - List a user's webinars
- POST /users/{userId}/webinars - Create a webinar for a user
- GET /webinars/{webinarId} - Get a webinar
- PATCH /webinars/{webinarId} - Update a webinar
- DELETE /webinars/{webinarId} - Delete a webinar
- GET /report/webinars/{webinarId}/participants - Get webinar participants report`,

        "account": `# Zoom Account API

The Account API allows you to manage your Zoom account settings and profile.

## Endpoints

- GET /accounts/me/settings - Get account settings
- PATCH /accounts/me/settings - Update account settings
- GET /accounts/me - Get account profile
- GET /accounts - List sub accounts
- POST /accounts - Create a sub account`,

        "chat": `# Zoom Chat API

The Chat API allows you to manage Zoom Chat channels and messages.

## Endpoints

- GET /chat/users/me/channels - List channels
- POST /chat/users/me/channels - Create a channel
- GET /chat/channels/{channelId} - Get a channel
- PATCH /chat/channels/{channelId} - Update a channel
- DELETE /chat/channels/{channelId} - Delete a channel
- POST /chat/users/me/channels/{channelId}/messages - Send a message to a channel`,

        "phone": `# Zoom Phone API

The Phone API allows you to manage Zoom Phone users and numbers.

## Endpoints

- GET /phone/users - List phone users
- GET /phone/users/{userId} - Get a phone user
- PATCH /phone/users/{userId} - Update a phone user
- GET /phone/numbers - List phone numbers`,

        "recordings": `# Zoom Recordings API

The Recordings API allows you to manage cloud recordings.

## Endpoints

- GET /users/{userId}/recordings - List all recordings for a user
- GET /meetings/{meetingId}/recordings - Get recordings for a specific meeting
- DELETE /meetings/{meetingId}/recordings - Delete recordings for a specific meeting
- DELETE /meetings/{meetingId}/recordings/{recordingId} - Delete a specific recording file`,

        "webhooks": `# Zoom Webhooks API

The Webhooks API allows you to manage webhooks for event notifications.

## Endpoints

- GET /webhooks - List webhooks
- POST /webhooks - Create a webhook
- GET /webhooks/{webhookId} - Get a webhook
- PATCH /webhooks/{webhookId} - Update a webhook
- DELETE /webhooks/{webhookId} - Delete a webhook`
      };
      
      return categories[category.toLowerCase()] || 
        `# Zoom API Documentation

Available categories:
- overview
- meetings
- users
- webinars
- account
- chat
- phone
- recordings
- webhooks

Access documentation by using: zoom-api://{category}`;
    }

```

--------------------------------------------------------------------------------
/src/tools/webinars.js:
--------------------------------------------------------------------------------

```javascript
import { z } from 'zod';
    import { zoomApi, handleApiResponse, handleApiError } from '../utils/api.js';

    export const webinarsTools = [
      {
        name: "list_webinars",
        description: "List all webinars for a user",
        schema: {
          user_id: z.string().describe("The user ID or email address"),
          page_size: z.number().min(1).max(300).optional().describe("Number of records returned"),
          page_number: z.number().min(1).optional().describe("Page number")
        },
        handler: async ({ user_id, page_size, page_number }) => {
          try {
            const params = {};
            if (page_size) params.page_size = page_size;
            if (page_number) params.page_number = page_number;
            
            const response = await zoomApi.get(`/users/${user_id}/webinars`, { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "create_webinar",
        description: "Create a webinar for a user",
        schema: {
          user_id: z.string().describe("The user ID or email address"),
          topic: z.string().describe("Webinar topic"),
          type: z.number().min(5).max(9).describe("Webinar type (5: Webinar, 6: Recurring webinar with no fixed time, 9: Recurring webinar with fixed time)"),
          start_time: z.string().optional().describe("Webinar start time"),
          duration: z.number().optional().describe("Webinar duration in minutes"),
          timezone: z.string().optional().describe("Time zone"),
          password: z.string().optional().describe("Webinar password"),
          agenda: z.string().optional().describe("Webinar description"),
          settings: z.object({}).passthrough().optional().describe("Webinar settings")
        },
        handler: async ({ user_id, ...webinarData }) => {
          try {
            const response = await zoomApi.post(`/users/${user_id}/webinars`, webinarData);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "get_webinar",
        description: "Retrieve a webinar's details",
        schema: {
          webinar_id: z.string().describe("The webinar ID")
        },
        handler: async ({ webinar_id }) => {
          try {
            const response = await zoomApi.get(`/webinars/${webinar_id}`);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "update_webinar",
        description: "Update a webinar's details",
        schema: {
          webinar_id: z.string().describe("The webinar ID"),
          topic: z.string().optional().describe("Webinar topic"),
          type: z.number().min(5).max(9).optional().describe("Webinar type"),
          start_time: z.string().optional().describe("Webinar start time"),
          duration: z.number().optional().describe("Webinar duration in minutes"),
          timezone: z.string().optional().describe("Time zone"),
          password: z.string().optional().describe("Password"),
          agenda: z.string().optional().describe("Webinar description"),
          settings: z.object({}).passthrough().optional().describe("Webinar settings")
        },
        handler: async ({ webinar_id, ...webinarData }) => {
          try {
            const response = await zoomApi.patch(`/webinars/${webinar_id}`, webinarData);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "delete_webinar",
        description: "Delete a webinar",
        schema: {
          webinar_id: z.string().describe("The webinar ID"),
          occurrence_id: z.string().optional().describe("The occurrence ID for a recurring webinar"),
          cancel_webinar_reminder: z.boolean().optional().describe("Send cancellation email to registrants")
        },
        handler: async ({ webinar_id, occurrence_id, cancel_webinar_reminder }) => {
          try {
            const params = {};
            if (occurrence_id) params.occurrence_id = occurrence_id;
            if (cancel_webinar_reminder !== undefined) params.cancel_webinar_reminder = cancel_webinar_reminder;
            
            const response = await zoomApi.delete(`/webinars/${webinar_id}`, { params });
            return {
              content: [{ 
                type: "text", 
                text: "Webinar deleted successfully"
              }]
            };
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "list_webinar_participants",
        description: "List participants from a webinar",
        schema: {
          webinar_id: z.string().describe("The webinar ID"),
          page_size: z.number().min(1).max(300).optional().describe("Number of records returned"),
          next_page_token: z.string().optional().describe("Next page token")
        },
        handler: async ({ webinar_id, page_size, next_page_token }) => {
          try {
            const params = {};
            if (page_size) params.page_size = page_size;
            if (next_page_token) params.next_page_token = next_page_token;
            
            const response = await zoomApi.get(`/report/webinars/${webinar_id}/participants`, { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      }
    ];

```

--------------------------------------------------------------------------------
/src/tools/meetings.js:
--------------------------------------------------------------------------------

```javascript
import { z } from 'zod';
    import { zoomApi, handleApiResponse, handleApiError } from '../utils/api.js';

    export const meetingsTools = [
      {
        name: "list_meetings",
        description: "List all meetings for a user",
        schema: {
          user_id: z.string().describe("The user ID or email address"),
          type: z.enum(["scheduled", "live", "upcoming", "pending"]).optional().describe("Meeting type"),
          page_size: z.number().min(1).max(300).optional().describe("Number of records returned per page"),
          page_number: z.number().min(1).optional().describe("Page number")
        },
        handler: async ({ user_id, type, page_size, page_number }) => {
          try {
            const params = {};
            if (type) params.type = type;
            if (page_size) params.page_size = page_size;
            if (page_number) params.page_number = page_number;
            
            const response = await zoomApi.get(`/users/${user_id}/meetings`, { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "create_meeting",
        description: "Create a new meeting for a user",
        schema: {
          user_id: z.string().describe("The user ID or email address"),
          topic: z.string().describe("Meeting topic"),
          type: z.number().min(1).max(8).describe("Meeting type (1: instant, 2: scheduled, 3: recurring with no fixed time, 8: recurring with fixed time)"),
          start_time: z.string().optional().describe("Meeting start time in format YYYY-MM-DDThh:mm:ss"),
          duration: z.number().optional().describe("Meeting duration in minutes"),
          timezone: z.string().optional().describe("Time zone for start_time"),
          password: z.string().optional().describe("Password for the meeting"),
          agenda: z.string().optional().describe("Meeting description"),
          settings: z.object({}).passthrough().optional().describe("Meeting settings")
        },
        handler: async ({ user_id, ...meetingData }) => {
          try {
            const response = await zoomApi.post(`/users/${user_id}/meetings`, meetingData);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "get_meeting",
        description: "Retrieve a meeting's details",
        schema: {
          meeting_id: z.string().describe("The meeting ID")
        },
        handler: async ({ meeting_id }) => {
          try {
            const response = await zoomApi.get(`/meetings/${meeting_id}`);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "update_meeting",
        description: "Update a meeting's details",
        schema: {
          meeting_id: z.string().describe("The meeting ID"),
          topic: z.string().optional().describe("Meeting topic"),
          type: z.number().min(1).max(8).optional().describe("Meeting type"),
          start_time: z.string().optional().describe("Meeting start time"),
          duration: z.number().optional().describe("Meeting duration in minutes"),
          timezone: z.string().optional().describe("Time zone"),
          password: z.string().optional().describe("Password"),
          agenda: z.string().optional().describe("Meeting description"),
          settings: z.object({}).passthrough().optional().describe("Meeting settings")
        },
        handler: async ({ meeting_id, ...meetingData }) => {
          try {
            const response = await zoomApi.patch(`/meetings/${meeting_id}`, meetingData);
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "delete_meeting",
        description: "Delete a meeting",
        schema: {
          meeting_id: z.string().describe("The meeting ID"),
          occurrence_id: z.string().optional().describe("The occurrence ID for a recurring meeting"),
          schedule_for_reminder: z.boolean().optional().describe("Send cancellation email to registrants")
        },
        handler: async ({ meeting_id, occurrence_id, schedule_for_reminder }) => {
          try {
            const params = {};
            if (occurrence_id) params.occurrence_id = occurrence_id;
            if (schedule_for_reminder !== undefined) params.schedule_for_reminder = schedule_for_reminder;
            
            const response = await zoomApi.delete(`/meetings/${meeting_id}`, { params });
            return {
              content: [{ 
                type: "text", 
                text: "Meeting deleted successfully"
              }]
            };
          } catch (error) {
            return handleApiError(error);
          }
        }
      },
      {
        name: "list_meeting_participants",
        description: "List participants from a meeting",
        schema: {
          meeting_id: z.string().describe("The meeting ID"),
          page_size: z.number().min(1).max(300).optional().describe("Number of records returned"),
          next_page_token: z.string().optional().describe("Next page token")
        },
        handler: async ({ meeting_id, page_size, next_page_token }) => {
          try {
            const params = {};
            if (page_size) params.page_size = page_size;
            if (next_page_token) params.next_page_token = next_page_token;
            
            const response = await zoomApi.get(`/report/meetings/${meeting_id}/participants`, { params });
            return handleApiResponse(response);
          } catch (error) {
            return handleApiError(error);
          }
        }
      }
    ];

```