icloud-mcp

icloud-mcp

Unified Apple/iCloud MCP server for Calendar, Contacts, and Mail using CalDAV, CardDAV, IMAP, and SMTP protocols with app-specific passwords.

Category
Visit Server

README

icloud-mcp

CI Release License: MIT

Unified Apple/iCloud MCP server for Calendar, Contacts, and Mail: one static Go binary, Model Context Protocol JSON-RPC on stdio.

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-builds windows/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.

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 until Apple documents a suitable remote third-party connector. Details: Supported scope.

Quick start

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
  1. Create an app-specific password (never the main Apple Account password).
  2. Export a minimal environment (recommended first deploy: Calendar read-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
  1. Register command = absolute path to icloud-mcp and this env in your MCP host (YAML, JSON, TOML, UI, or orchestrator; format is host-specific).
  2. Stdio = JSON-RPC; stderr = logs and mutation audit. Reload the host after env changes.

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

Optional domains (still host-agnostic export form):

# 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-only file:// 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.example for the full 12-variable contract.

MCP tools

Maximum 23 tools. Disabled tools are absent from tools/list; disabled domain clients are not constructed.

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: occurrence-aware Calendar update/delete with strong If-Match; Contacts opaque book IDs and vCard 3.0 writes; Mail identity (mailbox, UIDVALIDITY, UID), PEEK reads, SMTP exact-recipient policy. set_message_flags fails closed with protocol_error when 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_contact use server-side UID keys (client_uid or alias idempotency_key): a repeat create conflicts if the UID already exists (never silent overwrite). update_event / update_contact optional idempotency_key is process-local only (in-memory cache, 15 minute TTL, cleared on process restart). Same key + same params returns the cached success; same key + different params is conflict. Prefer combining update keys with a strong etag. See docs/error-codes.md.

Configuration

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 fallback. file:// as above.
ICLOUD_MCP_READ_ONLY false Global mutation kill switch.
ICLOUD_MCP_LOG_LEVEL info Stderr level; accepted forms are documented 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 only if not read-only.
ICLOUD_MCP_ENABLE_MAIL false Mail reads; requires Mail address/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 requested: exact addresses, or literal * (boot warning; prefer exact).

Booleans accept only unset, 0, false, 1, or true. Invalid values fail at boot. Config is validated before any 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, and error/4. Unset or unrecognized values use info.

Flags: -version; optional -health 127.0.0.1:port (loopback-only /healthz and /status JSON with domains and rate limits); optional -audit-format=json|text (default json mutation audit on stderr).

Dates

  • Calendar input start/end: RFC3339 with offset, or wall clock without offset in ICLOUD_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 UTC Z on the wire. All-day uses VALUE=DATE.
  • Calendar output: timed events always use RFC3339 with an explicit numeric offset in ICLOUD_MCP_DEFAULT_TZ (never bare Z). All-day dates are YYYY-MM-DD. See calendar_capabilities.outputFormat.
  • Contacts birthdays: write YYYY-MM-DD only.
  • Mail search: 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.

Security (summary)

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

  • Egress fixed: Calendar caldav.icloud.com / p[0-9]{1,3}-caldav.icloud.com:443; Contacts matching contacts hosts; IMAP imap.mail.me.com:993; SMTP smtp.mail.me.com:587 with 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-only file:// reads require mode 0600 or stricter; no os/exec, telemetry, or disk write after boot.
  • Audit: mutations log domain, resourceType, process-local HMAC resourceToken only (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.

Limits (summary)

Tool deadline 25s (DAV HTTP 30s)
Stdio / MCP result 1 MiB frame; 256 KiB result; reflected protocol errors capped
Calendar 366-day search; 400 returned / 2,500 per calendar / 10,000 multi-calendar materialization; 2,000 expansions / 100k steps per series / 250k steps per search; 60 read / 20 write per minute; concurrency 4 / 2
Contacts 100 books; 100 summaries; 2000 cards scanned; 60/20 per minute; concurrency 4
Mail 60 read / 20 mutation / 20 send per minute; semaphores 2 / 1 / 1; no mutation/send retry
Writes No automatic replay of Calendar PUT/DELETE, Contacts writes, IMAP mutations, or SMTP; ambiguous outcomes use outcome_unknown

Lifecycle: eager Calendar discovery at boot; lazy Contacts discovery; fresh IMAP/SMTP session per call. Rates, XML/iCal/vCard/MIME budgets, and retry rules: docs/testing.md, docs/architecture.md.

Supported scope

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

Modern Reminders are not treated as generic CalDAV VTODO. Apple's third-party documentation for this class of access covers Mail, Calendar, and Contacts.

Multi-account: one process holds one iCloud identity. Spawn separate icloud-mcp processes (distinct env, optional distinct -health ports) and let the MCP host multiplex them. See docs/agent-hosts.md.

Dependencies

Go 1.25.12 or newer, one module, exactly 10 direct dependencies. Adding another requires a written justification here.

Dependency Exact version Justification
github.com/emersion/go-webdav v0.7.0 CalDAV primitives; discovery and conditional ops stay hand-rolled
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.15.0 Per-domain rate limiters
github.com/emersion/go-vcard v0.0.0-20260618161152-d854b7e0e2d3 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.24.0 SMTP + STARTTLS
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 SASL PLAIN after STARTTLS

Build and test

make build        # local host binary, VERSION defaults to dev
make test         # go test ./... -race -cover
make lint         # go vet + pinned golangci-lint
make release VERSION=v0.4.0      # packaged linux/arm64, digest-pinned Go 1.25.12 image
make release-all VERSION=v0.4.0  # packaged linux/amd64, linux/arm64, darwin/arm64 (host Go)
make install      # host-compatible build to INSTALL_DIR (default ~/.local/bin)

Release targets reject an unset or dev version. Archives contain the binary, LICENSE, and THIRD_PARTY_NOTICES.md; dist/ also receives a SHA-256 checksum file. GitHub tag releases run make release-all only after CI and gitleaks succeed on that tag, with Go pinned to 1.25.12 (check-latest disabled). Local make release remains the digest-pinned container path for linux/arm64. Cosign keyless signatures are attached to release blobs. -version prefers the release ldflags value and falls back to Go module build information, so go install ...@version reports that module version.

CI: race tests, coverage floors (78% aggregate + package floors including cmd/icloud-mcp and internal/health), fuzz smoke, govulncheck, multi-arch build (plus windows/amd64 smoke), egress/security AST guards, gitleaks, 20 MiB binary budget, public-text policy on tree and new commits. Live iCloud tests use the integration build tag, are opt-in, and never run in CI. See docs/testing.md.

Attribution

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.

Contributing

See CONTRIBUTING.md. Run gofmt, make test, and make lint before opening a pull request.

License

MIT. See LICENSE and third-party notices.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured