# Che Apple Mail Mcp

> Apple Mail MCP server — 47 tools with SQLite-powered millisecond search across 250K+ emails, .emlx parser, batch operations. Swift native.

- **Type:** MCP server
- **Install:** `agentstack add mcp-psychquant-che-apple-mail-mcp`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [PsychQuant](https://agentstack.voostack.com/s/psychquant)
- **Installs:** 0
- **Category:** [Databases](https://agentstack.voostack.com/c/databases)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [PsychQuant](https://github.com/PsychQuant)
- **Source:** https://github.com/PsychQuant/che-apple-mail-mcp

## Install

```sh
agentstack add mcp-psychquant-che-apple-mail-mcp
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# che-apple-mail-mcp

[](https://opensource.org/licenses/MIT)
[](https://www.apple.com/macos/)
[](https://swift.org/)
[](https://modelcontextprotocol.io/)

**The most comprehensive Apple Mail MCP server** - 48 tools with SQLite-powered millisecond search across 250K+ emails.

[English](README.md) | [繁體中文](README_zh-TW.md)

---

## Why che-apple-mail-mcp?

| Feature | Other MCPs | che-apple-mail-mcp |
|---------|------------|-------------------|
| Total Tools | ~20 | **47** |
| Language | Python | **Swift (Native)** |
| Search Speed | Seconds (AppleScript) | **Milliseconds (SQLite)** |
| Search Fields | Subject/Sender | **Subject/Sender/Recipient/Date** |
| Batch Operations | No | **Up to 50 emails per call** |
| Mailbox Management | Basic | Full CRUD |
| Email Colors | No | 7 flag colors + background |
| VIP Management | No | Yes |
| Rule Management | Partial | Full CRUD |
| Signatures | No | Yes |
| Raw Headers/Source | No | Yes |

---

## Quick Start

```bash
# Clone and build
git clone https://github.com/kiki830621/che-apple-mail-mcp.git
cd che-apple-mail-mcp
swift build -c release

# Copy to ~/bin and add to Claude Code
# --scope user    : available across all projects (stored in ~/.claude.json)
# --transport stdio: local binary execution via stdin/stdout
# --              : separator between claude options and the command
mkdir -p ~/bin
cp .build/release/CheAppleMailMCP ~/bin/
claude mcp add --scope user --transport stdio che-apple-mail-mcp -- ~/bin/CheAppleMailMCP
```

> **💡 Tip:** Always install the binary to a local directory like `~/bin/`. Avoid placing it in cloud-synced folders (Dropbox, iCloud, OneDrive) as file sync operations can cause MCP connection timeouts.

Then grant Automation permission in **System Settings > Privacy & Security > Automation**.

---

## Recent Releases

For full details see [CHANGELOG.md](CHANGELOG.md).

### v2.7.2 (2026-05-10) — `attachmentFragment` cluster + fallback parity
- Hardened `attachmentFragment` indent across all 3 callers + removed dead `MailController.attachmentScript` helper that bypassed v2.7.0's race-mitigation delays ([#61](https://github.com/PsychQuant/che-apple-mail-mcp/issues/61), [#62](https://github.com/PsychQuant/che-apple-mail-mcp/issues/62))
- Attachment count cap (50) + env-configurable delays via `CHE_MAIL_ATTACHMENT_DELAY_BETWEEN` / `_TRAILING` ([#63](https://github.com/PsychQuant/che-apple-mail-mcp/issues/63), [#64](https://github.com/PsychQuant/che-apple-mail-mcp/issues/64))
- `get_email_metadata` SQLite path now falls back to AppleScript on error — last read-tool gap closed; all 8 SQLite-first read tools now have parity fallback ([#71](https://github.com/PsychQuant/che-apple-mail-mcp/issues/71))

### v2.7.1 (2026-05-09) — base64 fix + `.partial.emlx` + observability
- **Critical**: RFC822 header/body split was returning a relative array index instead of an absolute `Data` index, causing `html_body` to begin with `"sion: 1.0\n\n"` for some Android Gmail messages — raw base64 leaked into LLM context and triggered AUP false-positives downstream ([#72](https://github.com/PsychQuant/che-apple-mail-mcp/issues/72))
- `save_attachment` now reads from `Attachments///` cache when `.partial.emlx` body is empty — no more silent 0-byte writes for IMAP messages with stripped binaries ([#66](https://github.com/PsychQuant/che-apple-mail-mcp/issues/66))
- SQLite fast-path failures now log to stderr (`SQLite ... fast path failed for rowId=...; falling through to AppleScript`) ([#69](https://github.com/PsychQuant/che-apple-mail-mcp/issues/69))

### v2.7.0 (2026-05-04) — Mail.app race mitigation
- Multi-attachment AppleScript paced with 0.3s between + 0.5s trailing delays to mitigate Mail.app silently dropping attachments under fast IPC ([#60](https://github.com/PsychQuant/che-apple-mail-mcp/issues/60))

### v2.6.0 (2026-05-03) — Security & validation hardening (8 PRs, 16 issues)
- `forward_email` plain mode now embeds RFC 3676 `> ` quoted original (parity with `reply_email`'s #43 fix) ([#44](https://github.com/PsychQuant/che-apple-mail-mcp/issues/44))
- Hard-fail on tool param type mismatch — `bool` / `[String]` no longer silently coerced ([#35](https://github.com/PsychQuant/che-apple-mail-mcp/issues/35))
- Recipient email validation rejects header injection (control chars, missing/multiple `@`) ([#41](https://github.com/PsychQuant/che-apple-mail-mcp/issues/41))
- `cc_additional` deduplicates case-insensitively ([#34](https://github.com/PsychQuant/che-apple-mail-mcp/issues/34))
- Attachment path deny-list (`~/.ssh`, Keychains, TCC db, browser cookies) + symlink-resolved + new `MAIL_MCP_ATTACHMENT_ROOTS` env allow-list ([#38](https://github.com/PsychQuant/che-apple-mail-mcp/issues/38))
- All 17 id-taking tools hard-validate `id` as Int at handler boundary — defeats AppleScript predicate injection ([#50](https://github.com/PsychQuant/che-apple-mail-mcp/issues/50))
- Gated integration tests for `reply_email` runtime ([#37](https://github.com/PsychQuant/che-apple-mail-mcp/issues/37), [#45](https://github.com/PsychQuant/che-apple-mail-mcp/issues/45)) + smoke matrix templates ([#46](https://github.com/PsychQuant/che-apple-mail-mcp/issues/46), [#47](https://github.com/PsychQuant/che-apple-mail-mcp/issues/47))

### v2.5.0 (2026-04-17) — Composing `format` parameter
- All 4 composing tools (`compose_email` / `create_draft` / `reply_email` / `forward_email`) gain `format: "plain" | "markdown" | "html"` param (closes [#14](https://github.com/PsychQuant/che-apple-mail-mcp/issues/14), [#15](https://github.com/PsychQuant/che-apple-mail-mcp/issues/15))
- New `message-composition` capability spec

---

## All 48 Tools

Accounts (2)

| Tool | Description |
|------|-------------|
| `list_accounts` | List all mail accounts |
| `get_account_info` | Get account details |

Mailboxes (4)

| Tool | Description |
|------|-------------|
| `list_mailboxes` | List all mailboxes (folders) |
| `create_mailbox` | Create a new mailbox |
| `delete_mailbox` | Delete a mailbox |
| `get_special_mailboxes` | Get special mailbox names (inbox, drafts, sent, trash, junk, outbox) |

Emails (7)

| Tool | Description |
|------|-------------|
| `list_emails` | List emails in a mailbox |
| `get_email` | Get full email content |
| `search_emails` | Search by subject/content |
| `get_unread_count` | Get unread count |
| `get_email_headers` | Get all email headers |
| `get_email_source` | Get raw email source |
| `get_email_metadata` | Get metadata (forwarded, replied, size) |

Actions (8)

| Tool | Description |
|------|-------------|
| `mark_read` | Mark as read/unread |
| `flag_email` | Flag/unflag email |
| `set_flag_color` | Set flag color (7 colors) |
| `set_background_color` | Set email background color |
| `mark_as_junk` | Mark as junk/not junk |
| `move_email` | Move to another mailbox |
| `copy_email` | Copy to another mailbox |
| `delete_email` | Delete email (to trash) |

Compose (5)

| Tool | Description |
|------|-------------|
| `compose_email` | Send new email (supports cc/bcc/attachments; `format`: plain/markdown/html; optional `from_address` for multi-account sender selection — see [#131](https://github.com/PsychQuant/che-apple-mail-mcp/issues/131)). Plain-text bodies send via the wrapper-free native path when Accessibility is granted — see [#175](https://github.com/PsychQuant/che-apple-mail-mcp/issues/175) / `check_accessibility` |
| `reply_email` | Reply to email. Optional: `cc_additional`, `attachments`, `save_as_draft`, `format` (since v2.4.0). Plain mode embeds RFC 3676 `> ` quoted original (since v2.5.0 / #43) |
| `forward_email` | Forward email. Optional `body` + `format`. Plain mode embeds RFC 3676 `> ` quoted original (since v2.5.0+ / #44) |
| `redirect_email` | Redirect email (keeps original sender) |
| `open_mailto` | Open mailto URL |

#### Reply-as-draft example (v2.4.0+)

Reply to a thread, add extra CC, attach files, and save as a draft for human review before sending:

```
reply_email(
    id="",
    mailbox="INBOX",
    account_name="iCloud",
    body="Reply text",
    cc_additional=["x@y.com"],
    attachments=["/path/to/file.pdf"],
    save_as_draft=true
)
```

Drafts (2)

| Tool | Description |
|------|-------------|
| `list_drafts` | List draft emails |
| `create_draft` | Create a draft (supports attachments; optional `from_address` for multi-account sender selection — see [#131](https://github.com/PsychQuant/che-apple-mail-mcp/issues/131)). Plain-text bodies use the wrapper-free native path when Accessibility is granted — see [#175](https://github.com/PsychQuant/che-apple-mail-mcp/issues/175) / `check_accessibility` |

Attachments (2)

| Tool | Description |
|------|-------------|
| `list_attachments` | List email attachments |
| `save_attachment` | Save attachment to disk |

VIP (1)

| Tool | Description |
|------|-------------|
| `list_vip_senders` | List VIP senders |

Rules (5)

| Tool | Description |
|------|-------------|
| `list_rules` | List mail rules |
| `get_rule_details` | Get rule details |
| `create_rule` | Create a new rule |
| `delete_rule` | Delete a rule |
| `enable_rule` | Enable/disable a rule |

Signatures (2)

| Tool | Description |
|------|-------------|
| `list_signatures` | List email signatures |
| `get_signature` | Get signature content |

SMTP (1)

| Tool | Description |
|------|-------------|
| `list_smtp_servers` | List SMTP servers |

Sync (2)

| Tool | Description |
|------|-------------|
| `check_for_new_mail` | Check for new mail |
| `synchronize_account` | Sync IMAP account |

Utilities (4)

| Tool | Description |
|------|-------------|
| `extract_name_from_address` | Extract name from email address |
| `extract_address` | Extract email from full address |
| `get_mail_app_info` | Get Mail.app info |
| `import_mailbox` | Import mailbox from file |

### Response shape: `search_emails` / `list_emails`

Both tools return an **envelope object** `{ results, returned, limit, truncated }` — **not** a bare array (changed in [v2.14.0](CHANGELOG.md), [#204](https://github.com/PsychQuant/che-apple-mail-mcp/issues/204)). Read the matches from `.results`:

| Field | Meaning |
|-------|---------|
| `results` | Array of result objects (per-object fields unchanged from the pre-envelope shape). `search_emails` objects carry `id`, `subject`, `sender`, `date_received`, `account_name`, `mailbox`, `to`, plus `account_id` when the account UUID is resolvable. `list_emails` objects carry `id`, `subject`, `sender`. |
| `returned` | Number of objects in `results` |
| `limit` | Effective `limit` applied to the query |
| `truncated` | `true` when more results are available than were returned — **raise `limit` or narrow the query** to retrieve the rest (definitive on the SQLite fast path; a best-effort heuristic on the AppleScript fallback — see below) |

`truncated` is **definitive** on the SQLite fast path (it fetches `limit + 1` internally); on the AppleScript fallback it is a best-effort `returned == limit` heuristic. Any "enumerate → batch process" consumer should check `truncated` before assuming it has the full set.

---

## Installation

### Requirements

- macOS 13.0+
- Xcode Command Line Tools
- Apple Mail with at least one account configured

### Step 1: Build

```bash
git clone https://github.com/kiki830621/che-apple-mail-mcp.git
cd che-apple-mail-mcp
swift build -c release
```

### Step 2: Configure

#### For Claude Desktop

Edit `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "che-apple-mail-mcp": {
      "command": "/full/path/to/che-apple-mail-mcp/.build/release/CheAppleMailMCP"
    }
  }
}
```

#### For Claude Code (CLI)

```bash
# Copy to ~/bin and register (user scope = available in all projects)
mkdir -p ~/bin
cp .build/release/CheAppleMailMCP ~/bin/
claude mcp add --scope user --transport stdio che-apple-mail-mcp -- ~/bin/CheAppleMailMCP
```

### Step 3: Grant Permissions

**Automation** (control Mail.app):

```bash
open "x-apple.systempreferences:com.apple.preference.security?Privacy_Automation"
```

1. Find **CheAppleMailMCP** and enable permission for **Mail.app**
2. If using Claude Code, also add **Terminal** or **iTerm**

**Full Disk Access** (the SQLite fast path + `export_emails_markdown` read `~/Library/Mail`):

```bash
open "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles"
```

macOS grants Full Disk Access to the **responsible process** — the app that *launched* this server — not to the binary itself. For an MCP server run by **Claude Code inside a terminal**, that responsible process is the **terminal** (Ghostty / Terminal / iTerm), so add **your terminal app** here and enable it. One grant on the terminal covers every MCP server it launches. (If you instead run the binary directly, or use the **Claude Desktop** bundle, add that binary — `~/bin/CheAppleMailMCP` — since it is then its own responsible process.) The FDA-denied error message names these candidates for you — it does **not** auto-resolve the one exact app, because macOS exposes no reliable in-process API for that (#214). Without Full Disk Access, read tools silently fall back to the slower AppleScript path and SQLite-only features (`projection`, `export_emails_markdown`) fail. For the direct-launch path, a Developer ID-signed build makes the grant survive version bumps — see [Signing & Notarization](#signing--notarization).

**Guided setup** (#213) — instead of the manual steps above, the binary ships setup helpers:

- **`CheAppleMailMCP --setup`** opens a small window with **live** Full Disk Access status (re-checked on a timer, flips to "Ready ✅" the moment you grant) plus an **on-demand** Automation check, and "Open Full Disk Access settings" / "Copy binary path" buttons.
- **`CheAppleMailMCP --check-fda`** prints the status headlessly (and opens the pane when access is **denied**) — handy from a terminal or script.
- The **`check_fda` MCP tool** reports the same status to Claude on demand (call it when a SQLite-only feature errors).

None of these can remove the single manual toggle (Apple puts FDA in the manual-only bucket alongside Accessibility / Screen Recording), but they make "what do I do?" obvious and give live feedback the instant you flip it on.

**Accessibility (clean compose, #175)** — a *separate, optional* grant from Full Disk Access. Mail.app wraps any AppleScript-injected outgoing-message body in ``, which some mobile clients render as a quotation of your own text. To avoid this, `compose_email` / `create_draft` route plain-text mail through Mail's **native compose pipeline** (a `mailto:` hand-off), then drive save/send and attachments with keyboard shortcuts — which needs **Accessibility** (System Settings → Privacy & Security → Accessibility), granted to the same responsible process as FDA (your terminal / Claude Desktop). The **`check_accessibility` MCP tool** and the `--setup` window's **Accessibility** row report the status. Without it, compose still works but falls back to the wrapped legacy path (with a stderr warning). markdown/html bodies and a custom `from_address` always use the legacy path. To force the legacy path even when Accessibility is granted, set `CHE_MAIL_DISABLE_MAILTO_COMPOSE=1`.

### Step 4: Restart Claude

```bash
# For Claude Desktop
osascript -e 'quit app "Claude"' && sleep 2 && open -a "Claude"

# For Claude Code - start a new session
claude
```

---

## Usage Examples

### Natural Language (Claude Desktop)

```
"List all my mail accounts"
"Show unread emails in Gmail inbox"
"Search for emails about 'quarterly report'"
"Send an email to john@example.com about the meeting"
"Flag important emails in red"
"Create a rule to move newsletters to a folder"
```

### Direct Tool Calls (Claude Code)

```
"Use list_accounts to show my accounts"
"Use search_emails to find emails containing 'invoice'"
"Use set_flag_color to mark email ID 12345 as blue"
"Use check_for_new_mail to refresh"
```

---

## Flag & Background Colors

### Flag Colors

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [PsychQuant](https://github.com/PsychQuant)
- **Source:** [PsychQuant/che-apple-mail-mcp](https://github.com/PsychQuant/che-apple-mail-mcp)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-psychquant-che-apple-mail-mcp
- Seller: https://agentstack.voostack.com/s/psychquant
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
