icloud-mcp

by thomascrouzet

Not rated
GitHub

About

stdio MCP for iCloud Calendar with optional Contacts and Mail

Details

Author
thomascrouzet
Categories
Productivity

Setup

Install icloud-mcp in your MCP client (Claude Desktop, Cursor, Windsurf, and others).

Repository: https://github.com/thomascrouzet/icloud-mcp

Follow the installation instructions in the repository README, then restart your MCP client.

UnifiedApple/iCloudMCP server forCalendar, Contacts, and Mail: one static Go binary,Model Context ProtocolJSON-RPC onstdio.

Remote protocols only(CalDAV, CardDAV, IMAP, SMTP with app-specific passwords). Not macOS EventKit, AppleScript, browser automation, or a private Apple API. Runs headless on Linux and macOS; pure Go also builds for Windows (CI smoke-buildswindows/amd64; GitHub Release archives ship linux/amd64, linux/arm64, and darwin/arm64). Suitable for agents and orchestration, not only a desktop chat app.

Host-agnostic.Any MCP client that can spawn a child with an environment and wire stdin/stdout works: personal agents, Hermes, OpenClaw, IDE bridges, runners, or other stdio hosts. No preferred model vendor, chat product, or reseller. Configuration is the process environment only; the binary does not parse host-specific config files or.env.

Reminders, Notes, Photos, Drive, Messages, and similar apps areout of scopeuntil Apple documents a suitable remote third-party connector. Details:Supported scope.

go install github.com/ThomasCrouzet/icloud-mcp/cmd/icloud-mcp@latest # or: make build # or: make release VERSION=v0.4.0 # or: make release-all VERSION=v0.4.0

- Create anapp-specific password(never the main Apple Account password).
- Export a minimal environment (recommended first deploy: Calendarread-only):

export ICLOUD_EMAIL='you@icloud.com' export ICLOUD_PASSWORD='your-app-specific-password' export ICLOUD_MCP_READ_ONLY=true export ICLOUD_MCP_DEFAULT_TZ=Europe/Paris # owner IANA zone; default UTC

- Registercommand= absolute path toicloud-mcpand thisenvin your MCP host (YAML, JSON, TOML, UI, or orchestrator; format is host-specific).
- Stdio = JSON-RPC;stderr= logs and mutation audit. Reload the host after env changes.

With that config the process exposes7 tools(Calendar reads + local helpers +icloud_capabilities). No Contacts or Mail client is constructed.

Optional domains (still host-agnosticexportform):

# Contacts reads (writes also need ICLOUD_MCP_READ_ONLY=false) export ICLOUD_MCP_ENABLE_CONTACTS=true # Mail reads: IMAP identity may differ from ICLOUD_EMAIL (e.g. name@icloud.com) export ICLOUD_MCP_ENABLE_MAIL=true export ICLOUD_MAIL_ADDRESS='mailbox@icloud.com' # export ICLOUD_MAIL_PASSWORD='...' # optional; else copy of ICLOUD_PASSWORD # Mail mutation (flags / move / trash); independent of send export ICLOUD_MCP_ENABLE_MAIL_WRITE=true export ICLOUD_MCP_READ_ONLY=false # Mail send: requires exact recipient allowlist even under global read-only export ICLOUD_MCP_ENABLE_MAIL_SEND=true export ICLOUD_MCP_SMTP_ALLOWED_RECIPIENTS='alice@example.com,bob@example.net'

Boot-onlyfile://secrets (regular files only, at most 4 KiB, mode 0600 or stricter; never re-read after start):

export ICLOUD_EMAIL='file:///run/secrets/icloud-email' export ICLOUD_PASSWORD='file:///run/secrets/icloud-password' export ICLOUD_MAIL_ADDRESS='file:///run/secrets/icloud-mail-address' export ICLOUD_MAIL_PASSWORD='file:///run/secrets/icloud-mail-password'

See.env.examplefor the full 12-variable contract.

Maximum23tools. Disabled tools are absent fromtools/list; disabled domain clients are not constructed.

ICLOUD_MCP_READ_ONLY=trueremoves every Calendar/Contacts write, every Mail mutation, and Mail send. It does not enable a disabled read domain.

Highlights:occurrence-aware Calendar update/delete with strongIf-Match; Contacts opaque book IDs and vCard 3.0 writes; Mail identity(mailbox, UIDVALIDITY, UID), PEEK reads, SMTP exact-recipient policy.set_message_flagsfails closed withprotocol_errorwhen CONDSTORE is advertised and tagged MODIFIED cannot be observed (go-imap beta.8). Full behavior notes:docs/caldav-compatibility.md,docs/carddav-compatibility.md,docs/mail-compatibility.md.

Idempotency:create_event/create_contactuse server-side UID keys (client_uidor aliasidempotency_key): a repeat create conflicts if the UID already exists (never silent overwrite).update_event/update_contactoptionalidempotency_keyisprocess-local only(in-memory cache,15 minute TTL, cleared on process restart). Same key + same params returns the cached success; same key + different params isconflict. Prefer combining update keys with a strongetag. Seedocs/error-codes.md.

Exactly12product environment variables:

Booleans accept only unset,0,false,1, ortrue. Invalid values fail at boot. Config is validatedbeforeany network access: Mail write/send without Mail, Mail without address/password, or send without recipient policy are boot errors (including under read-only for the send policy). Global read-only can coexist with write/send flags but suppresses their registration.

Log levels are trimmed and case-insensitive:debug/-4,info,warn/warning/2, anderror/4. Unset or unrecognized values useinfo.

Flags:-version; optional-health 127.0.0.1:port(loopback-only/healthzand/statusJSON with domains and rate limits); optional-audit-format=json|text(defaultjsonmutation audit on stderr).

- Calendarinputstart/end: RFC3339 with offset, or wall clock without offset inICLOUD_MCP_DEFAULT_TZ. Prefer no-offset for the user's local time. Recurring or explicit-timezone creates write TZID + VTIMEZONE; non-recurring timed defaults to UTCZon the wire. All-day usesVALUE=DATE.
- Calendaroutput: timed events always use RFC3339 with an explicit numeric offset inICLOUD_MCP_DEFAULT_TZ(never bareZ). All-day dates areYYYY-MM-DD. Seecalendar_capabilities.outputFormat.
- Contacts birthdays: writeYYYY-MM-DDonly.
- Mail search:sinceinclusive,beforeexclusive (YYYY-MM-DD).

Agent error codes and retry policy:docs/error-codes.md. Host wiring examples:docs/agent-hosts.md. Product roadmap:ROADMAP.md.

Untrusted remote text can influence an LLM on the host; labels are not a boundary. A compromised model can call everyregisteredtool. Same model for every host and vendor.

- Egress fixed:Calendarcaldav.icloud.com/p[0-9]{1,3}-caldav.icloud.com:443; Contacts matching contacts hosts; IMAPimap.mail.me.com:993; SMTPsmtp.mail.me.com:587with mandatory STARTTLS. No configurable destinations, no proxy env for DAV, TLS 1.2+ verified.
- Isolation:separate credentials, transports/dialers, limiters, semaphores, and protocol stacks per domain; no union authenticated HTTP client.
- Secrets:redacted (including Basic and SASL PLAIN forms); boot-onlyfile://reads require mode 0600 or stricter; noos/exec, telemetry, or disk write after boot.
- Audit:mutations logdomain,resourceType, process-local HMACresourceTokenonly (never raw paths, UIDs, mailboxes, recipients).
- Residual risk:one process holds every enabled domain's credentials; feature flags do not remove compiled code. Prefer read-only, least domains, dedicated Mail password, or separate processes when stronger isolation is required.

Full policy:SECURITY.md,docs/security.md. Architecture:[docs/architecture.md.

No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.