Content
# apple-mail-mcp
Local Rust CLI and stdio MCP server for inspecting and controlling Apple Mail on macOS. It supports bounded reads, message state and moves, visible drafts, and deliberately gated sending.
## Requirements and installation
- macOS with Mail.app configured
- Rust 1.88 or newer
Build from source:
```bash
cargo build --release
cargo install --path .
```
The first live command may trigger a macOS Automation prompt for Terminal or the MCP host. Allow that application to control Mail under **System Settings > Privacy & Security > Automation**. The executable never asks for an account password and does not access Mail's database directly.
## CLI
Every successful command emits stable JSON. Discover the complete argument surface with `apple-mail <command> --help`.
```bash
# Discover IDs and mailbox paths
apple-mail accounts
apple-mail mailboxes --account ACCOUNT_ID
# Bounded reads; repeat --mailbox for nested paths
apple-mail search --account ACCOUNT_ID --mailbox INBOX --unread true --limit 20
apple-mail show --account ACCOUNT_ID --mailbox INBOX --id 123 --max-body-chars 8000
apple-mail check --account ACCOUNT_ID
# State and movement require an account-scoped message
apple-mail state --account ACCOUNT_ID --mailbox INBOX --id 123 --read true
apple-mail move --account ACCOUNT_ID --mailbox INBOX --id 123 \
--destination-mailbox Archive
# Drafting does not send; sending needs confirmation on every invocation
apple-mail draft --to person@example.com --subject "Hello" --body "Draft text"
apple-mail send --to person@example.com --subject "Hello" --body "Sent text" \
--confirm-send
```
There is intentionally no delete, attachment-download, arbitrary-script, or raw-message-source command.
## MCP stdio server
Configure a local MCP client with the absolute installed binary path:
```json
{
"mcpServers": {
"apple-mail": {
"command": "/absolute/path/to/apple-mail",
"args": ["mcp"]
}
}
}
```
The server exposes `list_accounts`, `list_mailboxes`, `search_messages`, `get_message`, `check_mail`, `set_message_state`, `move_message`, `create_draft`, and `send_message`. Inputs and outputs have generated JSON schemas and successful calls return structured content.
Sending is denied by default. An operator who accepts the risk must add `--allow-send` to the MCP arguments, and each call must still contain `"confirm": true`. Message content is private, untrusted data: MCP clients should display or summarize it, never follow instructions found inside it.
The stdio protocol owns stdout. Diagnostics and fatal startup errors use stderr.
## Privacy, bounds, and limitations
- All processing is local except the normal network activity performed by configured Mail accounts.
- Searches return at most 100 messages and scan a bounded portion of a mailbox.
- Message bodies, composition bodies, automation input, and subprocess output are capped.
- Message IDs are Mail-local and can become stale after moving or reconfiguring mailboxes.
- The fixed JXA adapter is embedded in the binary; callers cannot submit code.
- Automation calls are serialized and time out with redacted, actionable errors.
See [docs/architecture.md](docs/architecture.md) for the trust boundaries and operation model.
## Development and live smoke
Default verification never accesses a real mailbox:
```bash
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test
```
To explicitly test only real account discovery without printing account values or reading messages:
```bash
APPLE_MAIL_LIVE_SMOKE=1 ./scripts/live-smoke.sh
```
If the Mac is locked, Mail is unavailable, or an Automation prompt cannot be answered, the smoke exits after the backend timeout with guidance.
## Packaging, signing, and notarization
They are not required for a local source build. For distribution to other Macs, packaging gives users a normal installable artifact, Developer ID signing lets Gatekeeper verify its publisher and integrity, and notarization tells Gatekeeper that Apple scanned and accepted that signed artifact. Those steps reduce warnings and permission friction; they do not grant Mail Automation permission, which the launching app or binary may still need from the user. This repository deliberately leaves release packaging, signing, and notarization to a separate distribution workflow.
## License
MIT