{"name":"io.github.sethbang/proton-mail-mcp","slug":"sethbang-proton-mail-mcp","title":"Proton Mail (unofficial)","description":"Unofficial Proton Mail MCP — send, read, search & organize email over SMTP and IMAP","url":"https://mcp.market/server/sethbang-proton-mail-mcp","rating":null,"grade":"B","score":83,"certified":false,"status":"active","category":"email","tags":["email","search"],"presence":{"score":36,"stars":6,"forks":1,"downloads_week":113,"last_push_at":"2026-09-24T16:54:21.000Z","license":"MIT"},"uptime":null,"claimed":false,"transport":"npm","callable_via_gateway":false,"default_price_micros":0,"repository":"https://github.com/sethbang/proton-mail-mcp","website":"https://sethbang.github.io/proton-mail-mcp/","version":"1.0.2","remotes":[],"packages":[{"registryType":"npm","registryBaseUrl":"https://registry.npmjs.org","identifier":"proton-mail-mcp","version":"1.0.2","runtimeHint":"npx","transport":{"type":"stdio"},"environmentVariables":[{"description":"Your Proton Mail email address","isRequired":true,"name":"PROTONMAIL_USERNAME"},{"description":"Your Proton Mail SMTP password (not your login password; generate one in Proton settings)","isRequired":true,"isSecret":true,"name":"PROTONMAIL_PASSWORD"},{"description":"SMTP host","default":"smtp.protonmail.ch","name":"PROTONMAIL_HOST"},{"description":"SMTP port","format":"number","default":"587","name":"PROTONMAIL_PORT"},{"description":"Use TLS for SMTP (true for port 465)","default":"false","name":"PROTONMAIL_SECURE"},{"description":"Proton Mail Bridge IMAP host (required for read/search tools)","default":"127.0.0.1","name":"IMAP_HOST"},{"description":"Proton Mail Bridge IMAP port","format":"number","default":"1143","name":"IMAP_PORT"},{"description":"Use TLS for IMAP","default":"false","name":"IMAP_SECURE"},{"description":"Proton Mail Bridge username (falls back to PROTONMAIL_USERNAME)","name":"IMAP_USERNAME"},{"description":"Proton Mail Bridge password (falls back to PROTONMAIL_PASSWORD)","isSecret":true,"name":"IMAP_PASSWORD"},{"description":"Disable all mutating tools (send, move, delete, flags, labels, bulk ops, folder/label management)","default":"false","name":"READONLY"},{"description":"Refuse live sends to any recipient other than PROTONMAIL_USERNAME (for throwaway/QA accounts)","default":"false","name":"RESTRICT_OUTBOUND_TO_SELF"},{"description":"Allowlist directory enabling download_attachment.saveTo; when unset, attachments return as inline base64","name":"ALLOW_FILE_DOWNLOAD_DIR"},{"description":"Register the empty_folder tool (not recommended)","default":"false","name":"ALLOW_EMPTY_FOLDER"},{"description":"Enable verbose logging to stderr","default":"false","name":"DEBUG"}]}],"tools":[{"name":"bulk_delete","description":"Delete multiple messages in one operation. Provide EITHER `uids` OR `match`. By default soft-deletes to Trash; pass `permanent: true` to expunge. `permanent: true` ALSO requires `confirm: true` (the expunge is irreversible — there is no Trash to recover from). `dryRun: true` previews without deleting and needs no confirmation.","write_action":true,"price_micros":0,"input_schema":null},{"name":"bulk_move","description":"Move multiple messages to a different folder in one operation. Provide EITHER `uids` (an explicit list) OR `match` (search criteria — same shape as search_messages), not both. Set `dryRun: true` to preview what would be moved without making changes. Note: moved messages get new UIDs in the destination folder.","write_action":false,"price_micros":0,"input_schema":null},{"name":"bulk_update_flags","description":"Add or remove flags on multiple messages in one operation. Provide EITHER `uids` OR `match`, plus at least one of `flagsToAdd` / `flagsToRemove`. Same flag whitelist as update_message_flags.","write_action":true,"price_micros":0,"input_schema":null},{"name":"bulk_update_labels","description":"Add or remove Proton labels on many messages in one operation. Provide EITHER `uids` OR `match` (XOR), plus at least one of `labelsToAdd` / `labelsToRemove`. Same label-path rules as `update_message_labels` (must start with \"Labels/\"). Supports `dryRun: true` for safe preview.","write_action":true,"price_micros":0,"input_schema":null},{"name":"count_messages","description":"Count messages in a folder matching optional search criteria. Returns just a number (no envelopes fetched). The attachment filters (`hasAttachment`, `attachmentName`, `attachmentType`) are rejected here — they require an envelope scan that defeats the count's speed promise. Use search_messages for attachment-based filtering. A non-selectable namespace container (e.g. `Folders`/`Labels`) is rejecte","write_action":false,"price_micros":0,"input_schema":null},{"name":"create_folder","description":"Create a new mailbox folder. Returns gracefully if the folder already exists. On Proton Mail, folders must be created under the \"Folders/\" namespace (e.g. \"Folders/Receipts\") — root-level paths are rejected by the server with an actionable error.","write_action":true,"price_micros":0,"input_schema":null},{"name":"create_label","description":"Create a new Proton label. Pass the bare label name (e.g. \"Important\") — the tool prepends the \"Labels/\" namespace internally. Labels are non-exclusive tags: a message can carry many labels in addition to living in one folder. Apply or remove labels on messages with `update_message_labels`. Idempotent — succeeds silently if the label already exists.","write_action":true,"price_micros":0,"input_schema":null},{"name":"delete_folder","description":"","write_action":true,"price_micros":0,"input_schema":null},{"name":"delete_message","description":"Delete an email message. By default moves to Trash for safety; set permanent=true to permanently expunge. Note: moving to Trash assigns a new UID in the Trash folder — the original UID is no longer valid. **UID + folder pair caveat**: IMAP UIDs are per-folder. Always pair a UID with the folder it came from; the same integer can refer to different messages in INBOX, Sent, Trash, and All Mail.","write_action":true,"price_micros":0,"input_schema":null},{"name":"delete_thread","description":"Delete every message in a thread. Default soft-deletes to Trash; permanent:true expunges. acrossFolders:false by default for safety. dryRun:true previews.","write_action":true,"price_micros":0,"input_schema":null},{"name":"download_attachment","description":"Download an email attachment by part number. Use read_message or list_attachments first to see available attachments and their part numbers. By default returns base64-encoded content inline (read-only). When `saveTo` is provided AND the ALLOW_FILE_DOWNLOAD_DIR env var is set, this tool WRITES the decoded bytes to that path inside the allowlist root and returns the file path + size instead of base6","write_action":false,"price_micros":0,"input_schema":null},{"name":"empty_folder","description":"Permanently delete ALL messages in a folder (atomic UID EXPUNGE via messageDelete). Requires `confirm: true` (skipped for `dryRun`). By default restricted to Trash/Junk; pass `allowAnyFolder: true` to empty other folders. THIS IS NOT REVERSIBLE. Pass `dryRun: true` to preview the count that would be deleted without touching any mail. Disabled unless ALLOW_EMPTY_FOLDER=true is set in the environmen","write_action":false,"price_micros":0,"input_schema":null},{"name":"flag_thread","description":"Add or remove flags on every message in a thread, identified by Message-ID. Use this instead of update_message_flags when you want the change applied to a whole conversation, or bulk_update_flags when you have a flat set of UIDs rather than a thread. At least one of flagsToAdd/flagsToRemove must be non-empty. acrossFolders:false by default. dryRun:true previews.","write_action":false,"price_micros":0,"input_schema":null},{"name":"folder_stats","description":"Return aggregate stats for a folder: total/unread (free), plus scanned-envelope aggregations (oldest/newest/total bytes). Default scanLimit 5000, max 20000. Response always includes scanned/truncated so callers can detect partial results. A non-selectable namespace container (e.g. `Folders`/`Labels`) is rejected with an actionable error rather than reporting empty stats.","write_action":false,"price_micros":0,"input_schema":null},{"name":"forward_email","description":"","write_action":true,"price_micros":0,"input_schema":null},{"name":"get_thread","description":"Get all messages in a conversation thread by walking In-Reply-To and References headers. Returns messages sorted chronologically (oldest first).","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_attachments","description":"List attachment metadata for a message without downloading the body. Returns part numbers, filenames, content types, and sizes — use these with download_attachment to fetch the content.","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_folders","description":"List available email folders/mailboxes with message counts. The per-folder counts come from a cached IMAP STATUS that Proton Mail Bridge can serve stale — do NOT treat them as authoritative for decisions like \"is this folder empty before deleting\". Use `count_messages` or `folder_stats` (both SELECT+SEARCH the live mailbox) when you need an exact count.","write_action":false,"price_micros":0,"input_schema":null},{"name":"list_messages","description":"List recent messages from an email folder, sorted by date (newest first). Returns subject, sender, date, and flags for each message. A non-selectable namespace container (e.g. `Folders`/`Labels`) is rejected with an actionable error rather than returning an empty list. **Pagination note:** the default date sort is paginated by a UID cursor (`beforeUid`). In folders where UID order disagrees with d","write_action":false,"price_micros":0,"input_schema":null},{"name":"mark_all_read","description":"Mark all unread messages in a folder as read. Optionally limit to messages older than a given date. Pass `dryRun: true` to preview the affected count without flipping any flags.","write_action":false,"price_micros":0,"input_schema":null},{"name":"move_message","description":"Move an email message to a different folder. Note: the message gets a new UID in the destination folder — the original UID is no longer valid after the move. **UID + folder pair caveat**: IMAP UIDs are per-folder, so UID 42 in INBOX and UID 42 in Sent identify different messages. Always carry the folder a UID came from; never reuse a UID across folders. For thread-level operations on messages you ","write_action":false,"price_micros":0,"input_schema":null},{"name":"move_thread","description":"Move every message in a thread to a destination folder. By default acts only in the seed message's folder; pass acrossFolders:true to walk INBOX/Sent/All Mail. dryRun:true previews the affected per-folder UIDs without moving.","write_action":false,"price_micros":0,"input_schema":null},{"name":"read_message","description":"","write_action":false,"price_micros":0,"input_schema":null},{"name":"rename_folder","description":"Rename a mailbox folder or label. Errors if the source path does not exist. Works for both \"Folders/\" and \"Labels/\" paths.","write_action":false,"price_micros":0,"input_schema":null},{"name":"reply_all_email","description":"Reply to all recipients of an email (sender + original TO + original CC), excluding the authenticated user. Sends with proper threading headers. Equivalent to `reply_email` with `replyAll: true`, exposed as a dedicated tool for discoverability. Response leads with `[sent-copy:verified|unverified]`; like `reply_email`, the `[reply-to:*]` tokens do not apply because there's no `replyTo` parameter to","write_action":true,"price_micros":0,"input_schema":null},{"name":"reply_email","description":"Reply to an email message. Reads the original message and sends a reply with proper threading headers (In-Reply-To, References). Response leads with a `[sent-copy:verified|unverified]` token; the `[reply-to:*]` family of tokens does NOT apply here because this tool doesn't accept a `replyTo` parameter — there's no requested Reply-To to verify against. If you need Reply-To control or rewriting dete","write_action":true,"price_micros":0,"input_schema":null},{"name":"save_draft","description":"Save an email as a draft without sending it. The draft is placed in the user's `\\Drafts` special-use folder (resolved at runtime; falls back to literal `Drafts` if no annotation). The destination is intentionally not caller-controlled — prior versions accepted an arbitrary `folder` parameter that allowed planting `\\Draft`-flagged messages in INBOX or other paths, which was confusing to anyone scan","write_action":false,"price_micros":0,"input_schema":null},{"name":"search_messages","description":"Search for messages in a folder by various criteria (sender, subject, date, flags). Returns matching message summaries sorted by date (newest first). Note: recently sent or received messages may take a few seconds to become searchable by subject or body due to server-side indexing delays; searching by 'from' is typically immediate. A non-selectable namespace container (e.g. `Folders`/`Labels`) is ","write_action":false,"price_micros":0,"input_schema":null},{"name":"send_email","description":"Send an email using Proton Mail SMTP. HTML bodies are sanitized through a conservative allowlist by default (v1.0.0: `sanitizeHtml` defaults to true) — scripts, event handlers, inline styles, and remote `<img>` beacons are stripped. Pass `sanitizeHtml: false` to send full-fidelity HTML in trusted-content workflows. Plain-text bodies pass through unchanged.","write_action":true,"price_micros":0,"input_schema":null},{"name":"top_senders","description":"Return a frequency table of top senders for a folder, optionally filtered by date range. Buckets are keyed by lowercased email address. Default limit 20, scanLimit 5000 (max 20000). Each row carries a `direction` of \"self\" or \"received\" so callers can distinguish messages from the authenticated user (typical when scanning \"All Mail\", which spans Sent). **v1.0.0 default change**: `excludeSelf` now ","write_action":false,"price_micros":0,"input_schema":null},{"name":"update_message_flags","description":"Add or remove flags on an email message. System flags (RFC 3501): \\\\Seen (read), \\\\Flagged (starred), \\\\Answered, \\\\Draft, \\\\Deleted, \\\\Recent. User-defined keywords without a backslash prefix are also accepted (alphanumeric + underscore, e.g. \"Important\", \"Custom_Tag\"), but Proton Mail Bridge has been observed to silently drop user keywords — any flags the server did not actually apply are report","write_action":true,"price_micros":0,"input_schema":null},{"name":"update_message_labels","description":"Add or remove Proton labels on a message. Labels live under the \"Labels/\" namespace and are additive — the message stays in its source folder while gaining or losing label tags. Pass full paths in `labelsToAdd` / `labelsToRemove` (e.g. [\"Labels/Important\", \"Labels/Work\"]). Adds are strict: copying to a missing label throws \"Label not found\" (create it first with `create_label`). Removes are idempo","write_action":true,"price_micros":0,"input_schema":null}],"scan":{"score":83,"grade":"B","scanned_at":"2026-09-27T12:18:24.752Z","report":{"scannerVersion":"0.1.9","scannedAt":"2026-09-27T12:18:24.760Z","components":{"code":{"score":25,"max":25,"notes":["6 source files scanned"]},"reliability":{"score":-1,"max":20,"notes":["no gateway calls yet and no remote to probe"]},"poisoning":{"score":-1,"max":15,"notes":["tools not inspected (local package is not executed); not counted"]},"auth":{"score":6,"max":15,"notes":["static API keys via environment variables"]},"maintenance":{"score":15,"max":15,"notes":["last push 3 days ago"]},"identity":{"score":8,"max":10,"notes":["registry namespace matches repository owner","GitHub account older than a year"]}},"findings":[],"inputs":{"packages":[{"registryType":"npm","identifier":"proton-mail-mcp","version":"1.0.2","found":true,"license":"MIT","hasInstallScripts":false,"dependencyCount":7,"publishedAt":"2026-05-30T13:36:31.576Z","repositoryUrl":"git+https://github.com/sethbang/proton-mail-mcp.git","weeklyDownloads":113}],"repo":{"found":true,"owner":"sethbang","repo":"proton-mail-mcp","archived":false,"pushedAt":"2026-09-24T16:54:21Z","stars":6,"forks":1,"openIssues":2,"ownerType":"User","ownerAvatarUrl":"https://avatars.githubusercontent.com/u/95317728?v=4","ownerCreatedAt":"2021-11-30T21:41:41Z","license":"MIT"},"icon":{"url":"https://avatars.githubusercontent.com/u/95317728?v=4&s=128","source":"github"},"presence":{"stars":6,"forks":1,"downloadsWeek":113,"license":"MIT","lastPushAt":"2026-09-24T16:54:21.000Z","score":36}}}},"grade_history":[],"reviews":[]}