Proton Mail Bridge MCP
```
____ ____ ___ _____ ___ _ _ __ __ _ ___ _
| _ \| _ \ / _ \_ _/ _ \| \ | | | \/ | / \ |_ _| |
| |_) | |_) | | | || || | | | \| | | |\/| | / _ \ | || |
| __/| _ <| |_| || || |_| | |\ | | | | |/ ___ \ | || |___
|_| |_| \_\\___/ |_| \___/|_| \_| |_| |_/_/ \_\___|_____|
Bridge Client · CLI + Claude Desktop MCP for Proton Mail
```
<div align="center">
[](https://www.npmjs.com/package/proton-mail-bridge-client)
[](https://github.com/googlarz/proton-mail-bridge-client/actions/workflows/ci.yml)
[](LICENSE)
[](https://nodejs.org)
[](https://www.typescriptlang.org)
[](https://modelcontextprotocol.io)
[](https://github.com/googlarz/proton-mail-bridge-client)
[](https://github.com/googlarz/proton-mail-bridge-client/commits/main)
[](https://github.com/googlarz/proton-mail-bridge-client)
[](https://glama.ai/mcp/servers/googlarz/proton-mail-bridge-client)
</div>
---
Give Claude Desktop (or Cline, or any MCP client) full access to your Proton Mail inbox: read, search, send, draft, triage threads, manage folders, save attachments, and more — 100 MCP tools in total. Most of the same capabilities are also available as a full CLI for scripting, cron, and piped automation — no Claude required.
> **Using Proton Drive too?** See [**proton-drive-mcp**](https://github.com/googlarz/proton-drive-mcp), the companion MCP server and CLI for Proton Drive (upload, download, share and manage your encrypted files from Claude). Run both side by side to handle mail and files in one conversation, for example save an attachment from here, then upload it to Drive. More in [Related projects](#related-projects).
> **Battle-tested at scale (v2.0.0):** full mailbox backfill validated end-to-end against a real account with 57,000+ indexed messages across 62 folders/labels — including a 22,800-message Archive folder backfilled from scratch, UID-window by UID-window, with zero data loss across restarts, transient IMAP disconnects, and connection timeouts.
## Quick start (2 minutes)
1. **Install and sign in to [Proton Mail Bridge](https://proton.me/mail/bridge)**, and leave it running. In the Bridge app, open your account and copy the **Bridge password** (it is not your Proton password).
2. **Connect Claude Desktop**, either way:
- **Bundle:** download the `proton-mail-bridge-client-<your-os>.mcpb` for macOS (`darwin-arm64` Apple silicon, `darwin-x64` Intel), Linux (`linux-x64`, `linux-arm64`) or Windows (`win32-x64`) from the [latest release](https://github.com/googlarz/proton-mail-bridge-client/releases/latest) and open it. Claude Desktop asks for your Proton address and the Bridge password, plus optional settings (Bridge IMAP/SMTP host and port, read-only, allow sending, signature, extra accounts, data and download folders, tool tier); the defaults match a standard Bridge install.
- **npm:** `npm install -g proton-mail-bridge-client`, then `proton-mail-bridge-client setup-claude-desktop` (it checks your Bridge ports and writes the config). Fully quit and reopen Claude Desktop afterwards.
3. **Check it works:** run `proton-mail-bridge-client doctor`, or ask Claude "run the Proton Mail doctor".
4. **Try these prompts:**
- "Give me a digest of my inbox and flag anything that needs a reply today."
- "Find the last email from `alice@example.com` and draft a reply for me to review." (nothing is sent until you say so)
- "Which of my emails have attachments from this month?"
- "Show me my drafts and tighten the oldest one."
5. **Optional:** `PROTONMAIL_READ_ONLY=true` to browse without being able to change anything, `PROTONMAIL_TOOL_TIER=core` for a smaller tool set (25 instead of 100), and more accounts under [Multi-account support](#multi-account-support-v210). More recipes below in [Try it](#try-it-example-claude-prompts).
---
## What you get
- **Claude reads and manages your Proton Mail** — triage, reply, draft, archive, search, move, batch-act on threads, pull attachments
- **Full CLI** — a dedicated command for every one of the 100 tools (plus a generic `tool <name>` passthrough), scriptable and pipeable, works in cron and shell scripts
- **Fast local search** — full-text search across your inbox without hitting IMAP on every query
- **Safety controls** — read-only mode, send gate, destructive-action confirmation, per-action allowlist
- **Privacy-native** — no third-party email service involved; your mail stays on your machine
**How this relates to Proton Bridge.** This is a layer on top of [Proton Mail Bridge](https://proton.me/mail/bridge), not a replacement for it. Bridge is Proton's official, local IMAP/SMTP gateway and the only supported way to reach a Proton mailbox from other software; this project talks to it. Bridge's own `--cli` mode manages Bridge itself (signing in, account details and the Bridge password, ports and other settings), it is not a mail client. Proton does not publish a terminal mail client, so reading, searching, drafting and sending from Claude or a script is what this adds on top, together with the safety controls above. Bridge has to be running and signed in.
---
## Privacy model
Your emails travel: **Proton Mail → Proton Bridge (local) → this server (local) → your AI client**.
Nothing goes through a third-party email relay. Proton Bridge decrypts your mail locally; this server reads it over a local IMAP connection on `127.0.0.1`. The AI model (Claude Desktop, Cline, etc.) sees the email content you ask it to act on — that's the whole point — but no email leaves your machine except through your own Proton account when you send.
If you use Claude Desktop with the default Anthropic API, conversation content (including email snippets) is sent to Anthropic per their [privacy policy](https://www.anthropic.com/privacy). If you self-host an LLM or use a local-only Claude setup, nothing leaves your machine at all.
---
## Prerequisites
**1. Proton Bridge** — must be installed, signed in, and running.
Download: [proton.me/mail/bridge](https://proton.me/mail/bridge)
> **Bridge password vs Proton password:** Proton Bridge generates a separate local password that is *not* your Proton account password. Find it inside the Bridge app under **Account → Copy password** (or similar — exact label varies by Bridge version). You'll need this for setup.
**2. Node.js 24 or later** — `node --version` to check.
**3. Your Bridge credentials** — from the Bridge app:
- IMAP host/port (default: `127.0.0.1:1143`)
- SMTP host/port (default: `127.0.0.1:1025`)
- Username (your Proton email address)
- Bridge password (see note above)
---
## Install
**npm (recommended):**
```bash
npm install -g proton-mail-bridge-client
```
**Homebrew:**
```bash
brew tap googlarz/tap
brew install proton-mail-bridge-client
```
<details>
<summary>Source install (development)</summary>
```bash
git clone https://github.com/googlarz/proton-mail-bridge-client.git
cd proton-mail-bridge-client
npm install
npm run build
```
The `proton-mail-bridge-client` binary is available inside the repo after build.
</details>
---
## Connect to Claude Desktop
Run the guided setup wizard:
```bash
proton-mail-bridge-client setup-claude-desktop
```
The wizard:
- checks your local Bridge ports
- asks for your Bridge username and Bridge password
- writes the Claude Desktop MCP config entry
The runtime is installed into a staging directory first, checked (Node starts, `better-sqlite3` opens a database, `dist` imports, version matches) and only then swapped in; the previous runtime stays next to it as `<runtime dir>.previous`, and a failed install leaves the working one untouched. The config is merged, not replaced: existing `env` keys you added by hand are kept, and the config is rewritten atomically (the newest 5 `.bak-*` copies are kept). Passwords are never printed. Run `npm run check:claude-desktop` to verify an install; it exits non-zero when the command, the runtime or the database module is broken.
**After setup:** restart Claude Desktop, make sure Proton Bridge is open, then check **`+` → Connectors → proton-mail-bridge**.
### Updating
```bash
npm update -g proton-mail-bridge-client
proton-mail-bridge-client setup-claude-desktop
```
### Manual config
The wizard handles config automatically. If you need to set it up by hand, three credential methods are supported:
<details>
<summary>Option 1 — Environment variables (simplest)</summary>
```json
{
"mcpServers": {
"proton-mail-bridge": {
"command": "proton-mail-bridge-mcp",
"env": {
"PROTONMAIL_USERNAME": "you@proton.me",
"PROTONMAIL_PASSWORD": "your-bridge-password",
"PROTONMAIL_IMAP_HOST": "127.0.0.1",
"PROTONMAIL_IMAP_PORT": "1143",
"PROTONMAIL_IMAP_SECURE": "false",
"PROTONMAIL_SMTP_HOST": "127.0.0.1",
"PROTONMAIL_SMTP_PORT": "1025"
}
}
}
}
```
</details>
<details>
<summary>Option 2 — File-based secrets (credentials in files, not config)</summary>
```json
{
"mcpServers": {
"proton-mail-bridge": {
"command": "proton-mail-bridge-mcp",
"env": {
"PROTONMAIL_USERNAME_FILE": "/path/to/username.txt",
"PROTONMAIL_PASSWORD_FILE": "/path/to/password.txt",
"PROTONMAIL_IMAP_HOST": "127.0.0.1",
"PROTONMAIL_IMAP_PORT": "1143",
"PROTONMAIL_IMAP_SECURE": "false",
"PROTONMAIL_SMTP_HOST": "127.0.0.1",
"PROTONMAIL_SMTP_PORT": "1025"
}
}
}
}
```
</details>
<details>
<summary>Option 3 — Command-based secrets (pass, gopass, or any secret manager)</summary>
```json
{
"mcpServers": {
"proton-mail-bridge": {
"command": "proton-mail-bridge-mcp",
"env": {
"PROTONMAIL_USERNAME_COMMAND": "pass proton/username",
"PROTONMAIL_PASSWORD_COMMAND": "pass proton/password",
"PROTONMAIL_IMAP_HOST": "127.0.0.1",
"PROTONMAIL_IMAP_PORT": "1143",
"PROTONMAIL_IMAP_SECURE": "false",
"PROTONMAIL_SMTP_HOST": "127.0.0.1",
"PROTONMAIL_SMTP_PORT": "1025"
}
}
}
}
```
</details>
---
## Connect to Claude Code
Install globally, then register the server with one command:
```bash
npm install -g proton-mail-bridge-client
claude mcp add proton-mail-bridge \
-e PROTONMAIL_USERNAME=you@proton.me \
-e PROTONMAIL_PASSWORD=your-bridge-password \
-- proton-mail-bridge-mcp
```
`your-bridge-password` is the Bridge app's own password (**Bridge → account → Mailbox details**), not your Proton account password — see the note under [Prerequisites](#prerequisites).
By default this registers the server for the current project only. Add `-s user` to make it available in every project:
```bash
claude mcp add proton-mail-bridge -s user \
-e PROTONMAIL_USERNAME=you@proton.me \
-e PROTONMAIL_PASSWORD=your-bridge-password \
-- proton-mail-bridge-mcp
```
Verify it's connected:
```bash
claude mcp list
```
For file-based or command-based credentials instead of plaintext env vars, add `-e PROTONMAIL_USERNAME_FILE=/path/to/file` (or `_COMMAND`) the same way — see the credential methods under [Manual config](#connect-to-claude-desktop) above.
---
## Connect to Cline (VS Code)
Install globally (`npm install -g proton-mail-bridge-client`), then open Cline's MCP settings:
- VS Code → Cline extension panel → MCP servers icon → **Edit MCP Settings**
- Or edit directly: `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` (macOS)
Add the server:
```json
{
"mcpServers": {
"proton-mail-bridge": {
"command": "proton-mail-bridge-mcp",
"env": {
"PROTONMAIL_USERNAME": "you@proton.me",
"PROTONMAIL_PASSWORD": "your-bridge-password",
"PROTONMAIL_IMAP_HOST": "127.0.0.1",
"PROTONMAIL_IMAP_PORT": "1143",
"PROTONMAIL_IMAP_SECURE": "false",
"PROTONMAIL_SMTP_HOST": "127.0.0.1",
"PROTONMAIL_SMTP_PORT": "1025"
}
}
}
}
```
For file-based or command-based credentials, use the same `PROTONMAIL_USERNAME_FILE` / `PROTONMAIL_PASSWORD_COMMAND` pattern from the Claude Desktop manual config above.
Reload the Cline extension after saving. Proton Mail tools will appear in Cline's tool list.
---
## Multi-account support (v2.1.0+)
Support multiple Proton Mail addresses configured in Bridge (using Bridge's **Split Addresses** feature). Each address gets its own isolated service stack, local index, and drafts storage.
### Setup
First, enable **Split Addresses** in Proton Bridge and configure each address as a separate account. Bridge will generate a separate password for each address. Then set `PROTONMAIL_ACCOUNTS_JSON`:
```json
{
"mcpServers": {
"proton-mail-bridge": {
"command": "proton-mail-bridge-mcp",
"env": {
"PROTONMAIL_USERNAME": "primary@proton.me",
"PROTONMAIL_PASSWORD": "primary-bridge-password",
"PROTONMAIL_IMAP_HOST": "127.0.0.1",
"PROTONMAIL_IMAP_PORT": "1143",
"PROTONMAIL_IMAP_SECURE": "false",
"PROTONMAIL_SMTP_HOST": "127.0.0.1",
"PROTONMAIL_SMTP_PORT": "1025",
"PROTONMAIL_ACCOUNTS_JSON": "[
{ \"address\": \"primary@proton.me\", \"password\": \"primary-bridge-password\" },
{ \"address\": \"alias@proton.me\", \"password\": \"alias-bridge-password\" },
{ \"address\": \"business@custom.com\", \"password\": \"business-bridge-password\" }
]"
}
}
}
}
```
**Note:** The password is the **Bridge password for each address**, not your Proton account password. Bridge generates a unique password for each Split Address.
### How it works
- **Primary account:** the first entry in `PROTONMAIL_ACCOUNTS_JSON` (or the main `PROTONMAIL_USERNAME`/`PROTONMAIL_PASSWORD` pair if not set). Email IDs from the primary account have no prefix.
- **Additional accounts:** each has an auto-generated slug (e.g., `alias`, `business`). Email IDs carry an account prefix: `alias::message-id`, `business::message-id`.
- **Sending:** `send_email`, `reply_to_email`, etc. route through the account matching the `from` address. If no account matches, the primary connection is used with a header override (for non-configured aliases).
- **Search and read:** all read tools (search, threads, analytics, digest) automatically fan out across all configured accounts and merge results, preserving the account prefix in returned IDs.
- **One account only:** the triage, statistics, draft and folder tools (`get_threads`, `get_inbox_digest`, `get_follow_up_candidates`, `get_labels`, `list_drafts`, `get_folders`, `top_senders`, ...) take an optional `account` (address or slug) to look at a single account instead of all of them; an unknown value is an error.
### Tools across accounts
**Fan out across all accounts:**
- Search & read: `search_indexed_emails`, `get_emails`, `get_threads`, `get_thread_by_id`, `count_messages`, `get_labels`, `folder_stats`, `top_senders`, `get_contacts`, `get_volume_trends`, `get_email_analytics`, `get_email_stats`, `get_folders`
- Triage: `get_inbox_digest`, `get_follow_up_candidates`, `get_actionable_threads`, `find_document_threads`, `prepare_meeting_context`
- Diagnostics: `get_connection_status`, `get_runtime_status`, `run_doctor` (include an `accounts` array showing per-account status)
- Drafts: `list_drafts` — lists every configured account's drafts, merged and re-sorted by last update; supports optional `limit`/`offset`
**Account-specific (prefix to target):**
- Actions: `mark_email_read`, `star_email`, `move_email`, `delete_email`, etc. — prefix the `emailId` with account slug if targeting a non-primary account
- Drafts: `create_draft`, `get_draft`, `send_draft`, etc. — drafts live per account; prefix `draftId` to access non-primary drafts
- Send: `send_email`, `reply_to_email`, `forward_email` — match the `from` address to route through the correct account's SMTP
- Sync and index maintenance: `sync_emails`, `sync_folders`, `run_background_sync`, `wait_for_mailbox_changes` — pass `account` (address or slug) to act on a non-primary account; without it they act on the primary account, and an unknown value is an error
**Primary-only (for now):**
- `list_remote_drafts` — shows the primary account's remote Proton Drafts folder only
- Thread actions (`move_thread`, `delete_thread`, `flag_thread`) — operate on the account the thread ID's prefix names
### Verify setup
Use `list_accounts` to check configured accounts and verify connection status:
```bash
proton-mail-bridge-client list_accounts --json
```
Output:
```json
[
{ "slug": "primary", "address": "primary@proton.me", "isPrimary": true, "index": "fresh", "imap": "ok", "smtp": "ok" },
{ "slug": "alias", "address": "alias@proton.me", "isPrimary": false, "index": "fresh", "imap": "ok", "smtp": "ok" },
{ "slug": "business", "address": "business@custom.com", "isPrimary": false, "index": "stale", "imap": "timeout", "smtp": "ok" }
]
```
---
## Try it: example Claude prompts
**Morning triage**
> "Give me a digest of my inbox. Flag anything that needs a reply today and anything that looks like a bill or invoice."
**Inbox zero**
> "Go through my unread emails from the past 3 days. Archive newsletters, trash anything promotional, and tell me what's left that needs action."
**Folder filing**
> "Find all emails from stripe.com and move them to Folders/Receipts. Create the folder if it doesn't exist."
**Meeting prep**
> "I have a call with alice@example.com in an hour. Pull up our last 5 email threads and summarise the open items."
**Draft review**
> "Show me my drafts, pick the oldest one, and suggest a better subject line and closing paragraph."
**Calendar invitations**
> "Do I have any unanswered meeting invitations this week? Accept the ones that don't clash with each other and show me the rest."
**Follow-up**
> "I just sent the proposal to Ann. If she hasn't answered in five days, remind me."
> **Tip:** When creating folders, use `Folders/Name` (not just `Name`) — that's the Proton Bridge namespace for real folders vs. labels.
More recipes — expanded triage prompts, cron scripts for scheduled digests, and a Claude Code `/mail-triage` slash command — are in [examples/](examples/).
---
## Recommended System Prompt
Add this to Claude Desktop's system prompt (Settings → Claude Desktop → System Prompt) for safer defaults:
```
You have access to my Proton Mail inbox via the proton-mail-bridge tool.
Rules:
- Always use dryRun: true before any batch operation (batch_email_action, apply_thread_action).
- Before calling send_email, reply_to_email, or forward_email, summarise what you are about to send and ask me to confirm.
- For anything important or hard to walk back (a wide CC list, a sensitive topic, an attachment), offer a short undo window via send_email's undoWindowSeconds instead of sending immediately — remind me it only protects against mistakes noticed in the next few seconds, not a change of mind days later.
- Before calling delete_email, confirm with me — deletion is permanent.
- Prefer create_draft over send_email when composing from scratch.
- When revising a draft, change parts of it with update_draft's bodyEdits (find/replace) instead of resending the whole body, and pass syncToRemote:false while iterating; call sync_draft_to_remote once when I say the draft is ready. Each synced edit costs seconds (see "Performance and token cost").
- Use get_inbox_digest or get_actionable_threads as your starting point for triage sessions.
```
---
## CLI
Every capability is also a scriptable terminal command — no Claude required:
```bash
proton-mail-bridge-client digest # morning triage summary
proton-mail-bridge-client search --from stripe.com --json | jq . # scriptable search
echo "Deploy done" | proton-mail-bridge-client send --to you@x.com --subject "Deploy"
proton-mail-bridge-client notify & # background new-mail alerts
```
All commands support `--json` for machine-readable output, and any MCP tool is directly callable via `proton-mail-bridge-client tool <name> --args '{...}'`.
`<command> --help` prints that command's usage without running it. Flags accept `--flag value` or `--flag=value` (a value starting with `--` needs the `=` form, or must come after a bare `--`); unknown flags are an error. Exit codes: `0` success, `1` failure (including a failed `doctor` or `connection-status` check, or a failed item in a `batch`/`bulk-*` run), `2` usage error.
**Full command reference: [docs/cli.md](docs/cli.md)** (a named command for every one of the 100 tools, across read, triage, compose, mailbox actions, folders, drafts, templates, analytics, and diagnostics).
---
## Safety controls
All flags work in both the MCP server and CLI:
```bash
PROTONMAIL_TOOL_TIER=core # expose 25 core tools instead of all 100 — saves context window
PROTONMAIL_READ_ONLY=true # disable all write operations
PROTONMAIL_ALLOW_SEND=false # disable SMTP sends only (other writes still work)
PROTONMAIL_CONFIRM_DESTRUCTIVE=true # require confirmed:true on send, reply, forward, delete
PROTONMAIL_RESTRICT_OUTBOUND_TO_SELF=true # every send may only go to your own address — the safe way to try the server
PROTONMAIL_ALLOW_EMPTY_FOLDER=true # enable empty_folder (off by default): permanently deletes ALL messages in a folder
PROTONMAIL_ALLOWED_ACTIONS='mark_read,archive,trash' # per-action allowlist
```
`batch_email_action` and `apply_thread_action` both support `dryRun: true` regardless of the above flags.
`PROTONMAIL_ALLOWED_ACTIONS` gates message actions (mark read/unread, star/unstar, archive, trash, restore, move, delete, and the flag/bulk/thread variants) and deleting folders or labels (as `delete`). Creating or renaming folders and labels, creating or deleting templates, importing messages, and clearing the cache or index are not in that list: they are blocked by `PROTONMAIL_READ_ONLY=true` and nothing else. To forbid them, run read-only.
**Trying it safely: `PROTONMAIL_RESTRICT_OUTBOUND_TO_SELF=true`.** With this on, the server can compose, draft, reply, forward and send for real, but every recipient (To, CC and BCC) must be your own Bridge login address; a `+tag` alias of it counts as yours. Anything else is refused with `RESTRICT_OUTBOUND_TO_SELF is enabled. Cannot send to: …` before any mail is built or queued. It covers `send_email`, `reply_to_email`, `reply_all_email`, `forward_email`, `send_draft`, `schedule_draft`, `send_test_email` and `unsubscribe_sender`, and it is checked again when a queued or scheduled send fires, so a send queued before you turned it on cannot slip out later. It does not stop the model from *writing* a message to someone else into a draft; it stops it from being sent.
**`PROTONMAIL_ALLOW_EMPTY_FOLDER`.** `empty_folder` deletes every message in a folder at once and cannot be undone, so it is switched off unless this is `true` (the call then fails with a message naming the variable). When enabled it still needs an explicit `confirmed: true` to delete: without it, it only returns a preview of what would be removed. For anything less than an entire folder use `bulk_delete`.
---
## Environment reference
```bash
# Credentials (required)
PROTONMAIL_USERNAME='you@proton.me'
PROTONMAIL_PASSWORD='your-bridge-password' # Bridge password, not Proton account password
PROTONMAIL_IMAP_HOST='127.0.0.1'
PROTONMAIL_IMAP_PORT='1143'
PROTONMAIL_IMAP_SECURE='false'
PROTONMAIL_SMTP_HOST='127.0.0.1'
PROTONMAIL_SMTP_PORT='1025'
PROTONMAIL_SMTP_SECURE='true' # Bridge's local SMTP port requires implicit TLS from the first byte; set false only for a non-Bridge SMTP relay
PROTONMAIL_IMAP_USERNAME='' # optional: log in to IMAP as a different user than PROTONMAIL_USERNAME (default: the same). The send identity, and RESTRICT_OUTBOUND_TO_SELF, still use PROTONMAIL_USERNAME.
PROTONMAIL_IMAP_PASSWORD='' # optional: a different IMAP password than PROTONMAIL_PASSWORD (default: the same)
# Secrets via file or command (avoids raw credentials in config)
PROTONMAIL_USERNAME_FILE='/path/to/user.txt'
PROTONMAIL_PASSWORD_FILE='/path/to/pass.txt'
PROTONMAIL_USERNAME_COMMAND='pass proton/username'
PROTONMAIL_PASSWORD_COMMAND='pass proton/password'
# Storage
PROTONMAIL_DATA_DIR="$HOME/.proton-mail-bridge-client"
# Tools
PROTONMAIL_TOOL_TIER='full' # 'core' exposes 25 essential tools, incl. draft review/edit and list_accounts (saves context window); 'full' exposes all 100
# Safety
PROTONMAIL_READ_ONLY='false'
PROTONMAIL_ALLOW_SEND='true'
PROTONMAIL_ALLOW_REMOTE_DRAFT_SYNC='true'
PROTONMAIL_ALLOWED_ACTIONS='mark_read,mark_unread,star,unstar,archive,trash,restore,move,delete'
PROTONMAIL_CONFIRM_DESTRUCTIVE='false'
PROTONMAIL_RESTRICT_OUTBOUND_TO_SELF='false' # true: sends may only go to your own Bridge login address (To, CC and BCC; +tag aliases count as yours); anything else is refused, and queued/scheduled sends are re-checked when they fire. Covers send_email, reply_to_email, reply_all_email, forward_email, send_draft, schedule_draft, send_test_email, unsubscribe_sender. The safest way to try the server.
PROTONMAIL_ALLOW_EMPTY_FOLDER='false' # true: enables empty_folder, which permanently deletes ALL messages in a folder (still needs confirmed:true; without it only a preview is returned). Off by default; use bulk_delete for a subset.
PROTONMAIL_ALLOW_UNSAFE_HTML='false' # HTML in outgoing mail is always sanitized (scripts, event handlers and remote images/beacons removed). A send that passes sanitizeHtml:false is IGNORED — still sanitized, with a warning logged — unless this is exactly 'true'. Leave it off unless you deliberately send raw HTML.
PROTONMAIL_SEND_DELAY_SECONDS='0' # >0: send_email, reply_to_email, reply_all_email and forward_email queue instead of sending immediately, cancelable via cancel_send (undoWindowSeconds overrides per call). send_draft is not delayed; use schedule_draft. Only fires while this server stays running.
PROTONMAIL_SIGNATURE='' # Plain text, appended to send_email/reply_to_email/reply_all_email/forward_email bodies (text + HTML), after your own text and before any quoted/forwarded content. Every send now goes out multipart (a plain-text send auto-gets an html alternative too), so the signature always gets its HTML treatment, not just the text/plain part. Opt out per-message with appendSignature: false. Never applied to send_draft/schedule_draft — draft content is already finalized.
#
# Note: a signature configured inside Proton Mail itself (Settings → Identity and addressing)
# is only inserted by Proton's own web/app compose UI — it is NEVER applied to mail submitted
# over SMTP by an external client, including this one. There is no way to make Bridge apply it
# for you. Set PROTONMAIL_SIGNATURE above instead if you want a signature on messages this
# server sends.
# Sync
PROTONMAIL_AUTO_SYNC='true'
PROTONMAIL_AUTO_SYNC_FOLDER='INBOX,Sent' # comma-separated folders the background sync keeps indexed
PROTONMAIL_AUTO_SYNC_FULL='false' # true: each background pass is a full sync (larger sample per folder, and messages moved/deleted elsewhere are pruned from the index) instead of incremental
PROTONMAIL_AUTO_SYNC_LIMIT_PER_FOLDER='100' # messages fetched per folder per background pass (1-500)
PROTONMAIL_STARTUP_SYNC='true'
PROTONMAIL_SYNC_INTERVAL_MINUTES='5'
PROTONMAIL_IDLE_WATCH='true'
PROTONMAIL_IDLE_MAX_SECONDS='30'
# Tuning and diagnostics
PROTONMAIL_OP_DELAY_MS='0' # minimum gap in ms between IMAP mailbox operations (0-5000); raise it to be gentler on a slow Bridge. 0 = no throttling
PROTONMAIL_DEBUG='false' # true: get_connection_status and run_doctor include the raw underlying connection error text (otherwise only a classified cause and suggestion)
PROTONMAIL_CLAUDE_RUNTIME_DIR='' # where the Claude Desktop installer puts its runtime copy (default: ~/Library/Application Support/Proton Mail Bridge Client on macOS, %APPDATA%\Proton Mail Bridge Client on Windows, ~/.local/share/proton-mail-bridge-client on Linux)
```
---
## Ask before sending: address and signature (optional skill)
With several accounts or a signature, the server sends from whichever `from` the caller passes and cannot know which one you meant. [`skills/send-with-identity`](skills/send-with-identity/SKILL.md) is a Claude skill that makes the agent ask "from which address, and with which signature?" before every send, reply, forward or schedule, then pass `from` and its own signature. On the four immediate-send tools (`send_email`, `reply_to_email`, `reply_all_email`, `forward_email`) that means `appendSignature: false` plus `isHtml: true` for an HTML signature, so nothing is doubled; drafts never get `PROTONMAIL_SIGNATURE` appended, so the skill writes the signature into the draft body, and `send_draft`/`schedule_draft` take no `from`, so the address and signature are settled on the draft first. It does not replace your explicit approval to send. Copy the folder to `~/.claude/skills/send-with-identity`, and fill in your addresses and signatures there (keep that copy private).
## Compared with Claude's native Gmail connector
| Capability | Gmail connector | Proton Mail Bridge Client |
|---|---|---|
| Setup | First-party OAuth | Requires Proton Bridge + this client |
| Search and read | Native Claude UX | IMAP + local index |
| Send email | No | Yes |
| Draft workflows | Better first-party UX | Full control incl. remote draft sync |
| Attachment content | Limited | Fetch and save to disk |
| Mailbox actions | Limited | Full (star, move, archive, trash, restore, delete, batch) |
| Folder management | No | Yes (create, rename, delete) |
| CLI access | No | Full parity with MCP |
| Privacy | Google-hosted | Proton E2E encryption, local Bridge |
---
## Tool surface
### Send
`send_email` · `send_test_email` · `reply_to_email` · `reply_all_email` · `forward_email` · `respond_to_invite` (answer a calendar invitation)
### Drafts
`create_draft` · `create_reply_draft` · `create_forward_draft` · `create_thread_reply_draft` · `list_drafts` · `list_remote_drafts` · `get_draft` · `update_draft` · `sync_draft_to_remote` · `send_draft` · `delete_draft`
### Read
`get_emails` · `get_email_by_id` · `count_messages` · `search_emails` · `search_indexed_emails` · `list_attachments` · `get_attachment_content` · `save_attachment` · `save_attachments`
### Triage
`get_folders` · `sync_folders` · `get_labels` · `get_threads` · `get_thread_by_id` · `get_thread_brief` · `get_actionable_threads` · `get_inbox_digest` · `get_follow_up_candidates` · `set_reply_reminder` · `list_reply_reminders` · `cancel_reply_reminder` · `find_document_threads` · `prepare_meeting_context` · `delete_thread` · `flag_thread` · `move_thread`
### Actions
`mark_email_read` · `star_email` · `move_email` · `archive_email` · `trash_email` · `restore_email` · `delete_email` · `batch_email_action` · `apply_thread_action` · `empty_folder` · `bulk_delete` · `bulk_move` · `bulk_update_flags` · `bulk_update_labels` · `update_message_flags` · `update_message_labels`
### Folder management
`create_folder` · `rename_folder` · `delete_folder` · `create_label` · `rename_label` · `delete_label`
### Analytics
`get_email_stats` · `get_email_analytics` · `get_contacts` · `get_volume_trends` · `folder_stats` · `top_senders`
### Diagnostics
`get_connection_status` · `get_runtime_status` · `run_doctor` · `get_audit_logs` · `run_background_sync` · `wait_for_mailbox_changes` · `sync_emails` · `get_index_status` · `clear_cache` · `clear_index` · `get_logs`
### Unsubscribe & trust
`get_unsubscribe_info` · `unsubscribe_sender` — `get_email_by_id` also returns a `security` block (DKIM/SPF/DMARC, encryption, spam score)
### Undo-send, scheduling & snooze
`cancel_send` · `list_scheduled_sends` · `schedule_draft` · `snooze_email` · `cancel_snooze` · `list_snoozed` — send_email, reply_to_email, reply_all_email and forward_email queue instead of sending immediately when `PROTONMAIL_SEND_DELAY_SECONDS` is set; all three only fire while this server process stays running, see [Operational notes](#operational-notes)
### Templates
`create_template` · `list_templates` · `get_template` · `delete_template` · `render_template` — `{{variable}}` substitution, render then pass the result to `send_email`
### Import/export & attachments
`export_email` · `import_email` · `get_attachment_text` · `get_emails_by_ids`
---
## Using it as a library
Beyond the CLI and MCP server, the underlying service classes are importable directly:
```ts
import { SimpleIMAPService, SMTPService } from "proton-mail-bridge-client/services";
const imapService = new SimpleIMAPService(config, logger);
const smtpService = new SMTPService(config);
```
`proton-mail-bridge-client/services` has no side effects on import — unlike the package's
main entry point, which also self-starts the MCP server when run directly. Also exported:
all shared types (`ProtonMailConfig`, `EmailSummary`, `EmailDetail`, …), `planFolderSync`,
`isLikelyAuthenticationError`, and `sanitizeHeader`.
---
## Performance and token cost
Measured against a real Proton Bridge with a ~57,000-message mailbox (INBOX 29k, Archive 23k, Sent 5k), September 2026. Your numbers depend on mailbox size and machine; the ratios are what to rely on.
| Call | Typical time |
|---|---|
| `search_indexed_emails` (local SQLite index) | ~10 ms |
| `get_emails` (3 messages) | ~0.2 s |
| `search_emails` with a `label`, or `folder` set to a small folder | < 1 s |
| `search_emails` in one large folder (INBOX, 29k) | ~2 s |
| `search_emails` with only `dateFrom`/`dateTo` in INBOX (29k) | ~1 s |
| `search_emails` with only `dateFrom`/`dateTo`, every real folder | ~2–2.5 s |
| `search_emails` with `subject` or `from` in INBOX | ~1.6 s |
| `search_emails` with free-text `query` in INBOX | ~2.5 s |
| `search_emails` with `subject` or `query`, every real folder | ~5.5–7 s |
| `search_emails` in a 50k-message label folder | ~3 s |
| `list_remote_drafts` (79 drafts) | ~15 ms |
Bridge answers IMAP `SEARCH` itself, and its cost grows with folder size. That is why a live search is slower than the local index, and why narrowing the scope matters most.
**Local index: filters and thread tools**
Measured in October 2026 (v2.7.0) on a copy of the same ~57,000-message index. "Warm" is a second call; the first call after the index changes (a sync, or another process writing to it) builds a map of which thread every message belongs to, which takes about 0.5 s (0.8 s for follow-ups and the digest) and about 31 MB of memory.
| Call | Warm |
|---|---|
| filter by `subject` or `from` | ~20 ms |
| filter by `to` | ~40 ms |
| `get_threads` with `query`, `label` or `folder` | ~25 ms |
| `get_threads` without a filter | ~40 ms |
| `get_thread_by_id` | ~1 ms |
| `get_follow_up_candidates` | 20-40 ms |
| `get_inbox_digest` | ~80 ms |
| `get_actionable_threads` | 17-35 ms |
| `prepare_meeting_context` with `person` or `domain` | ~30 ms |
| `find_document_threads` | ~225 ms |
**Make searches faster**
- **Prefer `search_indexed_emails`** when the index is current (`get_index_status`, `sync_emails`). It does not touch Bridge at all.
- **Pass `folder`** when you know it. With no `folder`, `search_emails` covers every *real* folder and skips **All Mail**, **Labels/\*** and **Starred**: those are views of mail that already lives in INBOX/Sent/Archive, and scanning them made an all-folders search about three times slower and inflated `totalMatched`. Pass `folder: "All Mail"` (or `"Labels/x"`) to search a view on purpose.
- **Use `label` for labels.** On Proton Bridge a label is the folder `Labels/<name>`, so `label: "Newsletters"` searches only that folder. A name that matches no label answers immediately with nothing instead of scanning.
- **Date filters are the cheapest filter Bridge offers** — about 1 s in INBOX and 2–2.5 s across every real folder, against 1.6 s / 5.5 s for `subject` and 2.5 s / 6–7 s for free-text `query`. When you can say *when* ("last week", "this month"), lead with `dateFrom`/`dateTo`. Even a wide range is fine: everything older than a year (about 20,000 messages) still took under 2 s.
- **Adding a date range to a text search does not make the text search faster.** In INBOX, `dateFrom` (last 30 days) + `subject` took about as long as `subject` alone (1.6 s vs 1.6 s), and + `query` 2.1 s vs 2.5 s: Bridge still does the text work. Use dates on their own to find the window, then search inside it (for example by `folder`, or by fetching that window and reading it).
- Free-text `query` searches message bodies on Bridge's side and is the slowest filter; `subject` and `from` are cheaper.
- (Single runs on one machine, so read them as ratios, not guarantees.)
**Keep responses (and token use) small**
- List and search results are compact by default: attachments are metadata only, and empty or derivable fields are left out. Pass `fields: ["subject","from","date"]` to trim further — 10 results is roughly 800 tokens that way, versus about 11,000 for 50 full results.
- Defaults are bounded: `search_emails`/`search_indexed_emails` return 50 (pass `limit` for more), `list_drafts` 20, `list_scheduled_sends` 50 newest first. Each reports `hasMore`; page with `offset`.
- Draft and queued-message bodies are shortened to a preview in list and update responses (`bodyTruncated`, `bodyLength`); use `get_draft` for one full draft. Attachment content is never inlined in a listing.
- Large attachments: set `PROTONMAIL_ALLOW_FILE_DOWNLOAD_DIR` and pass `saveTo`, rather than raising `PROTONMAIL_MAX_INLINE_BYTES`.
- **`PROTONMAIL_TOOL_TIER=core`** exposes 25 tools instead of 100: about 7k tokens of tool definitions instead of about 21k (roughly 67% less; the earlier measurement with a reference tokenizer, 7k against 20k for 96 tools, scaled by the 3.7% the four newest tool definitions add), paid on every conversation that loads the server.
- Repeating an `update_draft` that changes nothing on a draft whose remote copy is already in sync does no store write and no IMAP upload. (A draft whose last sync failed, or that was edited with `syncToRemote:false`, still syncs.)
**Working with drafts**
Drafts live in a local store first; the copy in Proton's Drafts folder is a separate upload.
- **Edit a long draft with `bodyEdits`, not by resending `body`.** `update_draft` takes `bodyEdits: [{find, replace}]` (optionally `all:true`) and changes just those fragments of the stored body: a two-sentence change to a ~14,000-character draft is a request of about 200 characters instead of about 14,000 (roughly 70 times smaller), and the model only has to write the words that change. Each `find` must match exactly once (add surrounding text, or set `all:true`); otherwise nothing is changed and the error says which edit failed and why. Edits apply in order, all-or-nothing, and match the stored text literally — for an HTML draft, the HTML source. Pass either `body` or `bodyEdits`, not both. The response reports `bodyEditsApplied`.
- **Each `update_draft` uploads the whole draft to the Drafts folder** (the full message, attachment bytes included — about 1.4 MB of MIME for a 1 MiB attachment) unless you pass `syncToRemote:false`. That upload is the slow part, and it is Bridge's, not ours: on the reference mailbox a local edit takes 2–6 ms, the first sync of a draft about 1.5 s, and a later sync that *replaces* the previous remote copy about 3.5 s (upload ≈ 1.4 s, then Bridge deleting the old copy ≈ 2.1 s; Bridge does not replace a draft appended with the same Message-ID, so the delete is required). Sending a change as `bodyEdits` or as a full `body` costs the same on the server side; `bodyEdits` saves the model's tokens, not Bridge time. So when you are making several edits in a row, do them with `syncToRemote:false` and finish with one `sync_draft_to_remote` — six edits are then about 3.5 s of waiting instead of about 20 s.
- **The response stays small however big the draft is.** `create_draft`, the reply/forward/thread-reply draft creators, `update_draft` and `sync_draft_to_remote` return a body preview and attachment metadata only (an edit of a ~200 KB draft cost about 300 tokens; a draft with a 1 MiB attachment about 220–300). Only `get_draft` returns the full body and attachment content.
- **Nothing to do if nothing changed:** repeating an `update_draft` that changes nothing on an already-synced draft does no write and no upload.
- `list_drafts` is local and returns the 20 most recently updated (`hasMore`, `offset` to page); `list_remote_drafts` reads the Drafts folder over IMAP (about 15 ms for 79 drafts).
- **Large attachments make every draft operation slower.** The store is one JSON file, so an edit reads and rewrites the whole file, attachment content of *every* draft included. In a benchmark (mocked IMAP), a tiny edit took about 1 ms normally and about 15 ms when a different draft held a 10 MiB attachment. If you edit drafts constantly, attach big files as late as possible.
- A draft cannot be edited while it is being sent, and simultaneous syncs of the same draft are serialized (so two clients cannot upload two copies).
**IMAP IDLE and the shared connection.** The server keeps one IMAP connection per account. The IDLE watcher (`PROTONMAIL_IDLE_WATCH`, on by default) waits on it, and since 2.1.25 it steps aside as soon as another operation needs the mailbox, so it no longer adds delay. Before that, with IDLE on, a single-folder search took 26–31 s and a multi-folder search did not finish.
To check a real Bridge yourself, run the read-only smoke test: `PROTONMAIL_USERNAME=… PROTONMAIL_PASSWORD=… node test/live/readonly-smoke.mjs`. It uses `PROTONMAIL_READ_ONLY=true` and a throwaway data directory, sends nothing, and prints counts and timings only.
---
## Operational notes
- `get_emails` and `search_emails` return a composite `emailId` — use it for all subsequent reads and actions.
- `search_indexed_emails` supports `from:`, `to:`, `subject:`, `label:`, `domain:` shortcuts.
- The local index lives at `PROTONMAIL_DATA_DIR/mail-index.sqlite`. Background sync and IMAP IDLE keep it warm.
- Filters on the local index ignore case and accents, and match spelling variants: `Müller` finds `Mueller`, `lodz` finds `Łódź`, `strasse` finds `Straße`. Live `search_emails` and `count_messages` go through Bridge: a query with umlauts or other non-ASCII letters also matches the spelled-out form (`Müller` finds `Mueller`), but a plain ASCII query is sent as typed, so `Mueller` does not find `Müller` there. Use `search_indexed_emails` for that.
- Audit logs live at `PROTONMAIL_DATA_DIR/audit.log`.
- Draft sync is best-effort — the local draft is always preserved even if remote sync fails.
- System folders (INBOX, Sent, Trash, Spam, Archive, All Mail) are guarded against accidental deletion.
---
## Troubleshooting
**"Wrong password" or connection refused**
Make sure you're using the **Bridge password**, not your Proton account password. Find it in the Bridge app under Account → Copy password. Bridge must be running before the MCP server or CLI can connect.
**Every tool answers "not configured"**
`PROTONMAIL_USERNAME` and `PROTONMAIL_PASSWORD` are not set for the server. Since 2.7.2 it still starts without them, lists its tools and answers each call with that message (it used to exit at once, which clients showed as "Connection closed"). Set both (the password is the Bridge password) and restart; `proton-mail-bridge-client setup-claude-desktop` does it for Claude Desktop. Any other configuration mistake, such as a bad port, still stops the server at start.
**macOS native module crash after update**
`better-sqlite3` is a native binary built for your machine. After a major Node.js upgrade or environment change, rebuild it:
```bash
proton-mail-bridge-client setup-claude-desktop
```
This reinstalls the runtime and rebuilds native modules in place.
**Claude can't see the connector**
After changing the MCP config, restart Claude Desktop fully (not just reload). Then check **`+` → Connectors → proton-mail-bridge**. If it's not there, run `proton-mail-bridge-client doctor` to validate the connection.
**Searches take ~30 seconds, or time out**
Upgrade to 2.1.25 or later. Before that, the IMAP IDLE watcher held the shared connection for its whole idle period and every other operation waited behind it (see [Performance and token cost](#performance-and-token-cost)). On a current version, a slow `search_emails` is Bridge's own `SEARCH` over a large folder: pass `folder` or `label`, or use `search_indexed_emails`.
**Folder not found when moving email**
Use `Folders/Name` for real folders (e.g., `Folders/Receipts`), not just `Name`. Labels and folders share the same namespace in Proton Bridge but are structurally different.
---
## Related projects
- **[proton-drive-mcp](https://github.com/googlarz/proton-drive-mcp)** — the companion MCP server and CLI for **Proton Drive**: upload, download, share and manage your end-to-end encrypted files from Claude. Run it next to this server to work with Proton Mail and Proton Drive in one conversation, for example by saving an attachment with `save_attachment` and then uploading it to Drive. If `PROTONMAIL_ALLOW_FILE_DOWNLOAD_DIR` points inside a folder that the Proton Drive desktop app syncs, attachments saved there also end up in Drive without any extra step.
---
## Changelog
See [CHANGELOG.md](CHANGELOG.md) for release history.
## Contributing
Bug reports and pull requests welcome: [github.com/googlarz/proton-mail-bridge-client/issues](https://github.com/googlarz/proton-mail-bridge-client/issues)
Testers wanted: some things (answering invitations from Outlook, Proton Calendar and Apple Calendar, Windows and Linux with a real Bridge, `brew install`) could not be verified by the maintainer. See [issue #32](https://github.com/googlarz/proton-mail-bridge-client/issues/32).
## License
MIT
TDQS
Scored across 96 tools
With 96 tools, many share similar purposes (e.g., multiple search, thread, delete, and send variants). The detailed descriptions cross-reference alternatives and help, but the sheer number of overlapping options creates a high risk of misselection.
All names use consistent snake_case and mostly follow a verb_noun pattern (e.g., get_emails, create_draft, send_email). A few noun-first outliers like folder_stats and top_senders slightly break the pattern.
96 tools is far beyond the typical 3-15 range for a well-scoped server. While the domain is complex, the count is excessive and many tools could be consolidated (e.g., single/bulk/thread variants).
The surface covers an exhaustive range of email lifecycle operations: sending, drafts, templates, labels, folders, search, sync, attachments, analytics, and diagnostics. Few gaps exist beyond niche features like contact management.