# 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);
}
}
}
];
```