Metadata-Version: 2.4
Name: apple-mailbox-mcp
Version: 1.5.1
Summary: Fast local MCP server for Apple Mail: search, read, triage, verified send. Works with Claude, Cursor, VS Code, or any MCP client.
License-Expression: MIT
Project-URL: Homepage, https://github.com/parasxos/email-mcp
Project-URL: Documentation, https://github.com/parasxos/email-mcp#readme
Project-URL: Issues, https://github.com/parasxos/email-mcp/issues
Project-URL: Repository, https://github.com/parasxos/email-mcp
Keywords: apple-mail,email,mcp,model-context-protocol
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: MacOS X
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Communications :: Email
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: mcp<3,>=1.27.1
Requires-Dist: pydantic<3,>=2
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: jsonschema>=4; extra == "dev"
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: setuptools>=77; extra == "dev"
Requires-Dist: twine>=6; extra == "dev"
Dynamic: license-file

<!-- mcp-name: io.github.parasxos/apple-mail-mcp -->
<div align="center">

# ✉️ apple-mail-mcp

### Your Apple Mail, fully agent-operable.

**Runs locally on your Mac.** It opens the Mail store read-only, returns only
what a client asks for, and sends only when the client explicitly calls the
send tool. No third-party relay, no cloud copy of your mail.

Use **Claude, Codex, Cursor, VS Code, or any local MCP client** to search,
read, triage, and send email through Apple Mail — your mailbox becomes
something you can
**ask, search and delegate to** — find anything in seconds, file hundreds of
messages through a reviewed plan, send polished mail as the right identity,
and let Exchange deliver scheduled messages even while your Mac is asleep.

![ci](https://github.com/parasxos/apple-mail-mcp/actions/workflows/ci.yml/badge.svg)
![tools](https://img.shields.io/badge/MCP%20tools-21-brightgreen)
![platform](https://img.shields.io/badge/platform-macOS%20%2B%20Mail.app-orange)
![python](https://img.shields.io/badge/Python-3.11%E2%80%933.14-blue)
![mcp](https://img.shields.io/badge/MCP%20SDK-1.x%20%2B%202.x-purple)
![contract](https://img.shields.io/badge/wire%20contract-frozen%20v1-blue)

</div>

---

![demo: uvx install, status, and a Claude search over a 298k-message store](docs/assets/demo.gif)

## ✨ What you can do

🔍 **Ask your mailbox questions.** *"What did Stefan send me about the memo
last week?"* Search runs at database speed — sender, mailbox, dates, unread,
attachments — and reconstructs whole conversations.

🕳️ **Find what Mail itself can't.** Mail's built-in search only skims the
first line of most messages. apple-mail-mcp indexes every message **body** on your
Mac — and for Exchange accounts it even fetches the bodies Mail never
downloaded, straight from your own mailbox on the server. Queries that
returned nothing return twenty.

🎭 **Send as the right you.** Work mail through the work lane, personal
through Gmail — one parameter picks the identity. Every message is composed
from scratch as clean, standards-correct email that renders everywhere,
*including Outlook* (the AppleScript compose path that arrives blank in
Outlook is the reason this project exists).

⏰ **Schedule like "Send Later", but scriptable.** A scheduled message is
frozen in full — attachments, identity, exact text. Exchange can execute it
server-side at the requested time, lid closed; other providers use a local
background sender and deliver on its next pass (or just after the Mac wakes).

🗂️ **Triage at scale, without fear.** *"File these 40 newsletters"* becomes a
reviewable plan: nothing moves until it is approved, every message is
re-checked before it is touched, and the result is verified against Mail's
own records afterward. Delete means Mail's Trash — nothing is ever erased.

📝 **Draft where your drafts live.** Compose into your real Exchange Drafts
folder, ready to open in Outlook or OWA — created, never auto-sent.

## ⚡ Why it's different

Every other Apple-Mail MCP drives AppleScript for both finding and acting.
This one doesn't — and it shows:

| Operation | AppleScript whose-clause | apple-mailbox-mcp |
|---|---|---|
| 🔍 Search 300k messages | seconds-to-timeout | **milliseconds** |
| 🎯 Address one message in a 71k mailbox | **7–10 s** ([measured](docs/benchmarks.md)) | **< 0.1 ms** |
| ✉️ Send mail | body renders **blank in Outlook** | renders everywhere, plain+HTML |
| ⏰ Schedule mail | — | server-side on Exchange; reliable local queue everywhere else |
| 🗂️ Bulk triage | one call per message, fire-and-forget | one reviewed plan, one apply, **verified** |

Every number above was measured on a live ~300,000-message store; the
script and full method are in [docs/benchmarks.md](docs/benchmarks.md).

## 🛡️ Built to be trusted

- ✅ **Plan → review → apply → verify.** Bulk actions are frozen into a plan
  you can read before anything happens; the outcome is confirmed against
  Mail's own store afterward — never assumed.
- 🗑️ **Nothing is ever erased.** "Delete" files into Mail's Trash, and
  destructive plans have their own separate, capped door.
- 👓 **Read-only mail mode.** Set `EMAIL_MCP_READ_ONLY=1` and only the 11
  non-mutating mail tools exist in the session. Search may still maintain its
  local body index, and attachment retrieval writes the requested file to the
  configured temporary directory.
- 💾 **A crash-safe scheduled queue.** Manifest updates are flushed and
  atomically replaced, so an interrupted rewrite keeps the last valid record.
  If a file is damaged independently, diagnostics name it instead of claiming
  the queue is empty, while healthy scheduled messages keep moving.
- 🧾 **A local, best-effort activity ledger.** Sends, schedules,
  cancellations and triage runs are recorded without making an unwritable log
  block mail. For reconciliation, the message itself, its Message-ID and its
  scheduled record remain authoritative.
- 🔒 **No third-party mail relay.** Mail content stays local except for mail
  you send and optional access to your own provider for Exchange/IMAP body
  backfill, drafts and server-side scheduling. SMTP passwords stay in the
  macOS Keychain or 1Password; Microsoft OAuth tokens live in a private 0600
  cache under `~/.email-mcp/graph/`.
- 📜 **A written contract.** Since v1.0 every tool's shapes, error codes and
  caps evolve additively, held in place by 800+ automated tests.
- 🤝 **Clear to every MCP client.** All 21 tools identify what they do, explain
  every input, and declare whether they read, change or can remove data. Newer
  clients receive structured results; older clients keep the same JSON text.
  Both the maintained MCP 1.x line and current MCP 2.x are tested.
- 🧱 **Built to evolve without breaking your workflow.** Email rules are
  isolated from MCP, Mail.app, Exchange, delivery, and local storage. Provider
  or SDK changes stay at the edge while the 21-tool contract remains stable.
  The dependency rules are enforced in CI and explained in the
  [architecture guide](https://github.com/parasxos/apple-mail-mcp/blob/main/docs/architecture.md).
- 📦 **Releases you can verify.** Every tagged release is built and installed
  in a clean environment before publishing. GitHub includes the wheel, source
  archive, SHA-256 checksums and signed build provenance—not just source code.
- 🩺 **Self-diagnosing.** `apple-mail-mcp status` gives one readable readiness,
  scheduling and recovery screen. `apple-mail-mcp doctor` provides the complete
  diagnostic detail and an exact fix for anything red.

## 🚀 Quick start

1. **Grant Full Disk Access** to your terminal app
   (System Settings → Privacy & Security → Full Disk Access), then **quit and
   reopen the terminal**. This is Apple's one manual toggle — there is no
   pop-up for it.

2. **Install and set up:**

   ```bash
   uvx apple-mailbox-mcp setup     # or: pipx install apple-mailbox-mcp
   ```

3. **Register with your client** — one line for Claude Code:

   ```bash
   claude mcp add --transport stdio --scope user apple-mail -- uvx apple-mailbox-mcp
   ```

   or the same JSON block for Claude Desktop / Cursor / VS Code
   (`claude_desktop_config.json` / `.cursor/mcp.json` / `.vscode/mcp.json`):

   ```json
   {
     "mcpServers": {
       "apple-mail": { "command": "uvx", "args": ["apple-mailbox-mcp"] }
     }
   }
   ```

4. **Verify:** `uvx apple-mailbox-mcp status` prints one readiness screen —
   or just ask your client to run the `doctor` tool; every red line comes
   with its exact fix. The first body-index build on a large mailbox runs in
   the background and can take a few minutes; search works immediately and
   completes as the index fills.

Before running `setup`, grant your terminal app **Full Disk Access**
(System Settings → Privacy & Security → Full Disk Access) — that is how
reading stays fast and local. There is no pop-up for this one; it is Apple's
one manual toggle, and `setup` walks you to the exact pane if it finds it
missing.

`setup` asks everything in plain words (bare Enter accepts the recommended
answer), offers a sending identity, builds the body-search index, verifies
the nightly refresh actually runs, and ends by printing the one block you
paste into your MCP client:

```json
{
  "mcpServers": {
    "apple-mail": { "command": "apple-mail-mcp" }
  }
}
```

Setup ends with a clear **ready** verdict or numbered recovery steps. Grant
**Automation → Mail** when triage first asks for it. Check the installation,
the next scheduled message, and failed scheduled sends anytime with
`apple-mail-mcp status`; use `apple-mail-mcp doctor` for the full technical detail.

> 💡 *New to the terminal?* Three things that look wrong and aren't:
> `brew install pipx` wants a typed `y` (Enter alone is rejected);
> `pipx ensurepath` may print a ⚠️ — the "pipx is ready to go!" line after it
> is the verdict; and after `ensurepath`, close and reopen the terminal once
> so `apple-mail-mcp` is found.

## 🔧 Troubleshooting

| Symptom | Fix |
|---|---|
| Client says the server failed to start, but `uvx apple-mailbox-mcp` works in your terminal | GUI apps don't inherit your shell PATH. Use the absolute path: `"command": "/opt/homebrew/bin/uvx"` (find yours with `which uvx`). |
| `database is locked` or empty results | Full Disk Access is missing for the app that *launches* the server (the client, not the terminal). Grant it, then fully quit and reopen that app. |
| Search finds recent mail but not bodies of old mail | The body index is still building — first build on a 100k+ mailbox takes minutes. `status` shows progress. |
| Send fails with `transport_unavailable` | Run `doctor`: it names the failing lane (Keychain item missing, SSH socket cold, SMTP host unreachable) and prints the exact fix. |
| Triage does nothing the first time | Grant **Automation → Mail** when macOS asks; the prompt appears on first use, not at install. |

## 🧰 The 21 tools

| Group | Tools |
|---|---|
| 🔍 **Read** (8) | `search_emails` (full-body search) · `get_email` · `get_emails_batch` · `get_thread` · `list_mailboxes` · `list_recent` · `get_attachment` · `refresh_mail` |
| ✉️ **Send** (6) | `send_email` · `reply_email` (threaded, quoted) · `create_draft` · `schedule_email` · `list_scheduled` · `cancel_scheduled` |
| 🗂️ **Triage** (5) | `triage_plan` · `triage_plan_delete` · `triage_apply` · `mailbox_create` · `mailbox_delete` |
| 🩺 **Meta** (2) | `doctor` (full diagnostics with fix-it strings) · `audit` (the local ledger) |

Attachments both ways, size-budgeted. Replies thread correctly in every
client. Scheduling survives sleep — a message due while the lid was closed
goes out on the first tick after wake, or exactly on time via Exchange.

## 🎭 Your addresses, your lanes

The From: address decides how mail travels. `~/.email-mcp/identities.toml`:

```toml
default = "work"

[work]                    # sent through a host you already trust, over SSH
from_addr = "you@example.org"
driver    = "ssh_sendmail"
host      = "bastion.example.org"   # any login host you already SSH to

[gmail]                   # classic SMTP — the app password stays in 1Password
from_addr = "you@gmail.com"
driver    = "smtp"
host      = "smtp.gmail.com"
op        = "op://Personal/gmail app password/password"
```

Exchange identities can add one sign-in to unlock the extras: drafts filed in
your real Drafts folder, and scheduled sends executed by the server itself —
lid closed, Mac asleep. `setup` offers it in one plain question. Reading
needs no sending configuration at all.

---

<div align="center">

**v1.3** · 21 tools · 800+ tests · additive wire contract since v1.0
Live-calibrated end-to-end on a 305k-message store.

Built for one Mac — and for anyone else whose Mac runs Mail.app.

</div>
