list_calendar_events
Retrieve calendar events from Fastmail. Filter by date range and specific calendar to get the events you need.
Instructions
List events from a calendar
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| calendarId | No | ID of the calendar (optional, defaults to all calendars) | |
| startDate | No | Filter events starting from this date (ISO 8601, e.g. 2026-03-23T00:00:00Z) | |
| endDate | No | Filter events ending before this date (ISO 8601, e.g. 2026-03-30T00:00:00Z) | |
| limit | No | Maximum number of events to return (default: 50) |
Implementation Reference
- src/index.ts:494-519 (registration)Tool 'list_calendar_events' is registered in the ListToolsRequestSchema handler with its name, description, and inputSchema (calendarId, startDate, endDate, limit).
{ name: 'list_calendar_events', description: 'List events from a calendar', inputSchema: { type: 'object', properties: { calendarId: { type: 'string', description: 'ID of the calendar (optional, defaults to all calendars)', }, startDate: { type: 'string', description: 'Filter events starting from this date (ISO 8601, e.g. 2026-03-23T00:00:00Z)', }, endDate: { type: 'string', description: 'Filter events ending before this date (ISO 8601, e.g. 2026-03-30T00:00:00Z)', }, limit: { type: 'number', description: 'Maximum number of events to return (default: 50)', default: 50, }, }, }, }, - src/index.ts:1321-1335 (handler)Handler for 'list_calendar_events' in the CallToolRequestSchema switch-case. Extracts calendarId, limit, startDate, endDate from args; first tries JMAP via ContactsCalendarClient.getCalendarEvents(), falls back to CalDAVCalendarClient.getCalendarEvents().
case 'list_calendar_events': { const { calendarId, limit = 50, startDate, endDate } = args as any; try { const contactsClient = initializeContactsCalendarClient(); const events = await contactsClient.getCalendarEvents(calendarId, limit); return { content: [{ type: 'text', text: JSON.stringify(events, null, 2) }] }; } catch { const davClient = initializeCalDAVClient(); if (!davClient) { throw new McpError(ErrorCode.InvalidRequest, 'JMAP calendars not available and CalDAV not configured. Set FASTMAIL_CALDAV_USERNAME and FASTMAIL_CALDAV_PASSWORD to use CalDAV.'); } const events = await davClient.getCalendarEvents(calendarId, limit, startDate, endDate); return { content: [{ type: 'text', text: JSON.stringify(events, null, 2) }] }; } } - src/contacts-calendar.ts:150-184 (handler)JMAP implementation: ContactsCalendarClient.getCalendarEvents() - checks permissions, builds CalendarEvent/query + CalendarEvent/get JMAP request with optional calendarId filter, sorts by start ascending, returns events.
async getCalendarEvents(calendarId?: string, limit: number = 50): Promise<any[]> { // Check permissions first const hasPermission = await this.checkCalendarsPermission(); if (!hasPermission) { throw new Error('Calendar access not available. This account may not have JMAP calendar permissions enabled. Please check your Fastmail account settings or contact support to enable calendar API access.'); } const session = await this.getSession(); const filter = calendarId ? { inCalendar: calendarId } : {}; const request: JmapRequest = { using: ['urn:ietf:params:jmap:core', 'urn:ietf:params:jmap:calendars'], methodCalls: [ ['CalendarEvent/query', { accountId: session.accountId, filter, sort: [{ property: 'start', isAscending: true }], limit }, 'query'], ['CalendarEvent/get', { accountId: session.accountId, '#ids': { resultOf: 'query', name: 'CalendarEvent/query', path: '/ids' }, properties: ['id', 'title', 'description', 'start', 'end', 'location', 'participants'] }, 'events'] ] }; try { const response = await this.makeRequest(request); return this.getListResult(response, 1); } catch (error) { throw new Error(`Calendar events access not supported: ${error instanceof Error ? error.message : String(error)}. Try checking account permissions or enabling calendar API access in Fastmail settings.`); } } - src/caldav-client.ts:226-263 (handler)CalDAV fallback implementation: CalDAVCalendarClient.getCalendarEvents() - fetches calendar objects from DAVClient, with optional calendarId filter, optional timeRange filter (startDate/endDate), parses iCalendar VEVENT data, sorts by start date, limits results.
async getCalendarEvents(calendarId?: string, limit: number = 50, startDate?: string, endDate?: string): Promise<CalendarEvent[]> { const client = await this.getClient(); if (!this.calendars) { this.calendars = await client.fetchCalendars(); } let targetCalendars = this.calendars.filter( c => c.displayName !== 'DEFAULT_TASK_CALENDAR_NAME' ); if (calendarId) { targetCalendars = targetCalendars.filter( c => c.url === calendarId || c.displayName === calendarId ); } const fetchOptions: any = {}; if (startDate || endDate) { fetchOptions.timeRange = { start: startDate || '1970-01-01T00:00:00Z', end: endDate || '2099-12-31T23:59:59Z', }; } const allEvents: CalendarEvent[] = []; for (const cal of targetCalendars) { const objects = await client.fetchCalendarObjects({ calendar: cal, ...fetchOptions }); for (const obj of objects) { allEvents.push(parseCalendarObject(obj)); } if (allEvents.length >= limit) break; } // Sort by start date ascending allEvents.sort((a, b) => (a.start || '').localeCompare(b.start || '')); return allEvents.slice(0, limit); } - src/caldav-client.ts:95-125 (helper)Helper functions for CalDAV event parsing: parseCalendarObject() parses DAVCalendarObject into CalendarEvent, extractVEvent() extracts VEVENT block, parseICalValue() and formatICalDate() handle iCalendar property parsing.
export function parseCalendarObject(obj: DAVCalendarObject): CalendarEvent { const vevent = extractVEvent(obj.data || ''); const title = parseICalValue(vevent, 'SUMMARY') || 'Untitled'; const description = parseICalValue(vevent, 'DESCRIPTION'); const rawStart = parseICalValue(vevent, 'DTSTART'); const rawEnd = parseICalValue(vevent, 'DTEND'); const location = parseICalValue(vevent, 'LOCATION'); const uid = parseICalValue(vevent, 'UID') || obj.url || ''; return { id: uid, url: obj.url || '', title: unescapeICalText(title), description: description ? unescapeICalText(description) : undefined, start: formatICalDate(rawStart), end: formatICalDate(rawEnd), location: location ? unescapeICalText(location) : undefined, }; } /** * Unescape an iCalendar text value (RFC 5545 §3.3.11). * Reverses escaping of newlines, semicolons, commas, and backslashes. */ export function unescapeICalText(value: string): string { return value .replace(/\\n/gi, '\n') .replace(/\\;/g, ';') .replace(/\\,/g, ',') .replace(/\\\\/g, '\\'); }