Skip to main content
Glama

Google Calendar MCP Server

by bezael
index.ts7.68 kB
#!/usr/bin/env node /** * MCP Server para Google Calendar * * Permite crear, obtener, listar, actualizar y eliminar eventos * de Google Calendar desde un modelo de lenguaje. */ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { z } from 'zod'; import 'dotenv/config'; import { createEvent } from './tools/createEvent.js'; import { getEvent } from './tools/getEvent.js'; import { listEvents } from './tools/listEvents.js'; import { updateEvent } from './tools/updateEvent.js'; import { deleteEvent } from './tools/deleteEvent.js'; import { MCPCalendarError } from './utils/errors.js'; import { logger } from './utils/logger.js'; /** * Formatea un resultado para la respuesta MCP */ function formatResult(result: unknown): { content: Array<{ type: 'text'; text: string }> } { return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } /** * Formatea un error para la respuesta MCP */ function formatError(error: unknown): { content: Array<{ type: 'text'; text: string }>; isError: true } { if (error instanceof MCPCalendarError) { return { content: [{ type: 'text', text: JSON.stringify(error.toMCPError(), null, 2) }], isError: true }; } return { content: [{ type: 'text', text: JSON.stringify({ type: 'unknown_error', message: error instanceof Error ? error.message : String(error) }, null, 2) }], isError: true }; } /** * Inicializa y ejecuta el servidor MCP */ async function main(): Promise<void> { logger.info('Iniciando servidor MCP para Google Calendar...'); const server = new McpServer({ name: 'mcp-gcal', version: '0.1.0' }); // === Tool: create_event === server.registerTool( 'create_event', { title: 'Create Event', description: 'Crea un nuevo evento en Google Calendar', inputSchema: { summary: z.string().describe('Título del evento (requerido)'), description: z.string().optional().describe('Descripción del evento'), location: z.string().optional().describe('Ubicación del evento'), start: z.string().describe('Fecha/hora de inicio en formato ISO 8601 (ej: 2025-11-30T10:00:00+01:00)'), end: z.string().describe('Fecha/hora de fin en formato ISO 8601'), calendarId: z.string().optional().describe('ID del calendario (por defecto: primary o GOOGLE_CALENDAR_ID)'), timeZone: z.string().optional().describe('Zona horaria (por defecto: Europe/Madrid)'), attendees: z.array(z.string()).optional().describe('Lista de emails de los asistentes') } }, async (params) => { try { const result = await createEvent({ summary: params.summary, description: params.description, location: params.location, start: params.start, end: params.end, calendarId: params.calendarId, timeZone: params.timeZone, attendees: params.attendees }); return formatResult(result); } catch (error) { return formatError(error); } } ); // === Tool: get_event === server.registerTool( 'get_event', { title: 'Get Event', description: 'Obtiene un evento por su ID', inputSchema: { eventId: z.string().describe('ID del evento a obtener (requerido)'), calendarId: z.string().optional().describe('ID del calendario (por defecto: primary)') } }, async (params) => { try { const result = await getEvent({ eventId: params.eventId, calendarId: params.calendarId }); return formatResult(result); } catch (error) { return formatError(error); } } ); // === Tool: list_events === server.registerTool( 'list_events', { title: 'List Events', description: 'Lista eventos en un rango de fechas', inputSchema: { timeMin: z.string().describe('Fecha/hora mínima en formato ISO 8601 (requerido)'), timeMax: z.string().describe('Fecha/hora máxima en formato ISO 8601 (requerido)'), maxResults: z.number().optional().describe('Número máximo de resultados (por defecto: 50)'), calendarId: z.string().optional().describe('ID del calendario (por defecto: primary)'), q: z.string().optional().describe('Texto de búsqueda para filtrar eventos') } }, async (params) => { try { const result = await listEvents({ timeMin: params.timeMin, timeMax: params.timeMax, maxResults: params.maxResults, calendarId: params.calendarId, q: params.q }); return formatResult(result); } catch (error) { return formatError(error); } } ); // === Tool: update_event === server.registerTool( 'update_event', { title: 'Update Event', description: 'Actualiza parcialmente un evento existente', inputSchema: { eventId: z.string().describe('ID del evento a actualizar (requerido)'), calendarId: z.string().optional().describe('ID del calendario (por defecto: primary)'), summary: z.string().optional().describe('Nuevo título del evento'), description: z.string().optional().describe('Nueva descripción'), location: z.string().optional().describe('Nueva ubicación'), start: z.string().optional().describe('Nueva fecha/hora de inicio en formato ISO 8601'), end: z.string().optional().describe('Nueva fecha/hora de fin en formato ISO 8601'), timeZone: z.string().optional().describe('Nueva zona horaria'), attendees: z.array(z.string()).optional().describe('Nueva lista de emails de asistentes') } }, async (params) => { try { const result = await updateEvent({ eventId: params.eventId, calendarId: params.calendarId, summary: params.summary, description: params.description, location: params.location, start: params.start, end: params.end, timeZone: params.timeZone, attendees: params.attendees }); return formatResult(result); } catch (error) { return formatError(error); } } ); // === Tool: delete_event === server.registerTool( 'delete_event', { title: 'Delete Event', description: 'Elimina un evento del calendario', inputSchema: { eventId: z.string().describe('ID del evento a eliminar (requerido)'), calendarId: z.string().optional().describe('ID del calendario (por defecto: primary)') } }, async (params) => { try { const result = await deleteEvent({ eventId: params.eventId, calendarId: params.calendarId }); return formatResult(result); } catch (error) { return formatError(error); } } ); // Conectar usando transporte stdio const transport = new StdioServerTransport(); await server.connect(transport); logger.info('Servidor MCP conectado y listo para recibir requests'); } // Ejecutar el servidor main().catch((error) => { logger.error('Error fatal al iniciar el servidor MCP', { error: String(error) }); 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/bezael/mcp-calendar'

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