The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Icloud MCP listing page.
This unified Apple/iCloud MCP server supports Calendar, Contacts, and Mail. It is one static Go binary. It uses Model Context Protocol JSON-RPC on stdio.
The server uses only remote protocols: CalDAV, CardDAV, IMAP, and SMTP with app-specific passwords. It does not use macOS EventKit, AppleScript, browser automation, or a private Apple API. It runs headless on Linux and macOS. Pure Go also builds for Windows.
CI runs a windows/amd64 smoke build. GitHub Release archives contain
linux/amd64, linux/arm64, and darwin/arm64 builds. Agents, orchestrators, and
desktop chat applications can use the server.
Host-agnostic. Any MCP client can use the server if it starts child processes
with an environment and connects stdin and stdout. Compatible clients include
personal agents, Hermes, OpenClaw, IDE bridges, runners, and other stdio hosts.
The server does not prefer a model vendor, chat product, or reseller. Configure
the server only through its process environment. The binary does not parse
host-specific config files or .env.
| Domain | Protocol | Default |
|---|---|---|
| Calendar | CalDAV HTTPS | Always on (read + write unless global read-only) |
| Contacts | CardDAV HTTPS | Off until ICLOUD_MCP_ENABLE_CONTACTS |
| Mail read | IMAP TLS | Off until ICLOUD_MCP_ENABLE_MAIL |
| Mail mutation | IMAP | Off until Mail + ICLOUD_MCP_ENABLE_MAIL_WRITE |
| Mail send | SMTP STARTTLS | Off until Mail + ICLOUD_MCP_ENABLE_MAIL_SEND + recipient policy |
Reminders, Notes, Photos, Drive, Messages, and similar apps are out of scope. They remain out of scope until Apple documents a suitable remote third-party connector. See Supported scope.
icloud-mcp path as the command in your MCP host.
Register the environment as env. The host can use YAML, JSON, TOML, a UI,
or an orchestrator. The exact format depends on the host.This configuration exposes 7 tools: Calendar reads, local helpers, and
icloud_capabilities. The process does not construct a Contacts or Mail client.
Optional domains (still host-agnostic export form):
You can load secrets from file:// only at boot. Each file must be a regular
file of at most 4 KiB with mode 0600 or stricter. The server does not read the
file again after startup:
See .env.example for the full 12-variable contract.
The server exposes a maximum of 23 tools. Disabled tools are absent from
tools/list. The process does not construct clients for disabled domains.
| Count | When |
|---|---|
| 10 | Default: Calendar read+write + icloud_capabilities |
| 7 | Global read-only, optional domains off (recommended first run) |
| 23 | Contacts + Mail read + mutation + send, read-only off |
ICLOUD_MCP_READ_ONLY=true removes every Calendar/Contacts write, every Mail
mutation, and Mail send. It does not enable a disabled read domain.
| Group | Tools |
|---|---|
| Global | icloud_capabilities |
| Calendar read | list_calendars, search_events, get_event, find_free_slots, validate_event, calendar_capabilities |
| Calendar write | create_event, update_event, delete_event |
| Contacts read | list_address_books, search_contacts, get_contact |
| Contacts write | create_contact, update_contact, delete_contact |
| Mail read | list_mailboxes, search_messages, get_message |
| Mail mutation | set_message_flags, move_message, trash_message |
| Mail send | send_message |
Highlights: Calendar update and delete operations understand occurrences
and use strong If-Match. Contacts uses opaque book IDs and writes vCard 3.0.
Mail uses (mailbox, UIDVALIDITY, UID) identities, PEEK reads, and an exact
recipient policy for SMTP. set_message_flags fails closed with
protocol_error when CONDSTORE is advertised. This occurs because go-imap
beta.8 cannot observe tagged MODIFIED. See the full behavior notes:
docs/caldav-compatibility.md,
docs/carddav-compatibility.md,
docs/mail-compatibility.md.
Idempotency: create_event and create_contact use server-side UID keys.
Use client_uid or its idempotency_key alias. A repeated create conflicts if
the UID exists. It never silently overwrites the object. The optional
idempotency_key for update_event and update_contact is
process-local only. Its in-memory cache has a 15 minute TTL and clears
when the process restarts.
The same key and parameters return the cached success. The same key with
different parameters returns conflict. Combine an update key with a strong
etag when possible. See
docs/error-codes.md.
Exactly 12 product environment variables:
| Variable | Default | Contract |
|---|---|---|
ICLOUD_EMAIL | none | Required Calendar/Contacts identity. file:// supported (regular file, <=4 KiB, mode 0600+). |
ICLOUD_PASSWORD | none | Required app-specific password. Mail uses it as a fallback. file:// as above. |
ICLOUD_MCP_READ_ONLY | false | Global mutation kill switch. |
ICLOUD_MCP_LOG_LEVEL | info | Stderr level. See the accepted forms below. |
ICLOUD_MCP_DEFAULT_TZ | UTC | IANA zone for offset-less Calendar inputs and recurring-write fallback. |
ICLOUD_MCP_ENABLE_CONTACTS | false | Contacts tools. Writes require read-only mode to be off. |
ICLOUD_MCP_ENABLE_MAIL | false | Mail reads. Requires a Mail address and password. |
ICLOUD_MAIL_ADDRESS | none | Full IMAP/SMTP address when Mail is on. file:// as above. |
ICLOUD_MAIL_PASSWORD | ICLOUD_PASSWORD | Optional dedicated Mail app password. file:// as above. |
ICLOUD_MCP_ENABLE_MAIL_WRITE | false | Three IMAP mutation tools. |
ICLOUD_MCP_ENABLE_MAIL_SEND | false | send_message (independent of Mail write). |
ICLOUD_MCP_SMTP_ALLOWED_RECIPIENTS | none | Required if send is on. Use exact addresses or literal *. Literal * causes a boot warning. Use exact addresses when possible. |
Booleans accept only unset, 0, false, 1, or true. Invalid values cause a
boot failure. The server validates the configuration before network access.
Mail write or send without Mail causes a boot error. Mail without an address or
password also causes a boot error. Send without a recipient policy causes a
boot error, including in read-only mode.
Global read-only can coexist with write and send flags, but it prevents their registration.
The server trims log levels and ignores case. It accepts debug or -4,
info, warn, warning, or 2, and error or 4. Unset or unrecognized
values use info.
Use -version to print the version. The optional
-health 127.0.0.1:port flag serves /healthz and /status on loopback. Both
endpoints return JSON with domains and rate limits. The optional
-audit-format=json|text flag selects the mutation audit format on stderr.
The default format is json.
start and end accept RFC3339 with an offset. They also
accept wall-clock values without an offset in ICLOUD_MCP_DEFAULT_TZ. Use
values without an offset for the user's local time. Recurring creates and
creates with an explicit timezone write TZID and VTIMEZONE. Non-recurring
timed creates default to UTC Z on the wire. All-day creates use
VALUE=DATE.ICLOUD_MCP_DEFAULT_TZ, never bare Z. All-day
dates use YYYY-MM-DD. See calendar_capabilities.outputFormat.YYYY-MM-DD only.since inclusive, before exclusive (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 do not form a security boundary. A compromised model can call every registered tool. This risk applies to every host and vendor.
caldav.icloud.com and matching
p[0-9]{1,3}-caldav.icloud.com:443 hosts. Contacts uses matching Contacts
hosts. IMAP uses imap.mail.me.com:993. SMTP uses
smtp.mail.me.com:587 with mandatory STARTTLS. Destinations are not
configurable. The server ignores DAV proxy environment variables and verifies
TLS 1.2 or later.file:// reads require mode 0600 or stricter. The server does not
use os/exec or telemetry. It does not write to disk after boot.domain, resourceType, and the process-local
HMAC resourceToken. They never contain raw paths, UIDs, mailboxes, or
recipients.Full policy: SECURITY.md, docs/security.md. Architecture: docs/architecture.md.
| Tool deadline | 25s (DAV HTTP 30s) |
| Stdio / MCP result | 1 MiB frame. 256 KiB result. Reflected protocol errors have a limit. |
| Calendar | Search range: 366 days. Results: 400 total, 2,500 per calendar, and 10,000 materialized across calendars. Recurrence: 2,000 expansions, 100k steps per series, and 250k steps per search. Rate: 60 reads and 20 writes per minute. Concurrency: 4 reads and 2 writes. |
| Contacts | 100 books. 100 summaries. 2000 cards scanned. Rate: 60 reads and 20 writes per minute. Concurrency: 4. |
| Rate: 60 reads, 20 mutations, and 20 sends per minute. Semaphores: 2 reads, 1 mutation, and 1 send. No mutation or send retry. | |
| Writes | No automatic replay of Calendar PUT/DELETE, Contacts writes, IMAP mutations, or SMTP. Ambiguous outcomes use outcome_unknown. |
The server discovers Calendar at boot and Contacts when first used. Each call uses a fresh IMAP or SMTP session. See these documents for rates, parser budgets, and retry rules: docs/testing.md, docs/architecture.md.
| Data | Connector | Support |
|---|---|---|
| Calendar | CalDAV | Always |
| Contacts | CardDAV | Optional |
| Mail read / mutation / send | IMAP + SMTP | Optional, independently gated |
| Modern Reminders, Notes, Photos, Drive, Find My, Keychain, Messages, Home | No suitable official remote connector for this model | Excluded |
The server does not treat modern Reminders as generic CalDAV VTODO. Apple's third-party documentation for this type of access covers Mail, Calendar, and Contacts.
Multi-account: one process holds one iCloud identity. Start a separate
icloud-mcp process for each identity. Give each process a separate environment
and, if needed, a separate -health port. The MCP host can multiplex these
processes. See docs/agent-hosts.md.
Use Go 1.26.0 or newer. The project has one module and exactly 10 direct dependencies. If you add a direct dependency, add its justification here.
| Dependency | Exact version | Justification |
|---|---|---|
github.com/emersion/go-webdav | v0.7.0 | CalDAV primitives. Discovery and conditional operations remain hand-written. |
github.com/emersion/go-ical | v0.0.0-20250609112844-439c63cef608 | iCalendar parse/encode |
github.com/mark3labs/mcp-go | v0.57.0 | MCP stdio, schemas, JSON-RPC |
github.com/teambition/rrule-go | v1.8.2 | Bounded recurrence with timezone preservation |
golang.org/x/time | v0.16.0 | Per-domain rate limiters |
github.com/emersion/go-vcard | v0.1.0 | vCard 3.0/4.0 read, 3.0 write |
github.com/emersion/go-imap/v2 | v2.0.0-beta.8 | IMAP behind internal/mail/imapadapter |
github.com/emersion/go-message | v0.18.2 | MIME / plain-text bounds |
github.com/emersion/go-smtp | v0.25.0 | SMTP + STARTTLS |
github.com/emersion/go-sasl | v0.0.0-20241020182733-b788ff22d5a6 | SASL PLAIN after STARTTLS |
Release targets reject an unset version or a dev version. Archives contain
the binary, LICENSE, and THIRD_PARTY_NOTICES.md. The dist/ directory also
contains a SHA-256 checksum file. GitHub tag releases run make release-all
only after CI and gitleaks succeed for that tag. They use Go 1.26.8 with
check-latest disabled.
Local make release uses the digest-pinned container path for linux/arm64.
Release blobs include cosign keyless signatures. -version first uses the
release ldflags value. If this value is absent, it uses Go module build
information. Thus, go install ...@version reports the module version.
CI runs race tests, fuzz smoke, govulncheck, gitleaks, and
multi-architecture builds. It also checks coverage, egress and security AST
guards, binary size, and public text. Aggregate coverage must be at least 78%.
Package floors include cmd/icloud-mcp and internal/health. The build check
includes windows/amd64 smoke. The binary budget is 20 MiB.
The public-text policy checks the tree and new commits. Live iCloud tests use
the integration build tag. These tests are optional and never run in CI. See
docs/testing.md.
Calendar tool shape and several patterns were inspired by
github.com/roygabriel/mcp-icloud-calendar
(MIT, copyright 2026 Gabe). Code was rewritten, not copied. This server adds
hard per-domain egress, redaction, bounded parsers, conditional mutation,
Contacts, Mail, and no telemetry.
See CONTRIBUTING.md. Run gofmt, make test, and make lint
before opening a pull request.
MIT. See LICENSE and third-party notices.