proton-bridge-mcp
# proton-bridge-mcp
An [MCP](https://modelcontextprotocol.io) server that lets AI assistants (Claude Code, Claude Desktop, and other MCP clients) read, search, organize, and send Proton Mail through [Proton Mail Bridge](https://proton.me/mail/bridge).
Unofficial, community project. Not affiliated with or endorsed by Proton AG.
## Why Bridge, and why this exists
Proton Mail doesn't expose a public IMAP/SMTP API directly — Proton Mail Bridge runs locally, decrypts your mail, and re-exposes it as standard IMAP/SMTP on `127.0.0.1`. That's the only supported way to speak IMAP to a Proton Mail account programmatically.
Bridge's local IMAP/SMTP listener uses a **self-signed TLS certificate** generated per install. Most quick-start IMAP MCP servers handle this by disabling certificate verification (`rejectUnauthorized: false`) — which defeats the purpose of TLS. This server instead **pins the exact certificate** Bridge presents at setup time and validates every connection against it, so a certificate swap (e.g. from another process impersonating Bridge on your machine) is still detected and rejected.
## Features
- `list_mailboxes` — list all folders/labels
- `list_messages` — list recent messages in a mailbox
- `search_messages` — search by sender, subject, or body text
- `get_message` — fetch headers, plain-text body, flags, and attachment metadata for a message by UID (does not mark it read). All read tools label returned mail as untrusted data, since a hostile email could try to instruct the model
- `move_messages` — move messages to another mailbox: trash, archive, restore to inbox, mark as spam, or file into any folder
- `flag_messages` — star/unstar or mark read/unread
- `delete_messages` — permanently delete (expunge); irreversible in Trash/Spam/Drafts, elsewhere Bridge turns it into a move to Trash
- `manage_mailbox` — create, rename, or delete a folder (`Folders/...`) or label (`Labels/...`)
- `send_message` — send an email via Bridge SMTP, optionally as a threaded reply to a UID
- `create_draft` — compose the same way but save to Drafts instead of sending
The first four tools are read-only and carry `readOnlyHint`. The rest modify your mailbox or send mail and are annotated as writes (`readOnlyHint: false`, plus `destructiveHint` on everything except `send_message` and `create_draft`, since clearing a flag or moving/deleting mail undoes existing state), so MCP clients that gate on annotations will prompt before running them.
Every `mailbox` argument (and `move_messages`'s `destination`, and `reply_mailbox`) accepts either an alias (`inbox`, `trash`, `archive`, `spam`, `sent`, `drafts`) or an exact mailbox path from `list_mailboxes`. Aliases resolve through the IMAP special-use attributes Bridge advertises (`\Trash`, `\Archive`, ...), with a fallback to the conventional folder name. `flag_messages` takes `flag: "starred" | "read"` and a boolean `value`; Proton's star is the IMAP `\Flagged` flag. `move_messages`, `flag_messages`, and `delete_messages` accept a list of up to 100 UIDs (from `list_messages` or `search_messages`), so you can act on many messages in one call.
`send_message` and `create_draft` take `to`/`cc`/`bcc` (string or array), `subject`, `text`, and optional `html`. A recipient is required to send but optional for a draft. Pass `reply_to_uid` (and `reply_mailbox` if not INBOX) to reply: the recipient defaults to the original's Reply-To/From, the subject to `Re: ...`, and `In-Reply-To`/`References` are set so the reply threads correctly. Sending goes through Bridge's SMTP listener (default port `1025`, STARTTLS) validated against the same pinned certificate as IMAP.
## Prerequisites
- A Proton Mail plan that supports Bridge (Unlimited, Business, or legacy Professional/Visionary — Bridge requires a paid plan)
- [Proton Mail Bridge](https://proton.me/mail/bridge) installed, running, and logged in
- Node.js 18+
- An MCP client: [Claude Code](https://claude.com/claude-code), Claude Desktop, or any other MCP-compatible client
## Setup
### 1. Install
```bash
git clone https://github.com/alpharomercoma/proton-bridge-mcp.git
cd proton-bridge-mcp
npm install
```
(Once published to npm, this step will be optional — see [Using via npx](#using-via-npx) below.)
### 2. Run the setup wizard
```bash
npm run setup
```
You'll be asked for:
- Bridge IMAP host/port (defaults: `127.0.0.1` / `1143`) and SMTP port (default `1025`)
- Your Proton Mail address
- Your **Bridge password** — this is a Bridge-generated password shown in the Bridge app under your account, *not* your normal Proton account password
The wizard then:
1. Connects to Bridge and performs the real IMAP `STARTTLS` upgrade to fetch its certificate (no `openssl` dependency — a portable Node-native handshake)
2. Prints the certificate's SHA-256 fingerprint for your own awareness
3. Verifies login actually works, validated strictly against that same pinned certificate
4. Writes `~/.config/proton-bridge-mcp/credentials.json` and `bridge-ca.pem`, both `chmod 600`, inside a `chmod 700` directory
Nothing is written until login has been verified.
### 3. Register with your MCP client
**Claude Code:**
```bash
claude mcp add proton-mail -s user -- node /path/to/proton-bridge-mcp/bin/proton-bridge-mcp.mjs
```
(Use `-- npx -y @alpharomercoma/proton-bridge-mcp` instead — see [Using via npx](#using-via-npx) below.)
**Claude Desktop / other MCP clients** — add to your MCP config file:
```json
{
"mcpServers": {
"proton-mail": {
"command": "node",
"args": ["/path/to/proton-bridge-mcp/bin/proton-bridge-mcp.mjs"]
}
}
}
```
### Using via npx
Published on npm as [`@alpharomercoma/proton-bridge-mcp`](https://www.npmjs.com/package/@alpharomercoma/proton-bridge-mcp) — skip the local clone entirely:
```bash
npx -y -p @alpharomercoma/proton-bridge-mcp proton-bridge-mcp-setup
claude mcp add proton-mail -s user -- npx -y @alpharomercoma/proton-bridge-mcp
```
**Codex CLI:**
Tested end-to-end against Codex CLI 0.146.0 (`codex mcp add`, real Bridge connection, real tool calls). Two things are Codex-specific and easy to miss:
1. Codex's MCP support is behind an under-development feature flag as of 0.146.0. Enable it once:
```bash
codex features enable mcp_2026_07_28
```
2. Register the server with an **exact, pinned version** — not a bare `npx -y @alpharomercoma/proton-bridge-mcp`:
```bash
codex mcp add proton-mail -- npx -y @alpharomercoma/proton-bridge-mcp@1.1.0
```
Reason: if you've ever run `npm install -g @alpharomercoma/proton-bridge-mcp` on the same machine, an unpinned `npx` invocation will silently prefer that stale global install over fetching the current version from the registry — no error, it just quietly runs old code. Pinning the version sidesteps this entirely. Bump the pinned version here when you upgrade.
Once registered, `codex mcp get proton-mail` should show `enabled: true`, and the read-only tools (`list_mailboxes`, `list_messages`, `search_messages`, `get_message`) work in both the interactive `codex` session and non-interactive `codex exec` runs without triggering an approval prompt — they're tagged with `readOnlyHint` annotations for exactly this reason. The write tools (everything after `get_message`) are not read-only, so expect an approval prompt for those depending on your client's policy.
## Configuration reference
By default, config lives at `~/.config/proton-bridge-mcp/`. Override the location or individual values with environment variables (useful for multiple accounts or containers):
| Variable | Purpose | Default |
|---|---|---|
| `PROTON_BRIDGE_MCP_HOME` | Config directory | `~/.config/proton-bridge-mcp` |
| `PROTON_BRIDGE_HOST` | Bridge IMAP/SMTP host | value from `credentials.json` |
| `PROTON_BRIDGE_PORT` | Bridge IMAP port | value from `credentials.json` (default `1143`) |
| `PROTON_BRIDGE_SMTP_PORT` | Bridge SMTP port (sending) | value from `credentials.json` (default `1025`) |
| `PROTON_BRIDGE_USER` | Proton Mail address | value from `credentials.json` |
| `PROTON_BRIDGE_PASSWORD` | Bridge password | value from `credentials.json` |
| `PROTON_BRIDGE_CA_PATH` | Path to pinned cert PEM | `<config dir>/bridge-ca.pem` |
Environment variables always take precedence over the credentials file.
## Security model
- **No disabled TLS verification.** The server validates every connection against the certificate pinned during setup (`tls.ca`), not `rejectUnauthorized: false`.
- **Trust-on-first-use, like SSH.** The one moment we can't validate against anything is fetching the certificate itself during setup — the same bootstrap problem SSH solves by showing you a host-key fingerprint on first connect. Bridge only listens on `127.0.0.1`, so this step can't be intercepted over the network; only another process already running as you on the same machine could tamper with it, and at that point your credentials file is equally exposed regardless of TLS.
- **Credentials never touch the MCP client's own config.** Rather than passing `-e PROTON_BRIDGE_PASSWORD=...` to `claude mcp add` (which lands in `~/.claude.json` and your shell history), the password lives only in `credentials.json`, `chmod 600`, outside any repo or synced config.
- **Rotate the Bridge password if it's ever been pasted into a chat, terminal share, or committed by accident.** Bridge passwords are cheap to regenerate (Bridge app → account → "Generate new password") and don't touch your actual Proton account password.
## Troubleshooting
**`Missing Proton Bridge "host"` / `"user"` / `"password"`** — run `npm run setup` (or `npx -p @alpharomercoma/proton-bridge-mcp proton-bridge-mcp-setup`) first, or set the `PROTON_BRIDGE_*` env vars.
**`No pinned Bridge certificate found`** — same as above; the setup wizard writes `bridge-ca.pem` alongside the credentials.
**Login fails during setup** — double check you're using the Bridge-generated password (visible in the Bridge app), not your Proton account password. Also confirm Bridge is running and unlocked.
**Certificate fingerprint changes unexpectedly** — this happens if you reinstall Bridge or reset its config (it regenerates its cert). Re-run `npm run setup` to re-pin. If you didn't reinstall Bridge and the fingerprint changed anyway, treat that as suspicious and investigate before continuing.
## Contributing
Issues and PRs welcome. Run `npm run lint`, `npm test`, and `npm run build` (syntax check plus a dry-run `npm pack`) before opening a PR; CI runs the same three on Node 18/20/22. Keep changes scoped. Anything that widens what the server can do to your mailbox (new write tools, attachments, bulk operations) should come with tests and a README note on its safety annotations.
## License
MIT — see [LICENSE](LICENSE).
TDQS
Scored across 4 tools
Each tool targets a distinct action: listing mailboxes, listing messages, searching messages, and fetching a single message. There is no overlap or ambiguity between them.
All tool names follow a consistent verb_noun pattern: list_mailboxes, list_messages, search_messages, get_message. The naming is predictable and uniform.
With 4 tools, the set is well-scoped for a focused read-only email access use case. Each tool serves a clear purpose and none are redundant.
The toolset covers the primary read workflow: enumerate mailboxes, list messages, search, and fetch full content. However, it lacks send, draft, or message mutation capabilities, which are common in email tools but may be intentionally omitted for a read-only bridge interface.