An MCP (Model Context Protocol) server that connects to ProtonMail via Proton Bridge, giving AI agents full email management capabilities over IMAP and SMTP.
- Proton Bridge — must be installed, logged in, and running
- Node.js >= 18
- A Proton Mail account (any paid plan — Bridge requires it)
npm installcp .env.example .envEdit .env with your Bridge credentials:
PROTON_BRIDGE_IMAP_HOST=127.0.0.1
PROTON_BRIDGE_IMAP_PORT=1143
PROTON_BRIDGE_SMTP_HOST=127.0.0.1
PROTON_BRIDGE_SMTP_PORT=1025
PROTON_BRIDGE_USERNAME=you@proton.me
PROTON_BRIDGE_PASSWORD=your-bridge-generated-passwordFinding your Bridge password: Open Proton Bridge > click your account > "Mailbox details" > copy the password. This is NOT your Proton account password.
npm run buildAdd to your Claude Desktop claude_desktop_config.json or Claude Code settings:
{
"mcpServers": {
"protonmail": {
"command": "node",
"args": ["/absolute/path/to/proton-mcp-server/dist/index.js"]
}
}
}| Tool | Description |
|---|---|
list_folders |
List all folders and labels |
create_folder |
Create a new folder (supports nesting with /) |
delete_folder |
Delete a folder (supports dryRun to preview first) |
rename_folder |
Rename or move a folder |
get_folder_stats |
Get total/unread counts for all folders |
| Tool | Description |
|---|---|
get_messages |
Paginated message list with metadata |
read_message |
Full message content by UID |
get_messages_with_snippets |
Message list with body preview snippets |
search_messages |
Search by sender, subject, date, body text — across multiple folders |
get_thread |
Get all messages in a conversation thread |
get_sender_summary |
Messages grouped by sender with counts and UIDs |
get_inbox_digest |
One-call overview: folder stats + top senders + counts |
get_unread_count |
Quick unread count for a single folder |
get_changes_since |
Stateless: returns new messages since an ISO 8601 timestamp |
| Tool | Description |
|---|---|
move_message |
Move a message to a different folder |
apply_label |
Copy a message to a label folder |
remove_label |
Remove a label from a message |
delete_message |
Move a message to Trash |
batch_move_messages |
Move up to 500 messages at once |
batch_apply_label |
Label up to 500 messages at once |
batch_delete_messages |
Trash up to 500 messages at once |
cross_folder_batch_move |
Move messages from multiple source folders to one destination |
move_by_sender |
Move all messages from a specific sender (exact envelope-address match) |
batch_move_by_senders |
Move messages from up to 100 senders in one call over a single connection |
move_by_search |
Search + move in one call |
route / batch_route |
Atomic label-and-move (avoids UID invalidation between separate calls) |
suggest_sender_routes |
Suggests routing rules based on historical sender→folder distribution (inboxOnly filters to actionable senders; limit caps output, default 50) |
| Tool | Description |
|---|---|
mark_read / mark_unread |
Toggle read status |
star_message / unstar_message |
Toggle star/flag |
batch_mark_read / batch_mark_unread |
Bulk read/unread (up to 500) |
mark_all_read |
Mark entire folder as read |
| Tool | Description |
|---|---|
send_email |
Send email via SMTP (supports HTML, CC/BCC, reply threading) |
get_attachment |
Download an attachment by part ID |
get_attachment_text |
Extract plain text from PDF or text/* attachments |
Batch behavior: All batch organize tools (
batch_move_messages,batch_apply_label,batch_remove_label,batch_delete_messages,cross_folder_batch_move,move_by_sender,batch_move_by_senders,move_by_search,batch_route) acceptdryRun: trueto preview UIDs that would be affected without mutating. All move/copy ops pre-validate folder paths and post-verify counts, returning{success, requested, moved|copied, failedUids?}.Exact sender matching:
move_by_senderandbatch_move_by_senderspost-filter IMAP's substringFROMsearch against the exact envelope address, sohit-reply@x.comnever grabsinmail-hit-reply@x.commail.Clean snippets: Snippets returned by
get_messages_with_snippetsare cleaned via mailparser (quoted-printable decoded, HTML stripped) and includehasUnsubscribe/unsubscribeOneClickflags when theList-Unsubscribeheader is present. FullunsubscribeMailto/unsubscribeHttpURLs are returned only withincludeUnsubscribeLinks: true(they can be very long).
npm run dev # Run with tsx (no build needed)
npm run build # Compile TypeScript
npm test # Run tests- Credentials are loaded from
.env(gitignored — never committed) - All input is validated with Zod schemas at the MCP boundary
- Batch operations are capped at 500 items
- Email addresses are validated before sending
- IMAP error messages are sanitized to prevent credential leakage
rejectUnauthorized: falseis used for TLS because Proton Bridge uses self-signed certificates on localhost
MIT