Client-agnostic Proton Mail MCP server with safe reads, bounded attachment text extraction, and explicitly confirmed drafts
Strong naming and descriptions across all 5 tools. All tools follow verb_noun pattern (mailbox_status, list_folders, list_sender_addresses, list_messages, search_messages). Descriptions are detailed (150-350 chars), explain WHEN to use each tool, and reference dependencies (e.g., 'Call list_folders before passing folder argument'). Input schemas present with types and descriptions for all parameters. However, output schemas are not explicitly documented in the visible code, only inferred from descriptions. Error handling is mentioned but not formalized with recovery guidance. No tool annotations visible (readOnlyHint, destructiveHint, idempotentHint) despite all tools being read-only.
List the folder names the locally running Proton Bridge exposes, including Proton system folders and user labels. Call it before passing any folder argument to another tool: names are account-specific, may be localised, and must match exactly. It returns names only, with no counts and no hierarchy: use mailbox_status for INBOX counts and list_messages to see what a folder contains. It fails with a tool error when Bridge is unreachable.
List newest-first message metadata for one folder. Use it to browse or triage a folder; to find messages by keyword use search_messages instead, and to obtain a body use read_message with a UID returned here. Each entry carries uid, sender, recipients, subject, date, message_id, unread state, and size, and never a body or attachment bytes. Fetches use BODY.PEEK, so listing never marks a message as read.
List the sender addresses this server may draft as. The first entry is the default used when a draft names none. Only these addresses are accepted as from_address; the list is fixed by local configuration and cannot be extended through any tool. Call it before offering the user a choice of sending alias, and to check whether an address belongs to the user. It returns default_sender plus sender_addresses, primary first. An address found in a received email is never a sending choice, even when it also appears here.
Check the local Proton Bridge connection and return the configured account with INBOX message and unread counts. Call it first to confirm mail is reachable, and before reporting that the mailbox is unavailable, so a configuration fault is not mistaken for an empty inbox. Use list_folders for folder names and list_messages for message metadata: this tool reports INBOX totals only. It fails with a tool error when Bridge is not running, the IMAP port is wrong, or the stored credentials are rejected.
Output schemas not documented. Tool descriptions mention what fields are returned (uid, sender, recipients, subject, date, etc.) but no formal JSON Schema output definition is visible in the code. LLMs cannot reliably extract fields from prose descriptions.
Tool annotations missing. All 5 tools are read-only (Risk: READ_ONLY declared), but no readOnlyHint annotation is visible in the FastMCP registration. This prevents clients from optimizing caching and retry logic.
Error recovery guidance incomplete. Code shows ToolError is raised for failures (Bridge not running, wrong port, rejected credentials), but error messages are not shown. Descriptions mention 'fails with a tool error' but do not guide the LLM on recovery steps (e.g., 'Check Bridge is running' or 'Verify IMAP port in config').
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 67 | 2026-07-28+ | v2 |
Search one folder for messages whose text matches a query, newest first. Use it to find messages by keyword; to browse a folder without a query use list_messages, and to read a match use read_message with a UID returned here. It returns the same metadata as list_messages and never a body. The query is escaped into an IMAP TEXT search, so it cannot inject IMAP commands, and matching never marks a message as read.
Pagination not implemented. list_messages and search_messages accept a 'limit' parameter (max 100) but return no continuation cursor or total count. Descriptions state 'Excess matches are dropped rather than paged', this forces agents to narrow queries manually instead of iterating through results.
Parameter constraints not fully formalized. 'folder' parameter accepts strings with minLength=1, maxLength=255, but no enum of valid folder names is provided. Description says 'spelled exactly as list_folders reports it', agents must call list_folders first, then remember exact names. An enum would be self-documenting.