calendar
Search, create, open, or list calendar events in the Apple Calendar app using specific criteria such as dates, titles, or event IDs. Simplify event management directly through the MCP server.
Instructions
Search, create, and open calendar events in Apple Calendar app
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| calendarName | No | Name of the calendar to create the event in (optional for create operation, uses default calendar if not specified) | |
| endDate | No | End date/time of the event in ISO format (required for create operation) | |
| eventId | No | ID of the event to open (required for open operation) | |
| fromDate | No | Start date for search range in ISO format (optional, default is today) | |
| isAllDay | No | Whether the event is an all-day event (optional for create operation, default is false) | |
| limit | No | Number of events to retrieve (optional, default 10) | |
| location | No | Location of the event (optional for create operation) | |
| notes | No | Additional notes for the event (optional for create operation) | |
| operation | Yes | Operation to perform: 'search', 'open', 'list', or 'create' | |
| searchText | No | Text to search for in event titles, locations, and notes (required for search operation) | |
| startDate | No | Start date/time of the event in ISO format (required for create operation) | |
| title | No | Title of the event to create (required for create operation) | |
| toDate | No | End date for search range in ISO format (optional, default is 30 days from now for search, 7 days for list) |
Input Schema (JSON Schema)
{
"properties": {
"calendarName": {
"description": "Name of the calendar to create the event in (optional for create operation, uses default calendar if not specified)",
"type": "string"
},
"endDate": {
"description": "End date/time of the event in ISO format (required for create operation)",
"type": "string"
},
"eventId": {
"description": "ID of the event to open (required for open operation)",
"type": "string"
},
"fromDate": {
"description": "Start date for search range in ISO format (optional, default is today)",
"type": "string"
},
"isAllDay": {
"description": "Whether the event is an all-day event (optional for create operation, default is false)",
"type": "boolean"
},
"limit": {
"description": "Number of events to retrieve (optional, default 10)",
"type": "number"
},
"location": {
"description": "Location of the event (optional for create operation)",
"type": "string"
},
"notes": {
"description": "Additional notes for the event (optional for create operation)",
"type": "string"
},
"operation": {
"description": "Operation to perform: 'search', 'open', 'list', or 'create'",
"enum": [
"search",
"open",
"list",
"create"
],
"type": "string"
},
"searchText": {
"description": "Text to search for in event titles, locations, and notes (required for search operation)",
"type": "string"
},
"startDate": {
"description": "Start date/time of the event in ISO format (required for create operation)",
"type": "string"
},
"title": {
"description": "Title of the event to create (required for create operation)",
"type": "string"
},
"toDate": {
"description": "End date for search range in ISO format (optional, default is 30 days from now for search, 7 days for list)",
"type": "string"
}
},
"required": [
"operation"
],
"type": "object"
}
Implementation Reference
- src/handlers/calendarHandler.ts:35-122 (handler)The main handler function for the 'calendar' tool. It parses arguments, loads the calendar module lazily, and dispatches to specific operations (search, open, list, create) by calling methods on the module.export async function handleCalendar( args: CalendarArgs, loadModule: LoadModuleFunction ): Promise<ToolResult> { try { const calendarModule = await loadModule("calendar"); switch (args.operation) { case "search": { const events = await calendarModule.searchEvents(args.searchText, args.limit, args.fromDate, args.toDate); return { content: [{ type: "text", text: events.length > 0 ? `Found ${events.length} events matching "${args.searchText}":\n\n${events.map(event => `${event.title} (${new Date(event.startDate!).toLocaleString()} - ${new Date(event.endDate!).toLocaleString()})\n` + `Location: ${event.location || 'Not specified'}\n` + `Calendar: ${event.calendarName}\n` + `ID: ${event.id}\n` + `${event.notes ? `Notes: ${event.notes}\n` : ''}` ).join("\n\n")}` : `No events found matching "${args.searchText}".` }], isError: false }; } case "open": { const result = await calendarModule.openEvent(args.eventId); return { content: [{ type: "text", text: result.success ? result.message : `Error opening event: ${result.message}` }], isError: !result.success }; } case "list": { const events = await calendarModule.getEvents(args.limit, args.fromDate, args.toDate); const startDateText = args.fromDate ? new Date(args.fromDate).toLocaleDateString() : 'today'; const endDateText = args.toDate ? new Date(args.toDate).toLocaleDateString() : 'next 7 days'; return { content: [{ type: "text", text: events.length > 0 ? `Found ${events.length} events from ${startDateText} to ${endDateText}:\n\n${events.map(event => `${event.title} (${new Date(event.startDate!).toLocaleString()} - ${new Date(event.endDate!).toLocaleString()})\n` + `Location: ${event.location || 'Not specified'}\n` + `Calendar: ${event.calendarName}\n` + `ID: ${event.id}` ).join("\n\n")}` : `No events found from ${startDateText} to ${endDateText}.` }], isError: false }; } case "create": { const result = await calendarModule.createEvent(args.title, args.startDate, args.endDate, args.location, args.notes, args.isAllDay, args.calendarName); return { content: [{ type: "text", text: result.success ? `${result.message} Event scheduled from ${new Date(args.startDate).toLocaleString()} to ${new Date(args.endDate).toLocaleString()}${result.eventId ? `\nEvent ID: ${result.eventId}` : ''}` : `Error creating event: ${result.message}` }], isError: !result.success }; } default: // This should be unreachable due to Zod validation throw new Error(`Unknown calendar operation: ${(args as any).operation}`); } } catch (error) { return { content: [{ type: "text", text: `Error in calendar tool: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } }
- src/handlers/calendarHandler.ts:4-30 (schema)Zod schema defining the discriminated union of input arguments for different calendar operations: search, open, list, create.// Define the Zod schema for calendar arguments export const CalendarArgsSchema = z.discriminatedUnion("operation", [ z.object({ operation: z.literal("search"), searchText: z.string().min(1), limit: z.number().optional(), fromDate: z.string().datetime().optional(), toDate: z.string().datetime().optional() }), z.object({ operation: z.literal("open"), eventId: z.string().min(1) }), z.object({ operation: z.literal("list"), limit: z.number().optional(), fromDate: z.string().datetime().optional(), toDate: z.string().datetime().optional() }), z.object({ operation: z.literal("create"), title: z.string().min(1), startDate: z.string().datetime(), endDate: z.string().datetime(), location: z.string().optional(), notes: z.string().optional(), isAllDay: z.boolean().optional(), calendarName: z.string().optional() }), ]);
- tools.ts:195-257 (registration)MCP Tool definition for 'calendar', including name, description, and inputSchema that matches the Zod schema used for validation.const CALENDAR_TOOL: Tool = { name: "calendar", description: "Search, create, and open calendar events in Apple Calendar app", inputSchema: { type: "object", properties: { operation: { type: "string", description: "Operation to perform: 'search', 'open', 'list', or 'create'", enum: ["search", "open", "list", "create"] }, searchText: { type: "string", description: "Text to search for in event titles, locations, and notes (required for search operation)" }, eventId: { type: "string", description: "ID of the event to open (required for open operation)" }, limit: { type: "number", description: "Number of events to retrieve (optional, default 10)" }, fromDate: { type: "string", description: "Start date for search range in ISO format (optional, default is today)" }, toDate: { type: "string", description: "End date for search range in ISO format (optional, default is 30 days from now for search, 7 days for list)" }, title: { type: "string", description: "Title of the event to create (required for create operation)" }, startDate: { type: "string", description: "Start date/time of the event in ISO format (required for create operation)" }, endDate: { type: "string", description: "End date/time of the event in ISO format (required for create operation)" }, location: { type: "string", description: "Location of the event (optional for create operation)" }, notes: { type: "string", description: "Additional notes for the event (optional for create operation)" }, isAllDay: { type: "boolean", description: "Whether the event is an all-day event (optional for create operation, default is false)" }, calendarName: { type: "string", description: "Name of the calendar to create the event in (optional for create operation, uses default calendar if not specified)" } }, required: ["operation"] } };
- index.ts:140-143 (registration)Server-side registration in the CallToolRequest handler switch statement, validating args with CalendarArgsSchema and calling handleCalendar.case "calendar": { const validatedArgs = CalendarArgsSchema.parse(args); return await handleCalendar(validatedArgs, loadModule); }
- utils/calendar.ts:573-581 (helper)Exports the low-level calendar module with JXA-based functions: searchEvents, openEvent, getEvents, createEvent. These are called by the handler.const calendar = { searchEvents, openEvent, getEvents, createEvent }; export default calendar;