{
  "markdown": "# icloud-mcp\n\n<!-- mcp-name: io.github.eodozzy/icloud-mcp -->\n\n[![PyPI](https://img.shields.io/pypi/v/icloud-mcp.svg)](https://pypi.org/project/icloud-mcp/)\n[![Python](https://img.shields.io/pypi/pyversions/icloud-mcp.svg)](https://pypi.org/project/icloud-mcp/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\nA [Model Context Protocol](https://modelcontextprotocol.io) server that gives AI\nassistants **read-only** access to your iCloud mail and calendar over IMAP and\nCalDAV. Credentials live in your OS keyring (macOS Keychain by default) — never\nin a config file or the repo.\n\nDefaults target iCloud, but every endpoint is overridable, so it works against\nany standard IMAP/CalDAV provider.\n\n## Status\n\n**Read-only by default; writes are strictly opt-in.** Mail, calendar, and contacts\ncan be listed, searched, and read with no ability to change anything: the IMAP\nconnection is opened read-only and bodies are fetched with `BODY.PEEK[]`, so\nmessages are never even marked as read.\n\nMutating tools span mail (`send_mail`, `reply_mail`, `forward_mail`), calendar\n(`create_event`, `update_event`, `delete_event`), and contacts (`create_contact`,\n`update_contact`, `delete_contact`) — but all are **disabled unless you set\n`ICLOUD_ENABLE_WRITES=1`**, and even then every write requires an interactive\nconfirmation (MCP elicitation) before it runs. See [Writes (opt-in)](#writes-opt-in).\n\n### Writes (opt-in)\n\nMutations are gated two ways:\n\n1. **Operator switch** — write tools refuse with an explanatory error unless\n   `ICLOUD_ENABLE_WRITES=1` is set in the server's environment.\n2. **Per-action confirmation** — when enabled, each write tool calls back to the client\n   to confirm the exact action before executing. Destructive operations (delete event /\n   contact) are gated identically — no extra force flag, just the same confirmation.\n\niCloud issues a single app-specific password covering IMAP, SMTP, CalDAV, and CardDAV\n— there is no scoped \"write-only\" credential — so write-safety is *structural*: mail is\nsent over a separate, fresh SMTP connection (the read path's IMAP client has no send\ncapability), and the opt-in flag + confirmation guard every mutation.\n\n## Install\n\n```bash\npip install icloud-mcp\n```\n\nOr run it without installing — handy for MCP clients that launch the server\non demand:\n\n```bash\nuvx icloud-mcp\n```\n\nTo install the latest unreleased code straight from GitHub:\n\n```bash\npip install git+https://github.com/eodozzy/icloud-mcp\n```\n\nOr for local development:\n\n```bash\ngit clone git@github.com:eodozzy/icloud-mcp\ncd icloud-mcp\npython3 -m venv .venv && .venv/bin/pip install -e \".[test]\"\n```\n\nRequires Python 3.11+.\n\n## Credentials\n\nGenerate an [app-specific password](https://appleid.apple.com) for your Apple ID\n(never use your main password), then store it in your keyring:\n\n```bash\n# macOS\nsecurity add-generic-password -a \"you@icloud.com\" -s \"icloud-mcp\" -w \"xxxx-xxxx-xxxx-xxxx\"\n```\n\nOn other platforms, the cross-platform [`keyring`](https://pypi.org/project/keyring/)\nlibrary is used — `keyring set icloud-mcp you@icloud.com` also works anywhere.\n\nThe username comes from the `ICLOUD_USERNAME` environment variable. Resolution\norder for the password:\n\n1. OS keyring (`ICLOUD_KEYRING_SERVICE`, default `icloud-mcp`, + username)\n2. `ICLOUD_APP_PASSWORD` environment variable (fallback)\n\nTo **reuse an existing keyring entry** (e.g. one named `my-icloud`), set\n`ICLOUD_KEYRING_SERVICE=my-icloud`.\n\n## Configuration\n\nSet `ICLOUD_USERNAME` (required). All else is optional — see\n[`.env.example`](.env.example) for the full list of endpoint/timezone overrides.\n\n## Register with an MCP client\n\nThe server speaks MCP over **stdio**: a client launches the `icloud-mcp` command\nas a subprocess and talks to it over stdin/stdout. \"Installing\" it into a client\njust means telling that client which command to run and which env vars to pass —\nthe password itself stays in the keyring, never in the client config.\n\nIf `icloud-mcp` isn't on your `PATH` (e.g. you installed into a virtualenv),\nuse the absolute path to the launcher, e.g. `/path/to/repo/.venv/bin/icloud-mcp`.\n\n### Claude Code\n\n```bash\nclaude mcp add icloud \\\n  --env ICLOUD_USERNAME=you@icloud.com \\\n  --env ICLOUD_KEYRING_SERVICE=icloud-mcp \\\n  -- icloud-mcp\n```\n\nThen `claude mcp list` to confirm. Add `-s user` to make it available across all\nprojects rather than just the current one.\n\n### Claude Desktop\n\nAdd the server to Claude Desktop's config file. On macOS this is:\n\n```\n~/Library/Application Support/Claude/claude_desktop_config.json\n```\n\n(On Windows: `%APPDATA%\\Claude\\claude_desktop_config.json`.)\n\nThe easiest way to open it is **Settings → Developer → Edit Config**. Add an\n`mcpServers` key alongside whatever is already in the file — do **not** paste a\nsecond top-level `{ ... }` object, or the file becomes invalid JSON:\n\n```json\n{\n  \"mcpServers\": {\n    \"icloud\": {\n      \"command\": \"icloud-mcp\",\n      \"env\": {\n        \"ICLOUD_USERNAME\": \"you@icloud.com\",\n        \"ICLOUD_KEYRING_SERVICE\": \"icloud-mcp\"\n      }\n    }\n  }\n}\n```\n\nIf the file already contains other keys, merge `mcpServers` in as a sibling\n(remember the comma between keys):\n\n```json\n{\n  \"someExistingSetting\": \"...\",\n  \"mcpServers\": { \"icloud\": { \"...\": \"...\" } }\n}\n```\n\nThen **fully quit** Claude Desktop (⌘Q, not just closing the window) and reopen\nit. The server shows up as a *Local MCP server* and its tools become available.\n\nNotes:\n\n- **First call prompts for Keychain access.** macOS asks whether `icloud-mcp`\n  may read the keyring item; click **Always Allow** to avoid repeat prompts.\n- **Claude Desktop may rewrite this file** when it saves its own preferences,\n  dropping hand-added keys it doesn't recognize. If the server disappears after\n  you change other settings, just re-add the `mcpServers` block.\n\n## Tools & resources\n\n**Tools** (model-invoked):\n\n| Tool | Description |\n|---|---|\n| `list_mail` | List a folder (default INBOX), newest first, with optional `since_date` |\n| `search_mail` | Full-text search the inbox (or a named folder) |\n| `list_folders` | List the available IMAP mailbox folder names |\n| `get_message` | Fetch one message by UID, with full body |\n| `list_events` | Calendar events in a date window |\n| `search_events` | Events whose title matches text, in a window |\n| `list_calendars` | Names of all calendars |\n| `list_contacts` | List address-book contacts (name, emails, phones, org) |\n| `search_contacts` | Find contacts matching text across name, org, and emails |\n| `send_mail` ⚠️ | Send a plain-text email |\n| `reply_mail` ⚠️ | Reply to a message by UID (quotes original; `reply_all` optional) |\n| `forward_mail` ⚠️ | Forward a message by UID to a new recipient |\n| `create_event` ⚠️ | Create a calendar event |\n| `update_event` ⚠️ | Edit an event by UID (only the fields you pass) |\n| `delete_event` ⚠️ | Delete an event by UID |\n| `create_contact` ⚠️ | Create a new contact |\n| `update_contact` ⚠️ | Edit a contact by UID (merges into the existing vCard) |\n| `delete_contact` ⚠️ | Delete a contact by UID |\n\n⚠️ Write tools require `ICLOUD_ENABLE_WRITES=1` and confirm each action — see\n[Writes (opt-in)](#writes-opt-in).\n\n**Resources** (passive context):\n\n| URI | Description |\n|---|---|\n| `icloud://mail/inbox/recent` | Most recent inbox messages |\n| `icloud://calendar/today` | Today + tomorrow's events |\n\n## Behavior notes\n\n- **Empty/missing headers:** messages whose `Subject` header is absent *or*\n  present-but-blank render as `(no subject)`; a blank/absent `From` renders as\n  `(unknown)`. (Some mail has an empty subject line rather than no subject line\n  at all — both are normalized.)\n- **Snippets** prefer the `text/plain` part; for HTML-only mail, tags are\n  stripped (`<style>`/`<script>`/`<head>` content discarded) so you still get a\n  readable preview.\n- **Double-wrapped bodies:** some senders (e.g. USPS Informed Delivery) embed a\n  redundant MIME header block at the top of the decoded body. A leading\n  `Content-*`/`MIME-Version` header block is stripped so those headers don't leak\n  into the snippet or body.\n- **`since_date` is date-granular** and evaluated in the IMAP server's timezone\n  (UTC for iCloud), so a `since_date` of today can include late-yesterday messages\n  in your local time.\n\n## Development\n\n```bash\n.venv/bin/pytest            # run the fixture-based test suite (no live account)\n.venv/bin/mcp dev -m icloud_mcp.server   # interactive MCP Inspector\n```\n\n## Security notes\n\n- The app-specific password lives in the OS keyring only — never in `.env`,\n  never committed.\n- Read access (mail, calendar, contacts) cannot mutate anything; the IMAP session\n  is opened read-only.\n- Writes are **off by default**. They require `ICLOUD_ENABLE_WRITES=1` *and* an\n  interactive confirmation per action, and mail is sent over a separate SMTP\n  connection isolated from the read path.\n- An app-specific password can be revoked at any time from appleid.apple.com\n  without affecting your Apple ID.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n\n---\n\nNot affiliated with, endorsed by, or sponsored by Apple Inc. iCloud is a\ntrademark of Apple Inc.\n",
  "bytes": 9129,
  "sha": "931520929c5bc3a580494a3cd7b9ab39b3439cf4e00a6827164e1a536d0e9efa",
  "repo_slug": "eodozzy/icloud-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_eodozzy_icloud_mcp_78980d7a/readme"
}