Skip to main content
Glama

Google Calendar and Meet MCP Server

by INSIDE-HAIR
smithery.ts45.5 kB
/** * Smithery Entry Point for Google Meet MCP Server v3.0 * Following the exact pattern from the working example */ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js"; import { z } from "zod"; import * as dotenv from "dotenv"; import { join } from "path"; dotenv.config({ path: join(process.cwd(), ".env.local") }); // Configuration schema for Smithery export const configSchema = z.object({ CLIENT_ID: z.string().optional().describe("Google OAuth2 Client ID for direct token authentication"), CLIENT_SECRET: z.string().optional().describe("Google OAuth2 Client Secret"), REFRESH_TOKEN: z.string().optional().describe("Google OAuth2 Refresh Token"), googleOAuthCredentials: z.string().optional().describe("Path to Google OAuth credentials JSON file (legacy)"), googleMeetCredentialsPath: z.string().optional().describe("Path to credentials file (legacy)"), googleMeetTokenPath: z.string().optional().describe("Path to token file (legacy)"), debug: z.boolean().default(false).describe("Enable debug logging"), }); function createMcpServer({ config }: { config: z.infer<typeof configSchema> }) { const CLIENT_ID = config?.CLIENT_ID || process.env.CLIENT_ID; const CLIENT_SECRET = config?.CLIENT_SECRET || process.env.CLIENT_SECRET; const REFRESH_TOKEN = config?.REFRESH_TOKEN || process.env.REFRESH_TOKEN; console.error("🚀 Creating MCP Server with config keys:", Object.keys(config || {})); console.error("🔑 Auth check - CLIENT_ID:", !!CLIENT_ID, "CLIENT_SECRET:", !!CLIENT_SECRET, "REFRESH_TOKEN:", !!REFRESH_TOKEN); const server = new McpServer( { name: "google-meet-server", version: "3.0.0", }, { capabilities: { tools: {}, }, } ); // Initialize Google Meet API let googleMeetAPI: any = null; const initializeAPI = async () => { if (!googleMeetAPI) { console.error("🔧 Initializing Google Meet API..."); // Set environment variables from config if (config.CLIENT_ID) process.env.CLIENT_ID = config.CLIENT_ID; if (config.CLIENT_SECRET) process.env.CLIENT_SECRET = config.CLIENT_SECRET; if (config.REFRESH_TOKEN) process.env.REFRESH_TOKEN = config.REFRESH_TOKEN; if (config.googleOAuthCredentials) process.env.G_OAUTH_CREDENTIALS = config.googleOAuthCredentials; if (config.googleMeetCredentialsPath) process.env.GOOGLE_MEET_CREDENTIALS_PATH = config.googleMeetCredentialsPath; if (config.googleMeetTokenPath) process.env.GOOGLE_MEET_TOKEN_PATH = config.googleMeetTokenPath; try { // Dynamic import to avoid static dependency issues const { default: GoogleMeetAPI } = await import("./GoogleMeetAPI.js"); googleMeetAPI = new GoogleMeetAPI("", ""); await googleMeetAPI.initialize(); console.error("✅ API initialized successfully"); } catch (error) { console.error("❌ API initialization failed:", error); throw error; } } return googleMeetAPI; }; // Error handling server.server.onerror = (error) => console.error("[MCP Error]", error); // Set up tool handlers server.server.setRequestHandler(ListToolsRequestSchema, async () => { console.error("🔧 ListToolsRequestSchema handler called - returning tools"); return { tools: [ { name: "health_check", description: "Check if the Google Meet MCP Server is healthy", inputSchema: { type: "object", properties: {}, }, }, // Google Calendar API v3 Tools { name: "calendar_v3_list_calendars", description: "[Calendar API v3] List all calendars available to the user", inputSchema: { type: "object", properties: {}, required: [], }, }, { name: "calendar_v3_list_events", description: "[Calendar API v3] List upcoming calendar events with Google Meet conferences", 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)", }, calendar_id: { type: "string", description: "Calendar ID to list events from (default: 'primary')", }, }, required: [], }, }, { name: "calendar_v3_get_event", description: "[Calendar API v3] Get details of a specific calendar event", inputSchema: { type: "object", properties: { event_id: { type: "string", description: "ID of the calendar event to retrieve", }, }, required: ["event_id"], }, }, { name: "calendar_v3_create_event", description: "[Calendar API v3] Create a new calendar event with Google Meet conference and guest permissions", inputSchema: { type: "object", properties: { summary: { type: "string", description: "Title of the event", }, description: { type: "string", description: "Description for the event (optional)", }, location: { type: "string", description: "Location for the event (optional)", }, start_time: { type: "string", description: "Start time in ISO format", }, end_time: { type: "string", description: "End time in ISO format", }, time_zone: { type: "string", description: "Time zone (default: UTC)", }, attendees: { type: "array", description: "List of email addresses for attendees (optional)", items: { type: "string", }, }, create_meet_conference: { type: "boolean", description: "Create Google Meet conference for this event (default: false)", }, guest_can_invite_others: { type: "boolean", description: "Allow guests to invite other people (default: true)", }, guest_can_modify: { type: "boolean", description: "Allow guests to modify the event (default: false)", }, guest_can_see_other_guests: { type: "boolean", description: "Allow guests to see other attendees (default: true)", }, calendar_id: { type: "string", description: "Calendar ID to create event in (default: 'primary')", }, }, required: ["summary", "start_time", "end_time"], }, }, { name: "calendar_v3_update_event", description: "[Calendar API v3] Update an existing calendar event", inputSchema: { type: "object", properties: { event_id: { type: "string", description: "ID of the event to update", }, summary: { type: "string", description: "Updated title of the event (optional)", }, description: { type: "string", description: "Updated description for the event (optional)", }, location: { type: "string", description: "Updated location for the event (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)", }, time_zone: { type: "string", description: "Updated time zone (optional)", }, attendees: { type: "array", description: "Updated list of email addresses for attendees (optional)", items: { type: "string", }, }, guest_can_invite_others: { type: "boolean", description: "Updated guest invite permission (optional)", }, guest_can_modify: { type: "boolean", description: "Updated guest modify permission (optional)", }, guest_can_see_other_guests: { type: "boolean", description: "Updated guest visibility permission (optional)", }, }, required: ["event_id"], }, }, { name: "calendar_v3_delete_event", description: "[Calendar API v3] Delete a calendar event", inputSchema: { type: "object", properties: { event_id: { type: "string", description: "ID of the event to delete", }, calendar_id: { type: "string", description: "ID of the calendar containing the event (defaults to 'primary')", default: "primary", }, }, required: ["event_id"], }, }, { name: "calendar_v3_move_event", description: "[Calendar API v3] Move a calendar event from one calendar to another", inputSchema: { type: "object", properties: { event_id: { type: "string", description: "ID of the event to move", }, source_calendar_id: { type: "string", description: "ID of the calendar where the event currently exists", }, destination_calendar_id: { type: "string", description: "ID of the calendar to move the event to", }, }, required: ["event_id", "source_calendar_id", "destination_calendar_id"], }, }, { name: "calendar_v3_freebusy_query", description: "[Calendar API v3] Query free/busy information for calendars", inputSchema: { type: "object", properties: { calendar_ids: { type: "array", items: { type: "string" }, description: "Array of calendar IDs to query", }, time_min: { type: "string", description: "Start time in ISO 8601 format (e.g., '2024-01-01T00:00:00Z')", }, time_max: { type: "string", description: "End time in ISO 8601 format (e.g., '2024-01-01T23:59:59Z')", }, }, required: ["calendar_ids", "time_min", "time_max"], }, }, { name: "calendar_v3_quick_add", description: "[Calendar API v3] Create event using natural language", inputSchema: { type: "object", properties: { calendar_id: { type: "string", description: "Calendar ID to add event to (default: 'primary')", }, text: { type: "string", description: "Natural language description (e.g., 'Lunch with John tomorrow at 2pm')", }, }, required: ["text"], }, }, // Google Meet API v2 Tools (GA - Generally Available) { name: "meet_v2_create_space", description: "[Meet API v2 GA] Create a Google Meet space with advanced configuration", inputSchema: { type: "object", properties: { access_type: { type: "string", enum: ["OPEN", "TRUSTED", "RESTRICTED"], description: "Access type for the space (default: TRUSTED)", }, enable_recording: { type: "boolean", description: "Enable automatic recording (requires Google Workspace Business Standard+)", }, enable_transcription: { type: "boolean", description: "Enable automatic transcription (requires Google Workspace)", }, enable_smart_notes: { type: "boolean", description: "Enable automatic smart notes with Gemini (requires Gemini license)", }, attendance_report: { type: "boolean", description: "Enable attendance report generation", }, moderation_mode: { type: "string", enum: ["ON", "OFF"], description: "Enable moderation for the space (default: OFF)", }, chat_restriction: { type: "string", enum: ["HOSTS_ONLY", "NO_RESTRICTION"], description: "Chat restriction level (default: NO_RESTRICTION)", }, present_restriction: { type: "string", enum: ["HOSTS_ONLY", "NO_RESTRICTION"], description: "Presentation restriction level (default: NO_RESTRICTION)", }, default_join_as_viewer: { type: "boolean", description: "Join participants as viewers by default (default: false)", }, }, required: [], }, }, { name: "meet_v2_get_space", description: "[Meet API v2 GA] Get details of a Google Meet space", inputSchema: { type: "object", properties: { space_name: { type: "string", description: "Name of the space (spaces/{space_id})", }, }, required: ["space_name"], }, }, { name: "meet_v2_update_space", description: "[Meet API v2 GA] Update configuration of a Google Meet space", inputSchema: { type: "object", properties: { space_name: { type: "string", description: "Name of the space (spaces/{space_id})", }, access_type: { type: "string", enum: ["OPEN", "TRUSTED", "RESTRICTED"], description: "Updated access type for the space (optional)", }, moderation_mode: { type: "string", enum: ["ON", "OFF"], description: "Updated moderation mode (optional)", }, chat_restriction: { type: "string", enum: ["HOSTS_ONLY", "NO_RESTRICTION"], description: "Updated chat restriction level (optional)", }, present_restriction: { type: "string", enum: ["HOSTS_ONLY", "NO_RESTRICTION"], description: "Updated presentation restriction level (optional)", }, }, required: ["space_name"], }, }, { name: "meet_v2_end_active_conference", description: "[Meet API v2 GA] End the active conference in a Google Meet space", inputSchema: { type: "object", properties: { space_name: { type: "string", description: "Name of the space (spaces/{space_id})", }, }, required: ["space_name"], }, }, { name: "meet_v2_list_conference_records", description: "[Meet API v2 GA] List conference records for historical meetings", inputSchema: { type: "object", properties: { filter: { type: "string", description: 'Filter for conference records (e.g., space.name="spaces/{space_id}")', }, page_size: { type: "number", description: "Maximum number of results to return (default: 10, max: 50)", }, }, required: [], }, }, { name: "meet_v2_get_conference_record", description: "[Meet API v2 GA] Get details of a specific conference record", inputSchema: { type: "object", properties: { conference_record_name: { type: "string", description: "Name of the conference record (conferenceRecords/{record_id})", }, }, required: ["conference_record_name"], }, }, { name: "meet_v2_list_recordings", description: "[Meet API v2 GA] List recordings for a conference record", inputSchema: { type: "object", properties: { conference_record_name: { type: "string", description: "Name of the conference record (conferenceRecords/{record_id})", }, }, required: ["conference_record_name"], }, }, { name: "meet_v2_get_recording", description: "[Meet API v2 GA] Get details of a specific recording", inputSchema: { type: "object", properties: { recording_name: { type: "string", description: "Name of the recording (conferenceRecords/{record_id}/recordings/{recording_id})", }, }, required: ["recording_name"], }, }, { name: "meet_v2_list_transcripts", description: "[Meet API v2 GA] List transcripts for a conference record", inputSchema: { type: "object", properties: { conference_record_name: { type: "string", description: "Name of the conference record (conferenceRecords/{record_id})", }, }, required: ["conference_record_name"], }, }, { name: "meet_v2_get_transcript", description: "[Meet API v2 GA] Get details of a specific transcript", inputSchema: { type: "object", properties: { transcript_name: { type: "string", description: "Name of the transcript (conferenceRecords/{record_id}/transcripts/{transcript_id})", }, }, required: ["transcript_name"], }, }, { name: "meet_v2_list_transcript_entries", description: "[Meet API v2 GA] List transcript entries (individual speech segments)", inputSchema: { type: "object", properties: { transcript_name: { type: "string", description: "Name of the transcript (conferenceRecords/{record_id}/transcripts/{transcript_id})", }, page_size: { type: "number", description: "Maximum number of entries to return (default: 100, max: 1000)", }, }, required: ["transcript_name"], }, }, { name: "meet_v2_get_participant", description: "[Meet API v2 GA] Get details of a specific participant", inputSchema: { type: "object", properties: { participant_name: { type: "string", description: "Name of the participant (conferenceRecords/{record_id}/participants/{participant_id})", }, }, required: ["participant_name"], }, }, { name: "meet_v2_list_participants", description: "[Meet API v2 GA] List participants for a conference record", inputSchema: { type: "object", properties: { conference_record_name: { type: "string", description: "Name of the conference record (conferenceRecords/{record_id})", }, page_size: { type: "number", description: "Maximum number of participants to return (default: 10, max: 100)", }, }, required: ["conference_record_name"], }, }, { name: "meet_v2_get_participant_session", description: "[Meet API v2 GA] Get details of a specific participant session", inputSchema: { type: "object", properties: { participant_session_name: { type: "string", description: "Name of the participant session (conferenceRecords/{record_id}/participants/{participant_id}/sessions/{session_id})", }, }, required: ["participant_session_name"], }, }, { name: "meet_v2_list_participant_sessions", description: "[Meet API v2 GA] List sessions for a specific participant", inputSchema: { type: "object", properties: { participant_name: { type: "string", description: "Name of the participant (conferenceRecords/{record_id}/participants/{participant_id})", }, page_size: { type: "number", description: "Maximum number of sessions to return (default: 10, max: 100)", }, }, required: ["participant_name"], }, }, { name: "get_current_time", description: "Get the current time in UTC or specific timezone", inputSchema: { type: "object", properties: { timeZone: { type: "string", description: "IANA timezone identifier" }, }, }, }, ], }; }); server.server.setRequestHandler(CallToolRequestSchema, async (request) => { console.error(`🎯 CallToolRequestSchema handler called for: ${request.params.name}`); switch (request.params.name) { case "health_check": return await handleHealthCheck(request.params.arguments); // Calendar API v3 Tools case "calendar_v3_list_calendars": return await handleListCalendars(request.params.arguments); case "calendar_v3_list_events": return await handleListEvents(request.params.arguments); case "calendar_v3_get_event": return await handleGetEvent(request.params.arguments); case "calendar_v3_create_event": return await handleCreateEvent(request.params.arguments); case "calendar_v3_update_event": return await handleUpdateEvent(request.params.arguments); case "calendar_v3_delete_event": return await handleDeleteEvent(request.params.arguments); case "calendar_v3_move_event": return await handleMoveEvent(request.params.arguments); case "calendar_v3_freebusy_query": return await handleFreeBusyQuery(request.params.arguments); case "calendar_v3_quick_add": return await handleQuickAdd(request.params.arguments); // Meet API v2 Tools case "meet_v2_create_space": return await handleCreateSpace(request.params.arguments); case "meet_v2_get_space": return await handleGetSpace(request.params.arguments); case "meet_v2_update_space": return await handleUpdateSpace(request.params.arguments); case "meet_v2_end_active_conference": return await handleEndActiveConference(request.params.arguments); case "meet_v2_list_conference_records": return await handleListConferenceRecords(request.params.arguments); case "meet_v2_get_conference_record": return await handleGetConferenceRecord(request.params.arguments); case "meet_v2_list_recordings": return await handleListRecordings(request.params.arguments); case "meet_v2_get_recording": return await handleGetRecording(request.params.arguments); case "meet_v2_list_transcripts": return await handleListTranscripts(request.params.arguments); case "meet_v2_get_transcript": return await handleGetTranscript(request.params.arguments); case "meet_v2_list_transcript_entries": return await handleListTranscriptEntries(request.params.arguments); case "meet_v2_get_participant": return await handleGetParticipant(request.params.arguments); case "meet_v2_list_participants": return await handleListParticipants(request.params.arguments); case "meet_v2_get_participant_session": return await handleGetParticipantSession(request.params.arguments); case "meet_v2_list_participant_sessions": return await handleListParticipantSessions(request.params.arguments); // Utility Tools case "get_current_time": return await handleGetCurrentTime(request.params.arguments); default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}` ); } }); // Tool handler functions async function handleHealthCheck(args: any) { return { content: [ { type: "text", text: "Google Meet MCP Server v3.0 is healthy and ready to use!", }, ], }; } async function handleListCalendars(args: any) { try { const api = await initializeAPI(); const result = await api.listCalendars(); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}\n\nTroubleshooting:\n- Check that CLIENT_ID, CLIENT_SECRET, and REFRESH_TOKEN are set\n- Verify that the refresh token is still valid` }], isError: true, }; } } async function handleListEvents(args: any) { try { console.error("🎯 handleListEvents args:", JSON.stringify(args, null, 2)); console.error("🔍 Environment check:", { CLIENT_ID: process.env.CLIENT_ID ? "SET" : "NOT SET", CLIENT_SECRET: process.env.CLIENT_SECRET ? "SET" : "NOT SET", REFRESH_TOKEN: process.env.REFRESH_TOKEN ? "SET" : "NOT SET", G_OAUTH_CREDENTIALS: process.env.G_OAUTH_CREDENTIALS ? "SET" : "NOT SET" }); const api = await initializeAPI(); console.error("🔑 API initialized, calling listCalendarEvents with:", { max_results: args.max_results, time_min: args.time_min, time_max: args.time_max, calendar_id: args.calendar_id }); const result = await api.listCalendarEvents(args.max_results, args.time_min, args.time_max, args.calendar_id); console.error("✅ listCalendarEvents completed successfully, events found:", result.length); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { console.error("❌ handleListEvents error:", error); return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleGetEvent(args: any) { try { console.error("🎯 handleGetEvent args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.getCalendarEvent(args.event_id, args.calendar_id); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}\n\nTroubleshooting:\n- Check that the event ID is correct\n- Verify that you have access to this calendar event` }], isError: true, }; } } async function handleCreateEvent(args: any) { try { console.error("🎯 handleCreateEvent args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.createCalendarEvent({ summary: args.summary, description: args.description, location: args.location, startTime: args.start_time, endTime: args.end_time, attendees: args.attendees, createMeetConference: args.create_meet_conference, guestPermissions: { canInviteOthers: args.guest_can_invite_others, canModify: args.guest_can_modify, canSeeOtherGuests: args.guest_can_see_other_guests, }, calendarId: args.calendar_id, }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleUpdateEvent(args: any) { try { console.error("🎯 handleUpdateEvent args:", JSON.stringify(args, null, 2)); console.error("🔍 Environment check:", { CLIENT_ID: process.env.CLIENT_ID ? "SET" : "NOT SET", CLIENT_SECRET: process.env.CLIENT_SECRET ? "SET" : "NOT SET", REFRESH_TOKEN: process.env.REFRESH_TOKEN ? "SET" : "NOT SET", G_OAUTH_CREDENTIALS: process.env.G_OAUTH_CREDENTIALS ? "SET" : "NOT SET" }); const api = await initializeAPI(); const updateData: any = {}; if (args.summary !== undefined) updateData.summary = args.summary; if (args.description !== undefined) updateData.description = args.description; if (args.location !== undefined) updateData.location = args.location; if (args.start_time !== undefined) updateData.startTime = args.start_time; if (args.end_time !== undefined) updateData.endTime = args.end_time; if (args.attendees !== undefined) updateData.attendees = args.attendees; console.error("🔍 Calling updateCalendarEvent with:", { event_id: args.event_id, calendar_id: args.calendar_id || "primary", updateData: updateData }); const result = await api.updateCalendarEvent(args.event_id, updateData, args.calendar_id); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleDeleteEvent(args: any) { try { console.error("🎯 handleDeleteEvent args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.deleteCalendarEvent(args.event_id, args.calendar_id); return { content: [{ type: "text", text: JSON.stringify({ success: result, message: "Event deleted successfully" }, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleMoveEvent(args: any) { try { console.error("🎯 handleMoveEvent args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.moveCalendarEvent( args.event_id, args.source_calendar_id, args.destination_calendar_id ); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleCreateSpace(args: any) { try { console.error("🎯 handleCreateSpace args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const config = { accessType: args.access_type, enableRecording: args.enable_recording, enableTranscription: args.enable_transcription, enableSmartNotes: args.enable_smart_notes, }; const result = await api.createMeetSpace(config); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } // Additional Calendar API v3 handlers async function handleFreeBusyQuery(args: any) { try { console.error("🎯 handleFreeBusyQuery args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.queryFreeBusy(args.calendar_ids, args.time_min, args.time_max); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleQuickAdd(args: any) { try { console.error("🎯 handleQuickAdd args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.quickAddEvent(args.calendar_id || "primary", args.text); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } // Additional Meet API v2 handlers async function handleGetSpace(args: any) { try { console.error("🎯 handleGetSpace args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.getMeetSpace(args.space_name); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleUpdateSpace(args: any) { try { console.error("🎯 handleUpdateSpace args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const updateData: any = {}; if (args.access_type !== undefined) updateData.accessType = args.access_type; if (args.moderation_mode !== undefined) updateData.moderationMode = args.moderation_mode; if (args.chat_restriction !== undefined) updateData.chatRestriction = args.chat_restriction; if (args.present_restriction !== undefined) updateData.presentRestriction = args.present_restriction; const result = await api.updateMeetSpace(args.space_name, updateData); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleEndActiveConference(args: any) { try { console.error("🎯 handleEndActiveConference args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.endActiveConference(args.space_name); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleListConferenceRecords(args: any) { try { console.error("🎯 handleListConferenceRecords args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.listConferenceRecords(args.filter, args.page_size); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleGetConferenceRecord(args: any) { try { console.error("🎯 handleGetConferenceRecord args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.getConferenceRecord(args.conference_record_name); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleListRecordings(args: any) { try { console.error("🎯 handleListRecordings args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.listRecordings(args.conference_record_name); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleGetRecording(args: any) { try { console.error("🎯 handleGetRecording args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.getRecording(args.recording_name); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleListTranscripts(args: any) { try { console.error("🎯 handleListTranscripts args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.listTranscripts(args.conference_record_name); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleGetTranscript(args: any) { try { console.error("🎯 handleGetTranscript args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.getTranscript(args.transcript_name); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleListTranscriptEntries(args: any) { try { console.error("🎯 handleListTranscriptEntries args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.listTranscriptEntries(args.transcript_name, args.page_size); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleGetParticipant(args: any) { try { console.error("🎯 handleGetParticipant args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.getParticipant(args.participant_name); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleListParticipants(args: any) { try { console.error("🎯 handleListParticipants args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.listParticipants(args.conference_record_name, args.page_size); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleGetParticipantSession(args: any) { try { console.error("🎯 handleGetParticipantSession args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.getParticipantSession(args.participant_session_name); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleListParticipantSessions(args: any) { try { console.error("🎯 handleListParticipantSessions args:", JSON.stringify(args, null, 2)); const api = await initializeAPI(); const result = await api.listParticipantSessions(args.participant_name, args.page_size); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } async function handleGetCurrentTime(args: any) { try { const now = new Date(); const result = { utc: now.toISOString(), timestamp: now.getTime(), timezone: args?.timeZone ? { requested: args.timeZone, formatted: new Intl.DateTimeFormat('en-US', { timeZone: args.timeZone, dateStyle: 'full', timeStyle: 'full' }).format(now) } : null }; return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } catch (error) { return { content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } } console.error("✅ MCP Server created with tools and handlers"); return server; } // Export using the exact pattern from the working example export default function createStatelessServer({ config }: { config: z.infer<typeof configSchema> }) { console.error("🚀 createStatelessServer called"); return createMcpServer({ config }); }

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/INSIDE-HAIR/mcp-google-calendar-and-meet'

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