diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 0a4275f..1bc1529 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -1,4 +1,4 @@ -name: Docker +name: Docker Publish # This workflow uses actions that are not certified by GitHub. # They are provided by a third-party and are governed by @@ -7,11 +7,8 @@ name: Docker on: push: - branches: ["master"] # Publish semver tags as releases. - tags: ["v*.*"] - pull_request: - branches: ["master"] + tags: ["v*.*.*"] env: # Use docker.io for Docker Hub if empty diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 4742978..8e9e929 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -1,4 +1,4 @@ -name: Update Version and Publish +name: Npm Publish on: release: @@ -15,7 +15,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v3 with: - node-version: "18" + node-version: "22" registry-url: "https://registry.npmjs.org" - name: Install dependencies @@ -23,7 +23,7 @@ jobs: - name: Extract version from release id: extract_version - run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV + run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV - name: Update version.ts run: | diff --git a/README.md b/README.md index 14d72ca..0cf5046 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,14 @@ Now you can date a Zoom meeting with AI's help - `delete the latest meeting` - `delete the 86226580854 meeting` +### 4. get a meeting detail + +- `Retrieve the latest meeting's details` +- `Retrieve 86226580854 meeting's details` + +## Usage with VS Code + [![Install with NPX in VS Code](https://img.shields.io/badge/VS_Code-NPM-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=zoom-mcp-server&inputs=%5B%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22ZOOM_ACCOUNT_ID%22%7D%2C%20%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22ZOOM_CLIENT_ID%22%7D%2C%20%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22ZOOM_CLIENT_SECRET%22%7D%5D&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40yitianyigexiangfa%2Fzoom-mcp-server%40latest%22%5D%2C%22env%22%3A%7B%22ZOOM_ACCOUNT_ID%22%3A%22%24%7Binput%3AZOOM_ACCOUNT_ID%7D%22%2C%20%22ZOOM_CLIENT_ID%22%3A%22%24%7Binput%3AZOOM_CLIENT_ID%7D%22%2C%20%22ZOOM_CLIENT_SECRET%22%3A%22%24%7Binput%3AZOOM_CLIENT_SECRET%7D%22%7D%7D) + ## 2 Steps to play with zoom-mcp-server - Get Zoom Client ID, Zoom Client Secret and Account ID @@ -41,7 +49,7 @@ Now you can date a Zoom meeting with AI's help "mcpServers": { "zoom-mcp-server": { "command": "npx", - "args": ["-y", "@yitianyigexiangfa/zoom-mcp-server"], + "args": ["-y", "@yitianyigexiangfa/zoom-mcp-server@latest"], "env": { "ZOOM_ACCOUNT_ID": "${ZOOM_ACCOUNT_ID}", "ZOOM_CLIENT_ID": "${ZOOM_CLIENT_ID}", diff --git a/common/types.ts b/common/types.ts index fa47fc8..93c26fa 100644 --- a/common/types.ts +++ b/common/types.ts @@ -109,3 +109,172 @@ export const ZoomListMeetingsSchema = z.object({ }), ), }); + +export const ZoomMeetingDetailSchema = z.object({ + assistant_id: z.string().optional(), + host_email: z.string().optional(), + host_id: z.string().optional(), + id: z.number(), + uuid: z.string(), + agenda: z.string().optional(), + created_at: z.string().optional(), + duration: z.number().optional(), + encrypted_password: z.string().optional(), + pstn_password: z.string().optional(), + h323_password: z.string().optional(), + join_url: z.string().optional(), + chat_join_url: z.string().optional(), + occurrences: z + .array( + z.object({ + duration: z.number().optional(), + occurrence_id: z.string().optional(), + start_time: z.string().optional(), + status: z.string().optional(), + }), + ) + .optional(), + password: z.string().optional(), + pmi: z.string().optional(), + pre_schedule: z.boolean().optional(), + recurrence: z + .object({ + end_date_time: z.string().optional(), + end_times: z.number().optional(), + monthly_day: z.number().optional(), + monthly_week: z.number().optional(), + monthly_week_day: z.number().optional(), + repeat_interval: z.number().optional(), + type: z.number().optional(), + weekly_days: z.string().optional(), + }) + .optional(), + settings: ZoomMeetingSettingsSchema.extend({ + approved_or_denied_countries_or_regions: z + .object({ + approved_list: z.array(z.string()).optional(), + denied_list: z.array(z.string()).optional(), + enable: z.boolean().optional(), + method: z.string().optional(), + }) + .optional(), + authentication_exception: z + .array( + z.object({ + email: z.string().optional(), + name: z.string().optional(), + join_url: z.string().optional(), + }), + ) + .optional(), + breakout_room: z + .object({ + enable: z.boolean().optional(), + rooms: z + .array( + z.object({ + name: z.string().optional(), + participants: z.array(z.string()).optional(), + }), + ) + .optional(), + }) + .optional(), + global_dial_in_numbers: z + .array( + z.object({ + city: z.string().optional(), + country: z.string().optional(), + country_name: z.string().optional(), + number: z.string().optional(), + type: z.string().optional(), + }), + ) + .optional(), + language_interpretation: z + .object({ + enable: z.boolean().optional(), + interpreters: z + .array( + z.object({ + email: z.string().optional(), + languages: z.string().optional(), + interpreter_languages: z.string().optional(), + }), + ) + .optional(), + }) + .optional(), + sign_language_interpretation: z + .object({ + enable: z.boolean().optional(), + interpreters: z + .array( + z.object({ + email: z.string().optional(), + sign_language: z.string().optional(), + }), + ) + .optional(), + }) + .optional(), + meeting_invitees: z + .array( + z.object({ + email: z.string().optional(), + internal_user: z.boolean().optional(), + }), + ) + .optional(), + continuous_meeting_chat: z + .object({ + enable: z.boolean().optional(), + auto_add_invited_external_users: z.boolean().optional(), + auto_add_meeting_participants: z.boolean().optional(), + who_is_added: z.string().optional(), + channel_id: z.string().optional(), + }) + .optional(), + resources: z + .array( + z.object({ + resource_type: z.string().optional(), + resource_id: z.string().optional(), + permission_level: z.string().optional(), + }), + ) + .optional(), + question_and_answer: z + .object({ + enable: z.boolean().optional(), + allow_submit_questions: z.boolean().optional(), + allow_anonymous_questions: z.boolean().optional(), + question_visibility: z.string().optional(), + attendees_can_comment: z.boolean().optional(), + attendees_can_upvote: z.boolean().optional(), + }) + .optional(), + auto_start_meeting_summary: z.boolean().optional(), + who_will_receive_summary: z.number().optional(), + auto_start_ai_companion_questions: z.boolean().optional(), + who_can_ask_questions: z.number().optional(), + summary_template_id: z.string().optional(), + }).optional(), + start_time: z.string().optional(), + start_url: z.string().optional(), + status: z.string().optional(), + timezone: z.string().optional(), + topic: z.string().optional(), + tracking_fields: z + .array( + z.object({ + field: z.string().optional(), + value: z.string().optional(), + visible: z.boolean().optional(), + }), + ) + .optional(), + type: z.number().optional(), + dynamic_host_key: z.string().optional(), + creation_source: z.string().optional(), +}); diff --git a/common/version.ts b/common/version.ts index d64046b..a972ad6 100644 --- a/common/version.ts +++ b/common/version.ts @@ -1 +1 @@ -export const VERSION = "0.6.5"; +export const VERSION = "0.7.2"; diff --git a/index.ts b/index.ts index a82b858..819cdcc 100644 --- a/index.ts +++ b/index.ts @@ -14,6 +14,8 @@ import { CreateMeetingOptionsSchema, deleteMeeting, DeleteMeetingOptionsSchema, + getAMeetingDetails, + GetMeetingOptionsSchema, ListMeetingOptionsSchema, listMeetings, } from "./operations/meeting.js"; @@ -37,6 +39,7 @@ enum PromptName { LIST_MEETINGS = "list_meetings", CREATE_A_MEETING = "create_meeting", DELETE_A_MEETING = "delete_a_meeting", + GET_A_MEETING_DETAILS = "get_a_meeting_details", } server.setRequestHandler(ListPromptsRequestSchema, async () => { @@ -54,6 +57,10 @@ server.setRequestHandler(ListPromptsRequestSchema, async () => { name: PromptName.DELETE_A_MEETING, description: "A prompt to delete a meeting", }, + { + name: PromptName.GET_A_MEETING_DETAILS, + description: "A prompt to get a meeting's details", + }, ], }; }); @@ -97,6 +104,18 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => { }, ], }; + } else if (name === PromptName.GET_A_MEETING_DETAILS) { + return { + messages: [ + { + role: "user", + content: { + type: "text", + text: "Get a zoom meeting's details", + }, + }, + ], + }; } throw new Error(`Unknown prompt: ${name}`); }); @@ -115,10 +134,15 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { inputSchema: zodToJsonSchema(ListMeetingOptionsSchema), }, { - name: "delete_meeting", + name: "delete_a_meeting", description: "Delete a meeting with a given ID", inputSchema: zodToJsonSchema(DeleteMeetingOptionsSchema), }, + { + name: "get_a_meeting_details", + description: "Retrieve the meeting's details with a given ID", + inputSchema: zodToJsonSchema(GetMeetingOptionsSchema), + }, ], }; }); @@ -145,13 +169,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { }; } - case "delete_meeting": { + case "delete_a_meeting": { const args = DeleteMeetingOptionsSchema.parse(request.params.arguments); const result = await deleteMeeting(args); return { content: [{ type: "text", text: result }], }; } + + case "get_a_meeting_details": { + const args = GetMeetingOptionsSchema.parse(request.params.arguments); + const result = await getAMeetingDetails(args); + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + }; + } } } catch (error) { if (error instanceof z.ZodError) { diff --git a/operations/meeting.ts b/operations/meeting.ts index 342f198..0ebda2c 100644 --- a/operations/meeting.ts +++ b/operations/meeting.ts @@ -1,6 +1,10 @@ import { z } from "zod"; import { zoomRequest } from "../common/util.js"; -import { ZoomListMeetingsSchema, ZoomMeetingSchema } from "../common/types.js"; +import { + ZoomListMeetingsSchema, + ZoomMeetingDetailSchema, + ZoomMeetingSchema, +} from "../common/types.js"; export const CreateMeetingOptionsSchema = z.object({ agenda: z @@ -37,9 +41,14 @@ export const DeleteMeetingOptionsSchema = z.object({ id: z.number().describe("The ID of the meeting to delete."), }); +export const GetMeetingOptionsSchema = z.object({ + id: z.number().describe("The ID of the meeting."), +}); + export type CreateMeetingOptions = z.infer; export type ListMeetingOptions = z.infer; export type DeleteMeetingOptions = z.infer; +export type GetMeetingOptions = z.infer; export async function createMeeting(options: CreateMeetingOptions) { const response = await zoomRequest( @@ -78,3 +87,13 @@ export async function deleteMeeting(options: DeleteMeetingOptions) { ); return response; } + +export async function getAMeetingDetails(options: GetMeetingOptions) { + const response = await zoomRequest( + `https://api.zoom.us/v2/meetings/${options.id}`, + { + method: "GET", + }, + ); + return ZoomMeetingDetailSchema.parse(response); +} diff --git a/package.json b/package.json index a9c319a..ade6c13 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "@yitianyigexiangfa/zoom-mcp-server", - "version": "0.6.5", + "version": "0.7.2", "description": "Now you can date a ZOOM meeting with AI's help.", "scripts": { "build": "tsc && shx chmod +x dist/*.js", "prepare": "husky", "watch": "tsc --watch", - "inspector": "npx @modelcontextprotocol/inspector dist/index.js" + "inspector": "npx @modelcontextprotocol/inspector -- node dist/index.js -e ZOOM_ACCOUNT_ID=$ZOOM_ACCOUNT_ID -e ZOOM_CLIENT_ID=$ZOOM_CLIENT_ID -e ZOOM_CLIENT_SECRET=$ZOOM_CLIENT_SECRET" }, "repository": { "type": "git",