Skip to main content
Glama

Google Calendar MCP Server

by peadams21
index.js28.5 kB
#!/usr/bin/env node "use strict"; /** * Google Calendar MCP Server * * This MCP server provides tools for integrating with Google Calendar API. * It handles authentication, event management, and calendar operations * through the Model Context Protocol. */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); var index_js_1 = require("@modelcontextprotocol/sdk/server/index.js"); var stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); var types_js_1 = require("@modelcontextprotocol/sdk/types.js"); var zod_1 = require("zod"); var google_auth_library_1 = require("google-auth-library"); var googleapis_1 = require("googleapis"); var dotenv = require("dotenv"); // Load environment variables dotenv.config(); // Google Calendar API setup var auth = new google_auth_library_1.GoogleAuth({ keyFile: process.env.GOOGLE_SERVICE_ACCOUNT_KEY_FILE, scopes: [ "https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/calendar.events", ], }); var calendar = googleapis_1.google.calendar({ version: "v3", auth: auth }); // Tool schemas var ListEventsArgsSchema = zod_1.z.object({ calendarId: zod_1.z.string().optional().default("primary"), timeMin: zod_1.z.string().optional(), timeMax: zod_1.z.string().optional(), maxResults: zod_1.z.number().optional().default(250), singleEvents: zod_1.z.boolean().optional().default(true), orderBy: zod_1.z.enum(["startTime", "updated"]).optional().default("startTime"), }); var CreateEventArgsSchema = zod_1.z.object({ calendarId: zod_1.z.string().optional().default("primary"), summary: zod_1.z.string(), description: zod_1.z.string().optional(), start: zod_1.z.object({ dateTime: zod_1.z.string(), timeZone: zod_1.z.string().optional(), }), end: zod_1.z.object({ dateTime: zod_1.z.string(), timeZone: zod_1.z.string().optional(), }), location: zod_1.z.string().optional(), attendees: zod_1.z.array(zod_1.z.object({ email: zod_1.z.string(), displayName: zod_1.z.string().optional(), })).optional(), recurrence: zod_1.z.array(zod_1.z.string()).optional(), }); var UpdateEventArgsSchema = zod_1.z.object({ calendarId: zod_1.z.string().optional().default("primary"), eventId: zod_1.z.string(), summary: zod_1.z.string().optional(), description: zod_1.z.string().optional(), start: zod_1.z.object({ dateTime: zod_1.z.string(), timeZone: zod_1.z.string().optional(), }).optional(), end: zod_1.z.object({ dateTime: zod_1.z.string(), timeZone: zod_1.z.string().optional(), }).optional(), location: zod_1.z.string().optional(), attendees: zod_1.z.array(zod_1.z.object({ email: zod_1.z.string(), displayName: zod_1.z.string().optional(), })).optional(), }); var DeleteEventArgsSchema = zod_1.z.object({ calendarId: zod_1.z.string().optional().default("primary"), eventId: zod_1.z.string(), }); var ListCalendarsArgsSchema = zod_1.z.object({ minAccessRole: zod_1.z.enum(["freeBusyReader", "owner", "reader", "writer"]).optional(), showDeleted: zod_1.z.boolean().optional().default(false), showHidden: zod_1.z.boolean().optional().default(false), }); // MCP Server setup var server = new index_js_1.Server({ name: "google-calendar-mcp-server", version: "1.0.0", }, { capabilities: { tools: {}, }, }); // Tool definitions var tools = [ { name: "list_events", description: "List events from a Google Calendar", inputSchema: { type: "object", properties: { calendarId: { type: "string", description: "Calendar ID (default: 'primary')", default: "primary", }, timeMin: { type: "string", description: "Lower bound for event start time (RFC3339 timestamp)", }, timeMax: { type: "string", description: "Upper bound for event start time (RFC3339 timestamp)", }, maxResults: { type: "number", description: "Maximum number of events to return (default: 250)", default: 250, }, singleEvents: { type: "boolean", description: "Whether to expand recurring events (default: true)", default: true, }, orderBy: { type: "string", enum: ["startTime", "updated"], description: "Order of events (default: 'startTime')", default: "startTime", }, }, }, }, { name: "create_event", description: "Create a new event in Google Calendar", inputSchema: { type: "object", properties: { calendarId: { type: "string", description: "Calendar ID (default: 'primary')", default: "primary", }, summary: { type: "string", description: "Event title/summary", }, description: { type: "string", description: "Event description", }, start: { type: "object", properties: { dateTime: { type: "string", description: "Start date and time (RFC3339 timestamp)", }, timeZone: { type: "string", description: "Time zone (e.g., 'America/New_York')", }, }, required: ["dateTime"], }, end: { type: "object", properties: { dateTime: { type: "string", description: "End date and time (RFC3339 timestamp)", }, timeZone: { type: "string", description: "Time zone (e.g., 'America/New_York')", }, }, required: ["dateTime"], }, location: { type: "string", description: "Event location", }, attendees: { type: "array", items: { type: "object", properties: { email: { type: "string" }, displayName: { type: "string" }, }, required: ["email"], }, description: "List of attendees", }, recurrence: { type: "array", items: { type: "string" }, description: "Recurrence rules (RRULE format)", }, }, required: ["summary", "start", "end"], }, }, { name: "update_event", description: "Update an existing event in Google Calendar", inputSchema: { type: "object", properties: { calendarId: { type: "string", description: "Calendar ID (default: 'primary')", default: "primary", }, eventId: { type: "string", description: "Event ID to update", }, summary: { type: "string", description: "Event title/summary", }, description: { type: "string", description: "Event description", }, start: { type: "object", properties: { dateTime: { type: "string", description: "Start date and time (RFC3339 timestamp)", }, timeZone: { type: "string", description: "Time zone (e.g., 'America/New_York')", }, }, }, end: { type: "object", properties: { dateTime: { type: "string", description: "End date and time (RFC3339 timestamp)", }, timeZone: { type: "string", description: "Time zone (e.g., 'America/New_York')", }, }, }, location: { type: "string", description: "Event location", }, attendees: { type: "array", items: { type: "object", properties: { email: { type: "string" }, displayName: { type: "string" }, }, required: ["email"], }, description: "List of attendees", }, }, required: ["eventId"], }, }, { name: "delete_event", description: "Delete an event from Google Calendar", inputSchema: { type: "object", properties: { calendarId: { type: "string", description: "Calendar ID (default: 'primary')", default: "primary", }, eventId: { type: "string", description: "Event ID to delete", }, }, required: ["eventId"], }, }, { name: "list_calendars", description: "List available Google Calendars", inputSchema: { type: "object", properties: { minAccessRole: { type: "string", enum: ["freeBusyReader", "owner", "reader", "writer"], description: "Minimum access role filter", }, showDeleted: { type: "boolean", description: "Include deleted calendars (default: false)", default: false, }, showHidden: { type: "boolean", description: "Include hidden calendars (default: false)", default: false, }, }, }, }, ]; // Tool handlers function handleListEvents(args) { return __awaiter(this, void 0, void 0, function () { var response, error_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, calendar.events.list({ calendarId: args.calendarId, timeMin: args.timeMin, timeMax: args.timeMax, maxResults: args.maxResults, singleEvents: args.singleEvents, orderBy: args.orderBy, })]; case 1: response = _a.sent(); return [2 /*return*/, { content: [ { type: "text", text: JSON.stringify({ success: true, events: response.data.items || [], nextPageToken: response.data.nextPageToken, summary: "Found ".concat((response.data.items || []).length, " events"), }, null, 2), }, ], }]; case 2: error_1 = _a.sent(); return [2 /*return*/, { content: [ { type: "text", text: JSON.stringify({ success: false, error: error_1 instanceof Error ? error_1.message : "Unknown error", }, null, 2), }, ], isError: true, }]; case 3: return [2 /*return*/]; } }); }); } function handleCreateEvent(args) { return __awaiter(this, void 0, void 0, function () { var event_1, response, error_2; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); event_1 = { summary: args.summary, description: args.description, start: args.start, end: args.end, location: args.location, attendees: args.attendees, recurrence: args.recurrence, }; return [4 /*yield*/, calendar.events.insert({ calendarId: args.calendarId, requestBody: event_1, })]; case 1: response = _a.sent(); return [2 /*return*/, { content: [ { type: "text", text: JSON.stringify({ success: true, event: response.data, message: "Event \"".concat(args.summary, "\" created successfully"), }, null, 2), }, ], }]; case 2: error_2 = _a.sent(); return [2 /*return*/, { content: [ { type: "text", text: JSON.stringify({ success: false, error: error_2 instanceof Error ? error_2.message : "Unknown error", }, null, 2), }, ], isError: true, }]; case 3: return [2 /*return*/]; } }); }); } function handleUpdateEvent(args) { return __awaiter(this, void 0, void 0, function () { var updates, response, error_3; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); updates = {}; if (args.summary !== undefined) updates.summary = args.summary; if (args.description !== undefined) updates.description = args.description; if (args.start !== undefined) updates.start = args.start; if (args.end !== undefined) updates.end = args.end; if (args.location !== undefined) updates.location = args.location; if (args.attendees !== undefined) updates.attendees = args.attendees; return [4 /*yield*/, calendar.events.patch({ calendarId: args.calendarId, eventId: args.eventId, requestBody: updates, })]; case 1: response = _a.sent(); return [2 /*return*/, { content: [ { type: "text", text: JSON.stringify({ success: true, event: response.data, message: "Event \"".concat(args.eventId, "\" updated successfully"), }, null, 2), }, ], }]; case 2: error_3 = _a.sent(); return [2 /*return*/, { content: [ { type: "text", text: JSON.stringify({ success: false, error: error_3 instanceof Error ? error_3.message : "Unknown error", }, null, 2), }, ], isError: true, }]; case 3: return [2 /*return*/]; } }); }); } function handleDeleteEvent(args) { return __awaiter(this, void 0, void 0, function () { var error_4; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, calendar.events.delete({ calendarId: args.calendarId, eventId: args.eventId, })]; case 1: _a.sent(); return [2 /*return*/, { content: [ { type: "text", text: JSON.stringify({ success: true, message: "Event \"".concat(args.eventId, "\" deleted successfully"), }, null, 2), }, ], }]; case 2: error_4 = _a.sent(); return [2 /*return*/, { content: [ { type: "text", text: JSON.stringify({ success: false, error: error_4 instanceof Error ? error_4.message : "Unknown error", }, null, 2), }, ], isError: true, }]; case 3: return [2 /*return*/]; } }); }); } function handleListCalendars(args) { return __awaiter(this, void 0, void 0, function () { var response, error_5; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, calendar.calendarList.list({ minAccessRole: args.minAccessRole, showDeleted: args.showDeleted, showHidden: args.showHidden, })]; case 1: response = _a.sent(); return [2 /*return*/, { content: [ { type: "text", text: JSON.stringify({ success: true, calendars: response.data.items || [], summary: "Found ".concat((response.data.items || []).length, " calendars"), }, null, 2), }, ], }]; case 2: error_5 = _a.sent(); return [2 /*return*/, { content: [ { type: "text", text: JSON.stringify({ success: false, error: error_5 instanceof Error ? error_5.message : "Unknown error", }, null, 2), }, ], isError: true, }]; case 3: return [2 /*return*/]; } }); }); } // Register handlers server.setRequestHandler(types_js_1.ListToolsRequestSchema, function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, { tools: tools }]; }); }); }); server.setRequestHandler(types_js_1.CallToolRequestSchema, function (request) { return __awaiter(void 0, void 0, void 0, function () { var _a, name, args, _b, validatedArgs, validatedArgs, validatedArgs, validatedArgs, validatedArgs, error_6; return __generator(this, function (_c) { switch (_c.label) { case 0: _a = request.params, name = _a.name, args = _a.arguments; _c.label = 1; case 1: _c.trys.push([1, 14, , 15]); _b = name; switch (_b) { case "list_events": return [3 /*break*/, 2]; case "create_event": return [3 /*break*/, 4]; case "update_event": return [3 /*break*/, 6]; case "delete_event": return [3 /*break*/, 8]; case "list_calendars": return [3 /*break*/, 10]; } return [3 /*break*/, 12]; case 2: validatedArgs = ListEventsArgsSchema.parse(args); return [4 /*yield*/, handleListEvents(validatedArgs)]; case 3: return [2 /*return*/, _c.sent()]; case 4: validatedArgs = CreateEventArgsSchema.parse(args); return [4 /*yield*/, handleCreateEvent(validatedArgs)]; case 5: return [2 /*return*/, _c.sent()]; case 6: validatedArgs = UpdateEventArgsSchema.parse(args); return [4 /*yield*/, handleUpdateEvent(validatedArgs)]; case 7: return [2 /*return*/, _c.sent()]; case 8: validatedArgs = DeleteEventArgsSchema.parse(args); return [4 /*yield*/, handleDeleteEvent(validatedArgs)]; case 9: return [2 /*return*/, _c.sent()]; case 10: validatedArgs = ListCalendarsArgsSchema.parse(args); return [4 /*yield*/, handleListCalendars(validatedArgs)]; case 11: return [2 /*return*/, _c.sent()]; case 12: throw new Error("Unknown tool: ".concat(name)); case 13: return [3 /*break*/, 15]; case 14: error_6 = _c.sent(); return [2 /*return*/, { content: [ { type: "text", text: "Error: ".concat(error_6 instanceof Error ? error_6.message : "Unknown error"), }, ], isError: true, }]; case 15: return [2 /*return*/]; } }); }); }); // Start server function main() { return __awaiter(this, void 0, void 0, function () { var transport; return __generator(this, function (_a) { switch (_a.label) { case 0: transport = new stdio_js_1.StdioServerTransport(); return [4 /*yield*/, server.connect(transport)]; case 1: _a.sent(); console.error("Google Calendar MCP Server running on stdio"); return [2 /*return*/]; } }); }); } if (require.main === module) { main().catch(function (error) { console.error("Server error:", 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/peadams21/Google-Calendar-MCP-Server'

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