Skip to main content
Glama

Google Meet MCP Server

index.js10.9 kB
#!/usr/bin/env node /** * Google Meet MCP Server * This implements the Model Context Protocol server for Google Meet * functionality via the Google Calendar API. */ import path from 'path'; import { fileURLToPath } from 'url'; import process from 'process'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from '@modelcontextprotocol/sdk/types.js'; import GoogleMeetAPI from './GoogleMeetAPI.js'; // Get __dirname equivalent in ES modules const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); class GoogleMeetMcpServer { /** * Initialize the Google Meet MCP server */ constructor() { this.server = new Server( { name: 'google-meet-mcp', version: '1.0.0', }, { capabilities: { tools: {}, } } ); // Setup Google Meet API client const credentialsPath = process.env.GOOGLE_MEET_CREDENTIALS_PATH; const tokenPath = process.env.GOOGLE_MEET_TOKEN_PATH; if (!credentialsPath || !tokenPath) { console.error("Error: Missing required environment variables."); console.error("Please set GOOGLE_MEET_CREDENTIALS_PATH and GOOGLE_MEET_TOKEN_PATH"); process.exit(1); } this.googleMeet = new GoogleMeetAPI(credentialsPath, tokenPath); // Setup request handlers this.setupToolHandlers(); // Error handling this.server.onerror = error => console.error(`[MCP Error] ${error}`); process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } /** * Set up the tool request handlers */ setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, this.handleListTools.bind(this)); this.server.setRequestHandler(CallToolRequestSchema, this.handleCallTool.bind(this)); } /** * Handle requests to list available tools */ async handleListTools() { return { tools: [ { name: 'list_meetings', description: 'List upcoming Google Meet meetings', inputSchema: { type: 'object', properties: { max_results: { type: 'number', description: 'Maximum number of results to return (default: 10)' }, time_min: { type: 'string', description: 'Start time in ISO format (default: now)' }, time_max: { type: 'string', description: 'End time in ISO format (optional)' } }, required: [] } }, { name: 'get_meeting', description: 'Get details of a specific Google Meet meeting', inputSchema: { type: 'object', properties: { meeting_id: { type: 'string', description: 'ID of the meeting to retrieve' } }, required: ['meeting_id'] } }, { name: 'create_meeting', description: 'Create a new Google Meet meeting', inputSchema: { type: 'object', properties: { summary: { type: 'string', description: 'Title of the meeting' }, description: { type: 'string', description: 'Description for the meeting (optional)' }, start_time: { type: 'string', description: 'Start time in ISO format' }, end_time: { type: 'string', description: 'End time in ISO format' }, attendees: { type: 'array', description: 'List of email addresses for attendees (optional)', items: { type: 'string' } } }, required: ['summary', 'start_time', 'end_time'] } }, { name: 'update_meeting', description: 'Update an existing Google Meet meeting', inputSchema: { type: 'object', properties: { meeting_id: { type: 'string', description: 'ID of the meeting to update' }, summary: { type: 'string', description: 'Updated title of the meeting (optional)' }, description: { type: 'string', description: 'Updated description for the meeting (optional)' }, start_time: { type: 'string', description: 'Updated start time in ISO format (optional)' }, end_time: { type: 'string', description: 'Updated end time in ISO format (optional)' }, attendees: { type: 'array', description: 'Updated list of email addresses for attendees (optional)', items: { type: 'string' } } }, required: ['meeting_id'] } }, { name: 'delete_meeting', description: 'Delete a Google Meet meeting', inputSchema: { type: 'object', properties: { meeting_id: { type: 'string', description: 'ID of the meeting to delete' } }, required: ['meeting_id'] } } ] }; } /** * Handle tool calls */ async handleCallTool(request) { // Initialize the API if not already initialized if (!this.googleMeet.calendar) { try { await this.googleMeet.initialize(); } catch (error) { return { content: [ { type: 'text', text: `Error initializing Google Meet API: ${error.message}` } ], isError: true }; } } const toolName = request.params.name; const args = request.params.arguments || {}; try { if (toolName === 'list_meetings') { const maxResults = args.max_results || 10; const timeMin = args.time_min || null; const timeMax = args.time_max || null; const meetings = await this.googleMeet.listMeetings( maxResults, timeMin, timeMax ); return { content: [ { type: 'text', text: JSON.stringify(meetings, null, 2) } ] }; } else if (toolName === 'get_meeting') { const meetingId = args.meeting_id; if (!meetingId) { throw new McpError(ErrorCode.InvalidParams, 'meeting_id is required'); } const meeting = await this.googleMeet.getMeeting(meetingId); return { content: [ { type: 'text', text: JSON.stringify(meeting, null, 2) } ] }; } else if (toolName === 'create_meeting') { const { summary, description = '', start_time, end_time, attendees = [] } = args; // Validate required parameters if (!summary || !start_time || !end_time) { const missing = []; if (!summary) missing.push('summary'); if (!start_time) missing.push('start_time'); if (!end_time) missing.push('end_time'); throw new McpError( ErrorCode.InvalidParams, `Missing required parameters: ${missing.join(', ')}` ); } const meeting = await this.googleMeet.createMeeting( summary, start_time, end_time, description, attendees ); return { content: [ { type: 'text', text: JSON.stringify(meeting, null, 2) } ] }; } else if (toolName === 'update_meeting') { const { meeting_id, summary, description, start_time, end_time, attendees } = args; if (!meeting_id) { throw new McpError(ErrorCode.InvalidParams, 'meeting_id is required'); } // Extract optional parameters const updateData = {}; if (summary !== undefined) updateData.summary = summary; if (description !== undefined) updateData.description = description; if (start_time !== undefined) updateData.startTime = start_time; if (end_time !== undefined) updateData.endTime = end_time; if (attendees !== undefined) updateData.attendees = attendees; if (Object.keys(updateData).length === 0) { throw new McpError( ErrorCode.InvalidParams, 'At least one field to update must be provided' ); } const meeting = await this.googleMeet.updateMeeting(meeting_id, updateData); return { content: [ { type: 'text', text: JSON.stringify(meeting, null, 2) } ] }; } else if (toolName === 'delete_meeting') { const { meeting_id } = args; if (!meeting_id) { throw new McpError(ErrorCode.InvalidParams, 'meeting_id is required'); } await this.googleMeet.deleteMeeting(meeting_id); return { content: [ { type: 'text', text: 'Meeting successfully deleted' } ] }; } else { throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${toolName}` ); } } catch (error) { // Handle any errors from Google API or MCP errors if (error instanceof McpError) { throw error; } return { content: [ { type: 'text', text: `Error: ${error.message}` } ], isError: true }; } } /** * Run the server */ async run() { const transport = new StdioServerTransport(); console.error('Google Meet MCP server starting on stdio...'); await this.server.connect(transport); console.error('Google Meet MCP server connected'); } } // Create and run the server const server = new GoogleMeetMcpServer(); server.run().catch(err => { console.error('Fatal error:', err); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cool-man-vk/google-meet-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server