car-integrations 0.51.0

OS-native account-bound integrations (Calendar, Contacts, Mail) for CAR
Documentation

car-integrations

Account-bound integrations for Common Agent Runtime — Calendar, Contacts, Mail, Messages, Notes, Reminders, Photos, Bookmarks, Files, Keychain.

What it does

The logical capabilities are the same on every OS; the backends are not. v1 defines the release contract — stable return shapes that carry explicit available + backend fields so downstream apps branch cleanly while backends light up incrementally.

OS Calendar Contacts Mail Messages Other Apple account surfaces
macOS EventKit (EKEventStore) Contacts.framework Mail.app automation Messages.app automation Notes/Reminders/Photos app automation, Safari bookmarks, iCloud Drive paths, Keychain
Windows MS Graph + Outlook MAPI Windows.Contacts + Graph MS Graph + MAPI Not modeled Not modeled
Linux Evolution DS + CalDAV Evolution DS + CardDAV Evolution DS + IMAP Not modeled Not modeled

Dependencies (and honest gaps)

Full operation requires:

  • car-secrets — where credentials and tokens live
  • car-accounts — to know which account a call is bound to
  • car-permissions — to preflight OS consent before the side effect

The macOS Calendar, Contacts, Mail account discovery, and Messages account discovery paths are implemented. Backends still return available:false with an explicit reason when macOS denies TCC access or an OS service is not configured.

Mail read surface

Capability macOS (Mail.app / JXA) Microsoft Graph
list_accounts Mail.accounts() the signed-in M365 account
list_inbox per-account unread/total /me/mailFolders/inbox
list_mailboxes recursive mailboxes() walk /me/mailFolders
list_messages bulk array property gets on one mailbox /me/mailFolders/{id}/messages
message_body content() of one message /me/messages/{id}?$select=body
send OutgoingMessage /me/sendMail

list_mailboxes / list_messages / message_body closed Parslee-ai/car-releases#84. Before them the read path could only reach INBOX and carried no message id, so a caller whose server-side rules file mail out of INBOX got an empty result that reads as "nothing there" rather than "cannot see there" — and had to shell out to osascript, which splits the macOS Automation TCC identity between CAR-spawned sends and caller-spawned reads.

Two properties worth knowing before extending them:

  • Message ids are composite on macOS. A Mail.app numeric message id is only addressable inside one mailbox of one account, so the wire id is mailapp:<b64url account>:<b64url mailbox path>:<numeric> (Graph ids are just msgraph:<id>). encode_message_id / decode_message_id are the only supported readers.
  • The message walk uses bulk array property gets, not a per-message loop. Reading subject/sender/dateReceived/readStatus/id off the mailbox's messages specifier is five Apple Events for a mailbox of any size; reading them per message is one Apple Event per message per field, which is what puts a full mailbox scan near the 15s JXA timeout. Per-message reads (recipients, bodies) happen only for the rows that survive the limit.

Messages chat listing falls back to ~/Library/Messages/chat.db when Messages.app automation cannot enumerate chats. macOS protects that database behind Full Disk Access, which must be granted to the host app by the user; there is no entitlement that can silently grant it.

Where it fits

Surfaced via the WebSocket calendar.*, contacts.*, mail.*, messages.*, notes.*, reminders.*, photos.*, bookmarks.*, files.*, and keychain.* methods. See docs/websocket-protocol.md.