feat(connectors): Apple Calendar connector for macOS#619
Conversation
Reads events directly from the macOS Calendar SQLite database at ~/Library/Group Containers/group.com.apple.calendar/Calendar.sqlitedb in read-only mode — no API, no OAuth, no app-specific password. Features: - Yields upcoming events as Documents with title, date, calendar name, location, and notes fields - Configurable lookahead/lookbehind window (default: 7 days ahead, 1 behind) - Apple epoch timestamp conversion (2001-01-01 UTC baseline) - Joins CalendarItem + Calendar + Location tables - Filters hidden/deleted events (hidden = 0) - mcp_tools(): calendar_upcoming + calendar_search for agent use - Registered as "apple_calendar" in ConnectorRegistry Requires Full Disk Access granted to the terminal/app in System Settings → Privacy & Security → Full Disk Access. Tested on macOS 15 (Sequoia) with iCloud-synced calendars. Follows the apple_contacts.py connector pattern exactly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
||
| timestamp = _apple_ts(last_mod) if last_mod else _apple_ts(start_date) | ||
| if since is not None: | ||
| since_utc = since if since.tzinfo else since.replace(tzinfo=timezone.utc) |
There was a problem hiding this comment.
This line trips ruff E501 in this repo (line too long). Please wrap it before merge.
There was a problem hiding this comment.
Thanks for putting this together. I like the direction here, especially keeping the Apple Calendar path local/no-auth and matching the existing Apple connector shape.
I found a few things that need fixing before this is mergeable:
- CI/lint currently fails on
src/openjarvis/connectors/apple_calendar.py:230withE501 line too long. - Event display times are currently formatted from UTC datetimes and then labeled with the event timezone. That can show the wrong local time, e.g. a 9 AM Pacific event rendering as 4/5 PM with
(America/Los_Angeles)appended. The formatter should convert throughzoneinfo.ZoneInfo(start_tz)before rendering, with care for_floatand all-day events. - Recurring events appear likely to be missed because the query filters directly on
CalendarItem.start_date BETWEEN ? AND ?. For recurring meetings created outside the lookahead window, Apple Calendar usually stores recurrence separately, so those upcoming occurrences may not show up. - The
mcp_tools()specs advertisecalendar_upcomingandcalendar_search, but this PR does not add executable tool implementations or register them. They may show up as connector metadata, but agents won’t be able to call them through the normal tool path yet.
Could you also add focused tests with a synthetic SQLite DB for basic sync, missing DB behavior, timezone formatting, and ideally recurrence behavior?
| start_tz, _loc_id, has_attendees, _last_mod, _uuid, cal_title, _color, | ||
| ) = row | ||
|
|
||
| start_dt = _apple_ts(start_ts) |
There was a problem hiding this comment.
This converts Apple timestamps to UTC and the formatter later renders that UTC value directly. For timed events with start_tz, this can display the wrong local wall time. Please convert to ZoneInfo(start_tz) before formatting, while preserving _float and all-day behavior.
| c.color AS calendar_color | ||
| FROM CalendarItem ci | ||
| JOIN Calendar c ON ci.calendar_id = c.ROWID | ||
| WHERE ci.start_date BETWEEN ? AND ? |
There was a problem hiding this comment.
Filtering only on CalendarItem.start_date BETWEEN ? AND ? likely misses recurring events whose original start date is outside the window. Can you verify this against recurring meetings and either expand occurrences from Apple Calendar’s recurrence/occurrence tables or document/test the limitation?
Summary
apple_calendar.pyconnector that reads macOS Calendar events directly from the local SQLite database — no API, no OAuth, no credentials"apple_calendar"inConnectorRegistry, auto-imported inconnectors/__init__.pyalongsideapple_contactsapple_contacts.pypattern exactlyDetails
DB path:
~/Library/Group Containers/group.com.apple.calendar/Calendar.sqlitedbRequires: Full Disk Access in System Settings → Privacy & Security → Full Disk Access (same requirement as
apple_contacts,apple_notes, etc.)What it yields: One
Documentper upcoming event with:title= event summarycontent= formatted card (date, calendar name, location, notes, attendees flag)metadata={summary, calendar, start, all_day, location, has_attendees}doc_type = "calendar_event"Configurable:
days_ahead(default: 7) anddays_behind(default: 1)Timestamp handling: Apple epoch (2001-01-01 UTC), same convention as
apple_contactsmcp_tools(): exposescalendar_upcomingandcalendar_searchfor agent useTest plan
AppleCalendarConnector().is_connected()returnsTrueon macOS with Calendar datalist(connector.sync())yields real events from system calendars (iCloud, subscribed, local)hidden = 0filter excludes deleted/declined eventsjarvis digest --freshwithsources = ["apple_calendar"]in config — calendar section rendersTested on macOS 15 (Sequoia), M3 Ultra, iCloud-synced calendars (personal, subscribed F1/holiday feeds).
🤖 Generated with Claude Code