Command Palette

Search for a command to run...

UnylyUnyly
Browse all

googlarz/proton-mail-bridge-client

FreeMaintained

Local-first Proton Mail MCP server and CLI via Proton Mail Bridge: search, read, draft, send, triage threads, answer calendar invitations, reply reminders, mult

GitHubEmbed

About

Local-first Proton Mail MCP server and CLI via Proton Mail Bridge: search, read, draft, send, triage threads, answer calendar invitations, reply reminders, multiple accounts, and a local SQLite index. Safety presets from read-only to full, with send confirmation and an outbound-to-self mode.

README

  ____  ____   ___ _____ ___  _   _   __  __    _    ___ _
 |  _ \|  _ \ / _ \_   _/ _ \| \ | | |  \/  |  / \  |_ _| |
 | |_) | |_) | | | || || | | |  \| | | |\/| | / _ \  | || |
 |  __/|  _ <| |_| || || |_| | |\  | | |  | |/ ___ \ | || |___
 |_|   |_| \_\\___/ |_| \___/|_| \_| |_|  |_/_/   \_\___|_____|
  Bridge Client  ·  CLI + Claude Desktop MCP for Proton Mail

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 — 116 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, 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.

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)

No terminal? Follow the step-by-step guide instead.

  1. Install and sign in to Proton 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 (quit Claude Desktop completely first, so it does not overwrite the config on exit):
    • Bundle (needs Node.js 24 or later on the machine): 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 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 [email protected] 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. Not sure about handing over control? Start with PROTONMAIL_MODE=drafts: Claude can read, search and prepare drafts, but nothing is sent or deleted until you change the mode. The server also tells the client how to behave (ids, accounts, never obeying instructions found inside a message).
  6. 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 116), and more accounts under Multi-account support. More recipes below in Try it.

What you get

  • Claude reads and manages your Proton Mail — triage, reply, draft, archive, search, move, batch-act on threads, pull attachments (PDF text and pictures included), answer calendar invitations
  • Safety you choose — PROTONMAIL_MODE presets (read-only, drafts, assisted, full), a send gate, confirmation for destructive actions, a per-action allowlist, and a threat model that says what is and is not covered
  • Several addresses at once — each Proton login (or each address Bridge exposes) is its own isolated account with its own index, drafts and settings
  • Fast local search — full-text search across your inbox from a local index that keeps its read state in step with the server
  • Rules that suggest themselves — a scan proposes filters and cleanup from your own mail, applies them to existing mail after a preview, and can forward by condition; see Mailbox rules
  • Automatic reply, per address, with rules — off by default, with a preview of what it would do, draft-for-approval mode and working-hours; see Autoresponder (Proton's own auto-reply may be all you need)
  • Full CLI — a dedicated command for every one of the 116 tools (plus a generic tool <name> passthrough), scriptable and pipeable; doctor checks every account and report prints a diagnostic summary for a bug report
  • Runs where you do — Claude Desktop bundle, npm, Homebrew, or a Docker image for a headless machine
  • 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, 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. 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

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); the IMAP and SMTP password is the same Bridge password
  • Username (your Proton email address)
  • Bridge password (see note above)

Install

npm (recommended):

npm install -g proton-mail-bridge-client

Homebrew:

brew tap googlarz/tap
brew install proton-mail-bridge-client
Source install (development)
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.


Connect to Claude Desktop

Run the guided setup wizard:

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

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:

Option 1 — Environment variables (simplest)
{
  "mcpServers": {
    "proton-mail-bridge": {
      "command": "proton-mail-bridge-mcp",
      "env": {
        "PROTONMAIL_USERNAME": "[email protected]",
        "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"
      }
    }
  }
}
Option 2 — File-based secrets (credentials in files, not config)
{
  "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"
      }
    }
  }
}
Option 3 — Command-based secrets (pass, gopass, or any secret manager)
{
  "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"
      }
    }
  }
}

Connect to Claude Code

Install globally, then register the server with one command:

npm install -g proton-mail-bridge-client

claude mcp add proton-mail-bridge \
  -e [email protected] \
  -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.

By default this registers the server for the current project only. Add -s user to make it available in every project:

claude mcp add proton-mail-bridge -s user \
  -e [email protected] \
  -e PROTONMAIL_PASSWORD=your-bridge-password \
  -- proton-mail-bridge-mcp

Verify it's connected:

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 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:

{
  "mcpServers": {
    "proton-mail-bridge": {
      "command": "proton-mail-bridge-mcp",
      "env": {
        "PROTONMAIL_USERNAME": "[email protected]",
        "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.


Running in Docker (headless machines)

For a server with no desktop, next to a headless Bridge. The image installs the published npm package and is published for each release to ghcr.io/googlarz/proton-mail-bridge-client (:<version> and :latest, linux/amd64 and linux/arm64). To build it yourself from this repo:

docker build -f docker/Dockerfile --build-arg VERSION=2.16.0 -t proton-mail-bridge-client .
docker run -i --rm --network host \
  -e [email protected] -e PROTONMAIL_PASSWORD_FILE=/run/secrets/bridge \
  -v /path/to/bridge-password:/run/secrets/bridge:ro -v bridge-client-data:/data \
  proton-mail-bridge-client

docker run -i speaks MCP over stdio. The container must reach Bridge's IMAP and SMTP ports (--network host, or --network container:<bridge> to share Bridge's network). The CLI is in the same image: add --entrypoint proton-mail-bridge-client and a command such as doctor. If Bridge is reached by a host name (for example host.docker.internal or another container), its self-signed certificate fails verification: export it from Bridge and point PROTONMAIL_TLS_CA_FILE at it, or list the name in PROTONMAIL_TLS_INSECURE_HOSTS. The server has no network transport of its own; reaching it from a remote client needs your own wrapper. The image was checked to build, report its version, run as a non-root user and answer an MCP initialize, and (by a tester, against a real Bridge) to pass doctor, list folders, sync and exit cleanly, with --network container:<bridge> or with Bridge reached by host name using PROTONMAIL_TLS_CA_FILE or PROTONMAIL_TLS_INSECURE_HOSTS.

Autoresponder (automatic reply per address)

Proton Mail has its own auto-reply (Settings → Filters / Forward and auto-reply; Proton lists it on its paid plans). It runs inside Proton, works while your computer is off, and applies to the whole account unless you write a filter. If that is enough, use it and leave this one off: enabling both would send each sender two replies. This one adds what a filter cannot do from a chat, and it only runs while this server runs.

An automatic reply for one address, plus a note that address appends to what it sends. Off by default, and it can send mail without asking each time, so it is held to stricter rules than a normal send:

  • Who gets a reply. Only a person (not a list, not automatic mail, not a no-reply/mailer-daemon sender) who wrote to this address (To or Cc), after the rule was switched on and inside its dates, and whom you have not already answered since. One reply per sender per replyEveryDays days (default 7), at most dailyLimit a day (default 50). A reply is recorded before it is sent, so a failure never becomes a second one.
  • When. After a background sync has indexed new mail, so only while this server runs with PROTONMAIL_AUTO_SYNC on; with several clients open, the instance holding the sync lock does it. outsideHours (with a timezone) limits it to when you are not at work.
  • What it does. mode: "send" sends; mode: "draft" leaves a draft in your drafts for you to review and send. If the server may not send (PROTONMAIL_MODE=drafts, ALLOW_SEND off, or outbound mail confined to yourself), a reply becomes a draft by itself; if it may not write drafts either (read-only), it is refused.
  • Rules, first match wins: by sender ([email protected], *@client.example), words in the subject, and whether you have written to the sender before (known: "yes"/"no"). Each gives reply, draft or skip, optionally with its own text. Mail that matches none gets the default reply.
  • Text. Placeholders {senderFirstName}, {senderName}, {senderAddress}, {subject}, {startsAt}, {endsAt}.
  • Safety headers. Auto-Submitted: auto-replied, Precedence: bulk, X-Auto-Response-Suppress: All; the reply threads under the message it answers.
  • Control. Switching it on needs confirmed: true when PROTONMAIL_CONFIRM_DESTRUCTIVE is on and the server can send. Switching it off is always allowed, in every mode.
preview_autoresponder  [email protected]  days=3        # what it WOULD do with recent mail; changes nothing
set_autoresponder  [email protected]  enabled=true  confirmed=true
                   body="Hi {senderFirstName}, I am away until {endsAt} and will answer then."
                   endsAt=2027-01-04  timezone=Europe/Warsaw  outgoingNote="I am away until 4 January."
                   rules=[{"name":"boss","from":["[email protected]"],"action":"reply","body":"Urgent? Call my mobile."},
                          {"name":"clients","from":["*@client.example"],"action":"draft"},
                          {"known":"no","subjectContains":["unsubscribe"],"action":"skip"}]
get_autoresponder  [email protected]                      # state, counts, who wrote, why mail was skipped, the log

outgoingNote is appended, after the signature, to every mail sent from that address while the rule is active (a send with appendSignature: false opts out). On the command line: set-autoresponder, autoresponder and preview-autoresponder. The rule, the log and the memory of who was answered live in autoresponder.json in the account's data directory; every account (every address Bridge exposes) has its own.

Mail firewall (prompt-injection guard)

Mail is untrusted text that a model reads. get_email_by_id and get_emails_by_ids add a risk note (caution or high, with the reasons) when a message looks written to steer an AI or to deceive you: instructions addressed to an assistant (English, Polish, German), text hidden by markup or zero-width characters, a link that reads one address and goes to another, a Reply-To on another domain, a sender name that shows a different address, a failed SPF/DKIM/DMARC check. Normal mail carries no note. The instruction patterns are also checked wherever else text reaches the model: attachment text, thread reads, lists with previews and reading a message as an MCP resource.

After a high message has been read, the tools that reach outside (send_email, replies, forward_email, send_draft, schedule_draft, delete_email, delete_thread, bulk_delete, empty_folder, unsubscribe_sender, respond_to_invite, and switching on set_rule or set_autoresponder) ask for confirmed: true and quote the reason, even when PROTONMAIL_CONFIRM_DESTRUCTIVE is off. Previews (dryRun) and switching things off pass. Rules and the autoresponder never act by themselves on a high-risk message. This narrows what a persuaded model can do; it cannot read intent (see the threat model).

Brief (what changed since you last looked)

get_brief (CLI: brief) reports from the local index how much mail came since the last brief, split into people and automatic mail, what waits for your reply, replies you are waiting for that are overdue, and suspicious mail (see the mail firewall above). markSeen moves the "since" mark, so a daily run reports only the day. To get it without opening anything, run it from your system's scheduler:

proton-mail-bridge-client brief --sync --mark-seen --notify

--notify shows the few lines as a system notification (macOS and Linux with notify-send). schedule --job brief prints the launchd, systemd, cron or Windows task definition that runs it every day (it installs nothing).

Tidy in one approval (plan_inbox)

Ask Claude to tidy the inbox and it prepares a plan: a numbered list of changes that can all be taken back (archive, move, mark read, label), from its own proposals and from a scan for automatic mail nobody reads (unread for 3 days, or read and over 14 days old). Nothing changes until you approve. approve_plan applies everything, only some numbers, or everything except some; each change goes through the same runtime policy as the single tools. undo_plan puts the applied changes back (mail returns to its folder, read marks and labels are reverted). Senders of scanned items you left out are not proposed again. A plan never contains delete or trash.

Commitments ("I'll send it by Friday")

A local ledger of what you promised and what was promised to you. find_commitments reads recent sent mail (and, if you ask, mail from people) and returns sentences that look like promises, in English, Polish and German, with the date each names ("by Friday", "do piątku", "bis Freitag", 05.11.). They are only candidates: Claude confirms with you, then set_commitment records the real ones. list_commitments shows each open entry as overdue, due today, open or probably done (the person who owed it wrote again in the thread). Overdue and due-today entries appear in the brief. Closing an entry is set_commitment with id and status. Nothing in the mailbox changes.

Style profile (drafts that sound like you)

get_style_profile works out how you write from your own sent mail, on your machine: language, how you open and close ("Dzień dobry {name},", "Cheers"), tone, typical length. It returns a few plain hints for drafting, overall and for a given recipient or company ("to [email protected] you write in Polish, formally"), and the server tells Claude to call it before drafting. The profile is cached for a week in style-profile.json and holds patterns and counts only: no sentences, no names.

Search by meaning (several wordings)

search_indexed_emails takes queries: several wordings of one question, up to 8. Claude supplies the variants (synonyms, other languages: ["deposit", "kaucja", "Kaution", "rental agreement"]), the server searches each in the local index and fuses the results, so a message found by several wordings ranks first and every hit says which wordings found it (matchedBy). There is no model and no download: the meaning comes from the model you already use. What it cannot find is mail that uses none of the words tried.

Mailbox rules (suggestions, filters, cleanup, forwarding)

Proton's own filters act on new mail, the free plan has one custom filter, and auto-delete of Trash and Spam is a paid feature with a fixed 30 days (see Proton's pages on filters and auto-delete). These rules work alongside them: on mail that is already there, with a preview, and with a scan that suggests rules instead of waiting for you to write them. They run on this server, so they only act while it runs.

Scan, then decide. suggest_rules looks through the mail in the local index and proposes, with the evidence for each: newsletters and other automatic mail you never open (archive and mark as read; or move to Trash when old), people whose mail you always file in the same folder, and an old Trash or Spam that was never emptied. Each says how risky it is (low: only moves and marks; recoverable: moves to Trash; permanent: deletes). Nothing changes until you accept one with set_rule proposalId=...; delete_rule on a proposal dismisses it for good.

A rule is when (sender or *@domain, words in the subject, folder, olderThanDays, hasAttachment, automated, unread) and actions (markRead, label, archive, moveTo, trash, delete, forwardTo). A rule needs a real condition, so it cannot touch a whole folder.

  • By itself, on new mail, after each background sync: mark read, label, archive, move, forward. Only mail that arrived after the rule was switched on, once per message.
  • Only when you ask: a rule that trashes or deletes never runs by itself. apply_rules runs the rules on mail already in the index and is a preview unless dryRun: false; deleting for good also needs confirmed: true. Every action goes through the same permissions as the other tools (PROTONMAIL_MODE, allowed actions), and a disallowed one is refused before anything is touched.
  • Forwarding (actions.forwardTo, up to 3 addresses, optional forwardNote) sends mail out on its own, so it is held to the rules of a send: sending must be allowed, the recipients must be allowed (not with PROTONMAIL_RESTRICT_OUTBOUND_TO_SELF), switching it on needs confirmed: true when PROTONMAIL_CONFIRM_DESTRUCTIVE is on, and the rule needs a condition on the sender, the subject or an attachment. It forwards mail that arrives after the rule is switched on, once per message, at most 50 a day, with the original attachments, and by default only mail from people (not lists or automatic mail). The copy carries Auto-Submitted: auto-generated and X-Forwarded-By.
suggest_rules  days=90                                        # proposals, with evidence; nothing is changed
set_rule  proposalId=p-1a2b3c4d5e                              # accept one
set_rule  name="Clients to Projects"  when={"from":["*@client.example"]}  actions={"moveTo":"Projects","markRead":false}
set_rule  name="Boss to phone"  when={"from":["[email protected]"]}  actions={"forwardTo":["[email protected]"]}  confirmed=true
apply_rules                                                    # preview of what the rules would do to existing mail
apply_rules  dryRun=false  limit=100
get_rules                                                      # rules, waiting proposals, what the rules did lately

The scan and apply_rules work from the local index (by default INBOX and Sent; run sync_emails for other folders), so check that it is fresh. Rules, proposals and the log live in rules.json in the account's data directory; each account has its own.

Multi-account support (v2.1.0+)

Use more than one Proton mailbox from the same server: either several addresses of one account (Bridge's Split Addresses feature) or separate Proton accounts signed in to the same Bridge. Each one gets its own isolated service stack, local index, and drafts storage.

Setup

In Proton Bridge, make each address or account its own login: with Split Addresses on, every address has a login; separate accounts each have their own. Bridge generates a separate password for each login, and the same password works for IMAP and SMTP. The first account is PROTONMAIL_USERNAME / PROTONMAIL_PASSWORD; PROTONMAIL_ACCOUNTS_JSON lists only the additional ones, as a JSON array on one line:

{
  "mcpServers": {
    "proton-mail-bridge": {
      "command": "proton-mail-bridge-mcp",
      "env": {
        "PROTONMAIL_USERNAME": "[email protected]",
        "PROTONMAIL_PASSWORD": "primary-bridge-password",
        "PROTONMAIL_ACCOUNTS_JSON": "[{\"address\": \"[email protected]\", \"password\": \"alias-bridge-password\"}, {\"address\": \"[email protected]\", \"password\": \"business-bridge-password\"}]"
      }
    }
  }
}

Instead of password an entry can use passwordFile (path to a file with the Bridge password) or passwordCommand (a command that prints it), so the password need not sit in the client config; use only one of the three. autoSyncFolder sets that account's folders for background sync (default PROTONMAIL_AUTO_SYNC_FOLDER). Folders in a list that an account does not have are skipped and reported as skippedFolders.

Listing the primary address again is refused at startup ("duplicate address"). Host, ports and the TLS mode default to the primary account's; an entry can override them with imapHost, imapPort, imapSecure, smtpHost, smtpPort and smtpSecure.

Note: The password is the Bridge password for each login, not your Proton account password.

How it works

  • Primary account: the PROTONMAIL_USERNAME/PROTONMAIL_PASSWORD pair. 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:

proton-mail-bridge-client list_accounts --json

Output:

[
  { "slug": "primary", "address": "[email protected]", "isPrimary": true, "index": "fresh", "imap": "ok", "smtp": "ok" },
  { "slug": "alias", "address": "[email protected]", "isPrimary": false, "index": "fresh", "imap": "ok", "smtp": "ok" },
  { "slug": "business", "address": "[email protected]", "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 [email protected] 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/.

Ready-made workflows. The server offers seven prompts that clients can show as menu items: Morning brief, Who is waiting on me, Waiting on others, Prepare for a meeting, Answer calendar invitations, Clean up newsletters and Draft replies. Each only instructs the assistant and says to ask before sending or changing anything. To get a digest written to a file every morning without opening Claude, see proton-mail-bridge-client schedule in docs/cli.md.


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:

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 [email protected] --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 (a named command for every one of the 116 tools, across read, triage, compose, mailbox actions, folders, drafts, templates, analytics, and diagnostics).


Safety controls

One switch: PROTONMAIL_MODE

If you would rather not combine the flags below yourself, pick how much the assistant may do:

PROTONMAIL_MODE Reads Drafts Flags (read, star) Archive, trash, move Send Permanent delete
read-only yes no no no no no
drafts yes yes yes no no no
assisted yes yes yes yes yes, with confirmed:true no
full (the default) yes yes yes yes yes yes

A mode is a floor: the individual flags below can make things stricter (PROTONMAIL_MODE=full with PROTONMAIL_READ_ONLY=true is read-only; assisted with PROTONMAIL_ALLOWED_ACTIONS=mark_read allows only that) but never looser (drafts with PROTONMAIL_ALLOW_SEND=true still does not send). If you want a combination a mode does not give you, leave PROTONMAIL_MODE unset and use the flags. An unknown value is an error that lists the valid ones. The mode in force is shown by get_runtime_status, and the Claude Desktop bundle has a Safety mode setting for it.

All flags work in both the MCP server and CLI:

PROTONMAIL_TOOL_TIER=core            # expose 25 core tools instead of all 116 — 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

# Credentials (required)
PROTONMAIL_USERNAME='[email protected]'
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'         # true = implicit TLS, false = STARTTLS. Unset, the server tries the other mode itself if Bridge's SMTP mode differs
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'

# Attachments
PROTONMAIL_TLS_CA_FILE=''              # PEM file with Bridge's exported certificate: trusted when Bridge is reached by a name other than localhost (another container, another machine)
PROTONMAIL_TLS_INSECURE_HOSTS=''      # comma-separated host names whose certificate is not verified, like localhost is (only for a private network you trust)
PROTONMAIL_SHORTEN_LINKS='false'     # true = long tracking links in Markdown bodies become host/… (unsubscribe, confirm, reset links are kept)
PROTONMAIL_MAX_INLINE_KB='40'         # in KB (older name PROTONMAIL_MAX_INLINE_BYTES still works, also in KB): largest attachment get_attachment_content returns inline as base64
PROTONMAIL_PDF_MAX_MB='10'            # largest PDF get_attachment_text extracts text from (1-100 MB); bigger ones are refused with a message
PROTONMAIL_PDF_MAX_PAGES='50'         # pages get_attachment_text reads from a PDF (1-2000); a longer PDF is cut off with a note saying how many pages are missing

# 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 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, save to disk, read text from PDFs
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

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

get_attachment_text reads text-like attachments and PDFs. A PDF comes back page by page, each page headed --- Page N of M ---, and the result has a pdf field with totalPages, extractedPages, omittedPages and truncated. It is limited by PROTONMAIL_PDF_MAX_MB (default 10) and PROTONMAIL_PDF_MAX_PAGES (default 50); past the page limit the text ends with a line saying how many pages were left out. A password-protected PDF, a damaged file and a scan without a text layer each fail with a short explanation. The PDF is read in memory with unpdf (pure JavaScript, no native module): nothing is written to disk, nothing is downloaded, and a document that takes longer than 30 seconds is stopped.


Using it as a library

Beyond the CLI and MCP server, the underlying service classes are importable directly:

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_KB.
  • PROTONMAIL_TOOL_TIER=core exposes 25 tools instead of 116: 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.
  • Several clients at once. Every MCP client starts its own server. Per data directory, only one of them runs the background sync and the IDLE watch (it holds background-sync.lock); the others serve tools from the index and take over when it exits. get_runtime_status shows backgroundSync.leader. A manual sync_emails / run_background_sync always runs.
  • 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:

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). 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 — 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 for release history.

Contributing

Bug reports and pull requests welcome: 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.

License

MIT

from github.com/googlarz/proton-mail-bridge-client

Installing googlarz/proton-mail-bridge-client

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/googlarz/proton-mail-bridge-client

FAQ

Is googlarz/proton-mail-bridge-client MCP free?

Yes, googlarz/proton-mail-bridge-client MCP is free — one-click install via Unyly at no cost.

Does googlarz/proton-mail-bridge-client need an API key?

Yes, it requires environment variables: PROTONMAIL_ACCOUNTS_JSON, PROTONMAIL_ALLOWED_ACTIONS, PROTONMAIL_ALLOW_EMPTY_FOLDER, PROTONMAIL_ALLOW_REMOTE_DRAFT_SYNC, PROTONMAIL_ALLOW_SEND, PROTONMAIL_ALLOW_UNSAFE_HTML, PROTONMAIL_AUTO_SYNC, PROTONMAIL_AUTO_SYNC_FOLDER, PROTONMAIL_AUTO_SYNC_FULL, PROTONMAIL_AUTO_SYNC_LIMIT_PER_FOLDER, PROTONMAIL_CLAUDE_RUNTIME_DIR, PROTONMAIL_CONFIRM_DESTRUCTIVE, PROTONMAIL_DATA_DIR, PROTONMAIL_DEBUG, PROTONMAIL_IDLE_MAX_SECONDS, PROTONMAIL_IDLE_WATCH, PROTONMAIL_IMAP_HOST, PROTONMAIL_IMAP_PASSWORD, PROTONMAIL_IMAP_PORT, PROTONMAIL_IMAP_SECURE, PROTONMAIL_IMAP_USERNAME, PROTONMAIL_MAX_INLINE_KB, PROTONMAIL_OP_DELAY_MS, PROTONMAIL_PASSWORD, PROTONMAIL_PASSWORD_COMMAND, PROTONMAIL_PASSWORD_FILE, PROTONMAIL_PDF_MAX_MB, PROTONMAIL_PDF_MAX_PAGES, PROTONMAIL_READ_ONLY, PROTONMAIL_RESTRICT_OUTBOUND_TO_SELF, PROTONMAIL_SEND_DELAY_SECONDS, PROTONMAIL_SHORTEN_LINKS, PROTONMAIL_SIGNATURE, PROTONMAIL_SMTP_HOST, PROTONMAIL_SMTP_PORT, PROTONMAIL_SMTP_SECURE, PROTONMAIL_STARTUP_SYNC, PROTONMAIL_SYNC_INTERVAL_MINUTES, PROTONMAIL_TLS_CA_FILE, PROTONMAIL_TLS_INSECURE_HOSTS, PROTONMAIL_TOOL_TIER, PROTONMAIL_USERNAME, PROTONMAIL_USERNAME_COMMAND, PROTONMAIL_USERNAME_FILE. Unyly injects them into the config during install.

Is googlarz/proton-mail-bridge-client hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install googlarz/proton-mail-bridge-client in Claude Desktop, Claude Code or Cursor?

Open googlarz/proton-mail-bridge-client on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare googlarz/proton-mail-bridge-client with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All communication MCPs