Skip to main content
Glama

calendar

Search, create, and manage calendar events directly in the Apple Calendar app. Perform operations like searching event details, opening specific events, listing upcoming events, or creating new events with customizable details.

Instructions

Search, create, and open calendar events in Apple Calendar app

Input Schema

NameRequiredDescriptionDefault
calendarNameNoName of the calendar to create the event in (optional for create operation, uses default calendar if not specified)
endDateNoEnd date/time of the event in ISO format (required for create operation)
eventIdNoID of the event to open (required for open operation)
fromDateNoStart date for search range in ISO format (optional, default is today)
isAllDayNoWhether the event is an all-day event (optional for create operation, default is false)
limitNoNumber of events to retrieve (optional, default 10)
locationNoLocation of the event (optional for create operation)
notesNoAdditional notes for the event (optional for create operation)
operationYesOperation to perform: 'search', 'open', 'list', or 'create'
searchTextNoText to search for in event titles, locations, and notes (required for search operation)
startDateNoStart date/time of the event in ISO format (required for create operation)
titleNoTitle of the event to create (required for create operation)
toDateNoEnd 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

  • Definition of the CALENDAR_TOOL including name, description, and detailed input schema for operations: search, open, list, create.
    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"] } };
  • tools.ts:308-308 (registration)
    Registration of the calendar tool as part of the exported tools array.
    const tools = [CONTACTS_TOOL, NOTES_TOOL, MESSAGES_TOOL, MAIL_TOOL, REMINDERS_TOOL, WEB_SEARCH_TOOL, CALENDAR_TOOL, MAPS_TOOL];
  • Helper function to search calendar events by text in title/location/notes within date range.
    async function searchEvents( searchText: string, limit: number = 10, fromDate?: string, toDate?: string ): Promise<CalendarEvent[]> { try { if (!await checkCalendarAccess()) { return []; } console.error(`searchEvents - Processing calendars for search: "${searchText}"`); const events = await run((args: { searchText: string, limit: number, fromDate?: string, toDate?: string, maxEventsPerCalendar: number }) => { try { const Calendar = Application("Calendar"); // Set default date range if not provided (today to 30 days from now) const today = new Date(); const defaultStartDate = today; const defaultEndDate = new Date(); defaultEndDate.setDate(today.getDate() + 30); const startDate = args.fromDate ? new Date(args.fromDate) : defaultStartDate; const endDate = args.toDate ? new Date(args.toDate) : defaultEndDate; // Array to store matching events const matchingEvents: CalendarEvent[] = []; // Get all calendars at once const allCalendars = Calendar.calendars(); // Search in each calendar for (let i = 0; i < allCalendars.length && matchingEvents.length < args.limit; i++) { try { const calendar = allCalendars[i]; const calendarName = calendar.name(); // Get all events from this calendar const events = calendar.events.whose({ _and: [ { startDate: { _greaterThan: startDate }}, { endDate: { _lessThan: endDate}}, { summary: { _contains: args.searchText}} ] }); const convertedEvents = events(); // Limit the number of events to process const eventCount = Math.min(convertedEvents.length, args.maxEventsPerCalendar); // Filter events by date range and search text for (let j = 0; j < eventCount && matchingEvents.length < args.limit; j++) { const event = convertedEvents[j]; try { const eventStartDate = new Date(event.startDate()); const eventEndDate = new Date(event.endDate()); // Skip events outside our date range if (eventEndDate < startDate || eventStartDate > endDate) { continue; } // Get event details let title = ""; let location = ""; let notes = ""; try { title = event.summary(); } catch (e) { title = "Unknown Title"; } try { location = event.location() || ""; } catch (e) { location = ""; } try { notes = event.description() || ""; } catch (e) { notes = ""; } // Check if event matches search text if ( title.toLowerCase().includes(args.searchText.toLowerCase()) || location.toLowerCase().includes(args.searchText.toLowerCase()) || notes.toLowerCase().includes(args.searchText.toLowerCase()) ) { // Create event object const eventData: CalendarEvent = { id: "", title: title, location: location, notes: notes, startDate: null, endDate: null, calendarName: calendarName, isAllDay: false, url: null }; try { eventData.id = event.uid(); } catch (e) { eventData.id = `unknown-${Date.now()}-${Math.random()}`; } try { eventData.startDate = eventStartDate.toISOString(); } catch (e) { /* Keep as null */ } try { eventData.endDate = eventEndDate.toISOString(); } catch (e) { /* Keep as null */ } try { eventData.isAllDay = event.alldayEvent(); } catch (e) { /* Keep as false */ } try { eventData.url = event.url(); } catch (e) { /* Keep as null */ } matchingEvents.push(eventData); } } catch (e) { // Skip events we can't process console.log("searchEvents - Error processing events: ----0----", JSON.stringify(e)); continue; } } } catch (e) { // Skip calendars we can't access console.log("searchEvents - Error processing calendars: ----1----", JSON.stringify(e)); continue; } } return matchingEvents; } catch (e) { return []; // Return empty array on any error } }, { searchText, limit, fromDate, toDate, maxEventsPerCalendar: CONFIG.MAX_EVENTS_PER_CALENDAR }) as CalendarEvent[]; // If no events found, create dummy events if (events.length === 0) { console.error("searchEvents - No events found, creating dummy events"); return []; } return events; } catch (error) { console.error(`Error searching events: ${error instanceof Error ? error.message : String(error)}`); // Fall back to dummy events on error return []; } }
  • Helper function to open a specific calendar event by ID in the Calendar app.
    async function openEvent(eventId: string): Promise<{ success: boolean; message: string }> { try { if (!await checkCalendarAccess()) { return { success: false, message: "Cannot access Calendar app. Please grant access in System Settings > Privacy & Security > Automation." }; } console.error(`openEvent - Attempting to open event with ID: ${eventId}`); const result = await run((args: { eventId: string, maxEventsPerCalendar: number }) => { try { const Calendar = Application("Calendar"); // Get all calendars at once const allCalendars = Calendar.calendars(); // Search in each calendar for (let i = 0; i < allCalendars.length; i++) { try { const calendar = allCalendars[i]; // Get the event from this calendar const events = calendar.events.whose({ uid: { _equals: args.eventId } }); const event = events[0] if(event.uid() === args.eventId) { Calendar.activate(); event.show(); return { success: true, message: `Successfully opened event: ${event.summary()}` }; } } catch (e) { // Skip calendars we can't access console.log("openEvent - Error processing calendars: ----2----", JSON.stringify(e)); continue; } } return { success: false, message: `No event found with ID: ${args.eventId}` }; } catch (e) { return { success: false, message: "Error opening event" }; } }, { eventId, maxEventsPerCalendar: CONFIG.MAX_EVENTS_PER_CALENDAR }) as { success: boolean; message: string }; return result; } catch (error) { return { success: false, message: `Error opening event: ${error instanceof Error ? error.message : String(error)}` }; } }
  • Helper function to list calendar events within a date range (for 'list' operation).
    async function getEvents( limit: number = 10, fromDate?: string, toDate?: string ): Promise<CalendarEvent[]> { try { console.error("getEvents - Starting to fetch calendar events"); if (!await checkCalendarAccess()) { console.error("getEvents - Failed to access Calendar app"); return []; } console.error("getEvents - Calendar access check passed"); const events = await run((args: { limit: number, fromDate?: string, toDate?: string, maxEventsPerCalendar: number }) => { try { // Access the Calendar app directly const Calendar = Application("Calendar"); // Set default date range if not provided (today to 7 days from now) const today = new Date(); const defaultStartDate = today; const defaultEndDate = new Date(); defaultEndDate.setDate(today.getDate() + 7); const startDate = args.fromDate ? new Date(args.fromDate) : defaultStartDate; const endDate = args.toDate ? new Date(args.toDate) : defaultEndDate; const calendars = Calendar.calendars(); // Array to store events const events: CalendarEvent[] = []; // Get events from each calendar for (const calender of calendars) { if (events.length >= args.limit) break; try { // Get all events from this calendar const calendarEvents = calender.events.whose({ _and: [ { startDate: { _greaterThan: startDate }}, { endDate: { _lessThan: endDate}} ] }); const convertedEvents = calendarEvents(); // Limit the number of events to process const eventCount = Math.min(convertedEvents.length, args.maxEventsPerCalendar); // Process events for (let i = 0; i < eventCount && events.length < args.limit; i++) { const event = convertedEvents[i]; try { const eventStartDate = new Date(event.startDate()); const eventEndDate = new Date(event.endDate()); // Skip events outside our date range if (eventEndDate < startDate || eventStartDate > endDate) { continue; } // Create event object const eventData: CalendarEvent = { id: "", title: "Unknown Title", location: null, notes: null, startDate: null, endDate: null, calendarName: calender.name(), isAllDay: false, url: null }; try { eventData.id = event.uid(); } catch (e) { eventData.id = `unknown-${Date.now()}-${Math.random()}`; } try { eventData.title = event.summary(); } catch (e) { /* Keep default title */ } try { eventData.location = event.location(); } catch (e) { /* Keep as null */ } try { eventData.notes = event.description(); } catch (e) { /* Keep as null */ } try { eventData.startDate = eventStartDate.toISOString(); } catch (e) { /* Keep as null */ } try { eventData.endDate = eventEndDate.toISOString(); } catch (e) { /* Keep as null */ } try { eventData.isAllDay = event.alldayEvent(); } catch (e) { /* Keep as false */ } try { eventData.url = event.url(); } catch (e) { /* Keep as null */ } events.push(eventData); } catch (e) { // Skip events we can't process continue; } } } catch (e) { // Skip calendars we can't access console.log("getEvents - Error processing events: ----0----", JSON.stringify(e)); continue; } } return events; } catch (e) { console.log("getEvents - Error processing events: ----1----", JSON.stringify(e)); return []; // Return empty array on any error } }, { limit, fromDate, toDate, maxEventsPerCalendar: CONFIG.MAX_EVENTS_PER_CALENDAR }) as CalendarEvent[]; // If no events found, create dummy events if (events.length === 0) { console.error("getEvents - No events found, creating dummy events"); return []; } return events; } catch (error) { console.error(`Error getting events: ${error instanceof Error ? error.message : String(error)}`); return []; } }
  • Helper function to create a new calendar event with given details.
    async function createEvent( title: string, startDate: string, endDate: string, location?: string, notes?: string, isAllDay: boolean = false, calendarName?: string ): Promise<{ success: boolean; message: string; eventId?: string }> { try { if (!await checkCalendarAccess()) { return { success: false, message: "Cannot access Calendar app. Please grant access in System Settings > Privacy & Security > Automation." }; } console.error(`createEvent - Attempting to create event: "${title}"`); const result = await run((args: { title: string, startDate: string, endDate: string, location?: string, notes?: string, isAllDay: boolean, calendarName?: string }) => { try { const Calendar = Application("Calendar"); // Parse dates const startDateTime = new Date(args.startDate); const endDateTime = new Date(args.endDate); // Find the target calendar let targetCalendar; if (args.calendarName) { // Find the specified calendar const calendars = Calendar.calendars.whose({ name: { _equals: args.calendarName } }); if (calendars.length > 0) { targetCalendar = calendars[0]; } else { return { success: false, message: `Calendar "${args.calendarName}" not found.` }; } } else { // Use default calendar // Calendar.defaultCalendar() doesn't exist - get the first calendar instead const allCalendars = Calendar.calendars(); if (allCalendars.length === 0) { return { success: false, message: "No calendars found in Calendar app." }; } targetCalendar = allCalendars[0]; } // Create the new event const newEvent = Calendar.Event({ summary: args.title, startDate: startDateTime, endDate: endDateTime, location: args.location || "", description: args.notes || "", alldayEvent: args.isAllDay }); // Add the event to the calendar targetCalendar.events.push(newEvent); return { success: true, message: `Event "${args.title}" created successfully.`, eventId: newEvent.uid() }; } catch (e) { return { success: false, message: `Error creating event: ${e instanceof Error ? e.message : String(e)}` }; } }, { title, startDate, endDate, location, notes, isAllDay, calendarName }) as { success: boolean; message: string; eventId?: string }; return result; } catch (error) { return { success: false, message: `Error creating event: ${error instanceof Error ? error.message : String(error)}` }; }

Other Tools

Related Tools

Latest Blog Posts

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/jxnl/apple-mcp'

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