MCP server for Apple Calendar integration on macOS
Apple Calendar MCP demonstrates solid tool design with comprehensive parameter descriptions and clear naming conventions. All 10 tools follow verb_noun patterns (get_*, create_*, delete_*, search_*, update_*). Descriptions are detailed and contextual, averaging ~180 chars, well within the 10-1024 char baseline. Input schemas are present with type declarations and parameter descriptions. However, output schemas are not explicitly documented in the source code, responses are formatted as strings rather than structured JSON objects, which limits downstream tool chaining and forces LLMs to parse unstructured text. Error handling guidance is minimal. Tool composition is strong: tools are single-responsibility, and IDs flow correctly between tools (e.g., get_calendars returns calendar_id, which create_events accepts). Security is well-handled with no exposed credentials.
Create a new calendar.
Create one or more events in a calendar. Pass a JSON array with one element for a single event. Required fields: summary, start_date, end_date. Optional: location, notes, url, availability ("free"/"busy"/"tentative"/"unavailable"). - allday (bool): end_date is inclusive for all-day events. - recurrence: RRULE string (e.g. "FREQ=WEEKLY;INTERVAL=2;BYDAY=MO;COUNT=10") or structured object with frequency, interval, days_of_week, count, until. - alerts (list): Minutes-before (int) or typed objects ({"type": "absolute", "date": "ISO 8601"} or {"type": "proximity", "proximity": "enter"|"leave"}). Omit to inherit calendar defaults. Pass [] to suppress defaults. - timezone (str): IANA identifier. Schedule in a remote timezone without converting manually. - structured_location: Object with title, latitude, longitude, radius.
Permanently delete a calendar and all its events.
Delete one or more events. For recurring events, deleting without occurrence_date removes the entire series. Always check is_recurring first.
Check availability (free/busy) for a time slot across one or more calendars.
Output schemas not documented. Tools return formatted strings rather than structured JSON. LLMs must parse unstructured text, increasing errors and token waste. Downstream tools cannot reliably extract required fields (e.g., calendar_id from get_calendars response).
No error recovery guidance. Tools lack descriptions of failure modes and recovery steps. E.g., delete_calendar says 'Permanently delete' but does not explain what happens if the calendar is not found or is read-only.
Destructive operations lack confirmation/dry-run support. delete_calendar and delete_events are irreversible but have no confirmation step or dry-run mode to prevent accidental data loss.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 66 | 2026-07-28+ | v2 |
List all calendars in Apple Calendar. Returns each calendar's calendar_id (UUID), name, access level, source (account), description, color, and is_default flag. Calendar names are not guaranteed unique — even within the same source. Disambiguate by source, then color, then event contents. If all visible properties match, ask the user.
Find conflicting events in a calendar for a given time slot.
Get events from one or more calendars within a date range.
Search for events by title, notes, location, or attendee across all calendars.
Update one or more events. Only provided fields are changed; omitted fields are unchanged. Use get_events or search_events first to find event UIDs. Supports same fields as create_events, plus: - Pass "" to clear location, notes, url, or recurrence. Pass [] to clear alerts. - allday (bool): Include when updating dates on all-day events. - occurrence_date (str): Target a specific recurring event occurrence. - span: "this_event" (default) or "future_events" for recurring events.
create_events and update_events accept 'events' and 'updates' as JSON strings rather than structured objects. This forces LLMs to manually construct JSON, increasing serialization errors and making validation opaque.
No pagination or result limits documented. get_events and search_events may return unbounded result sets, risking context window exhaustion. No mention of limit, offset, or next_cursor parameters.