proton-mcp
This local MCP server connects AI assistants to Proton Mail via Bridge, providing tools to read, search, organize, draft, and send emails securely.
Read & Search: List folders/labels, list messages, search by criteria (text, subject, sender, recipient, date, read/starred state, size), read messages as plain text (HTML converted), and fetch text attachments (binary refused).
Organize: Apply/remove labels, move messages between folders, mark read/unread or starred, and trash messages (no permanent deletion).
Draft & Send: Create, update, reply to, forward, and list drafts. Send new messages, replies, forwards, or drafts, with mandatory user confirmation.
Configuration & Security: Open a local web UI for sign-in, connection status, and credential management. Runs entirely locally with certificate pinning. Supports read-only mode. No access to calendar, contacts, Proton-specific features, or binary attachments.
Provides tools for interacting with Proton Mail, including reading emails, listing folders, searching messages, and retrieving attachments through the local Proton Mail Bridge.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@proton-mcpsearch for unread emails from last week"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
proton-mcp
An MCP server for Proton Mail. It talks to a locally running Proton Mail Bridge and makes your mailbox available to AI assistants such as Claude.
Status: under construction. Reading works and can be used, and so does signing in through the browser. Labels, moving between folders, read state, trash, drafts and sending all work. Every send asks you first, and that question is enforced by the server rather than requested in a prompt. See Status for the details.
Why everything runs locally
Proton Mail Bridge only listens on 127.0.0.1. It cannot be reached from outside, and that is by design: your messages are decrypted on your own machine.
This server therefore runs on your machine as well, started by the AI client as a child process. Your mail leaves your machine only as far as you show content to the assistant yourself. There is no remote access and no service in between.
Related MCP server: ProtonMail MCP Server
Status
What works
Tool | What it does |
| Lists folders and labels, grouped by kind |
| Headers of a mailbox, newest first, with paging. In "All Mail" the discarded ones are marked as such |
| Full text, subject, sender, recipient, date range, read state, star, size. Dates are the sender's, matching what the results show |
| One message as readable text, HTML converted, budgeted |
| One attachment by index, textual types only |
| Applies an existing label. The message stays in its folder |
| Removes a label. Folder, other labels and the message itself are untouched |
| Moves messages into a folder. Labels survive the move |
| Marks read or unread and sets or clears the star |
| Moves messages to the trash, which is what deletion means here |
| Writes a draft. Nothing is sent |
| Replaces a draft, keeping its id |
| Prepares a reply as a draft, with the original quoted |
| Prepares a forward as a draft, attachments and all |
| The drafts, newest first |
| Composes and sends. Asks you first, always |
| Replies and sends, keeping the conversation intact |
| Forwards and sends, attachments and all |
| Sends a draft that is already written |
| Hands back the address of the local web interface, with the token that opens it |
| Asks npm whether a newer version exists. The only thing here that contacts anything but your own machine, and only when called |
Every tool that composes a message takes either text or html, never both: Proton drops the plain text half of a message that carries markup, so the half you confirmed would be the half that never arrived.
They also take a markupLevel. At standard, the default, a message may use colour, background, font, alignment, spacing, borders and size, plus links, images, tables and lists; anything else is refused with a reason rather than quietly removed. At extended every CSS property is permitted, including ones that can put content out of sight. Nothing is refused there for being a style: what was used is reported instead, the confirmation carries a warning telling you to open the preview, and the preview names every property, its value and the element it sat on. A proper button needs extended, because a link cannot fill its own padding without display: inline-block.
The server also tells an assistant how it works, so the rules are not something a model has to infer from failures. A short text travels with every connection naming the four things that go wrong quietly, and two longer guides can be read on demand: proton-mcp://guide/writing for composing messages and proton-mcp://guide/bridge for the measured behaviour of the Bridge itself.
Underneath: a held IMAP connection that recovers from a Bridge restart, stable identifiers based on the Message-ID, HTML to text conversion, filtering of the public key Proton attaches to every sent message, and a character budget so a single message cannot exhaust a context window.
There is also a local web interface, on 127.0.0.1 only, running for as long as the server does. It is divided into sections, reached from a rail on the left:
Section | What is there |
Overview | Connection, address, where the password is kept, read-only mode, both Bridge ports, uptime and counters. State only: every control lives in the section it acts on |
Folders and labels | On its own page, only when asked. They belong to the whole account, so a folder named after a bank gives that away on its own |
Bridge | The ports, the pinned certificate, and a button that tries the connection. Ports are stored in |
Credentials | Where the Bridge password is kept, and signing out, which clears it from every store at once |
Activity | What is running and for how long, plus the last 50 finished calls. Tool names and timings only, never arguments |
One more appears only while a message is waiting for you to confirm it, linked from that question: the message rendered as the recipient will see it, every address in full, every alt text, every attachment. It is the one place this interface shows mail, and it carries no confirm button by design.
Before signing in there are two more: the sign-in form, where the address and Bridge password go into the browser rather than into a client configuration or a conversation, with four places to keep the password and the cost of each stated; and the unlock page, which after a restart asks for the master password alone, since the Bridge password is already on disk.
None of it uses JavaScript. The Content-Security-Policy forbids it, so navigation is links and anything interactive is a form control. It follows the system light or dark setting, which needs no script either.
When a tool is called before anyone has signed in, the server does not ask for the password in the conversation. It returns the address of that page, and on clients that support URL elicitation it asks the client to open it directly.
What does not exist yet
This table says what the server cannot do today. The issue behind each entry says what is planned and how far it has got.
Missing | Tracked in |
Attachments composed from files on this machine | |
Choosing per tool what an assistant may do | |
Choosing in the interface how much markup a message may carry. The levels exist and a caller picks one per message; what is missing is a ceiling the caller cannot raise | |
A fixed extra recipient the assistant cannot remove |
Binary attachments are missing from that table on purpose. They are not an unfinished feature waiting for its turn: get_attachment refuses anything that is not text and says why. Handing one over would mean either base64 in the context window, which is unusable, or writing decrypted content to your disk, which this server does not do. Changing that needs a decision first, not an implementation, so there is nothing to track yet.
What will never exist
These limits come from the Bridge itself and cannot be worked around:
No calendar, no contacts. Neither flows through the Bridge.
No Proton-specific features. No message expiration, no password-protected messages to outside recipients.
No access to filters, forwarding rules or account settings. This is also a deliberate security boundary, see below.
No server-side threading or sorting. The Bridge supports neither
THREADnorSORT, so ordering happens in this server, which costs one date read per message before a page can be cut out.No permanent deletion. Deleted will mean moved to trash, on purpose.
Requirements
A paid Proton plan. The Bridge is not included in the free tier.
Proton Mail Bridge, installed, running and unlocked. The official application expects a desktop. On a machine without one, proton-mail-bridge-docker runs it in a container instead. That is a way to get a bridge, not a way to put one somewhere else: it decrypts your mail wherever it runs, so a bridge on another machine means your mail is decrypted on that machine. The section above still applies, and the container's own readme says the same.
Node.js 24 or newer.
The Bridge password, which is not your Proton account password. The Bridge generates one per account. You find it in the Bridge application under the account, or in a terminal via
protonmail-bridge --cliand theninfo.
Installing
Hand it to your assistant
If you already have an AI assistant with a shell, the shortest route is to let it do the work:
Fetch https://raw.githubusercontent.com/RndmJoker/proton-mcp/main/prompt.md and follow itIt checks the prerequisites, registers the server with your client and tells you what to do next. It never asks for your Bridge password and cannot: that goes into the local web interface, in your browser, and nowhere else.
You can also just ask your assistant whether there is an update: the server has a check_for_updates tool that asks npm and reports what to do. It is the one thing in here that contacts anything other than your own machine, it runs only when called, it sends nothing about you, and it installs nothing.
Or hand over the guide, the same way as for the installation:
Fetch https://raw.githubusercontent.com/RndmJoker/proton-mcp/main/update.md and follow itIt works out how you installed it, since the three ways look identical from the outside and are updated differently, and it reminds you that the client has to be restarted. Nothing has to be set up again: credentials, the pinned certificate and the ports all survive.
From npm
npx @rndmjoker/proton-mcpThat is also the command to register with your client, so nothing has to be installed permanently. The published package carries provenance, so npm can show which commit and which workflow built it.
To register it with Claude Code:
claude mcp add proton-mcp -- npx -y @rndmjoker/proton-mcpFor Claude Desktop, Cursor and anything else that reads a JSON file:
{
"mcpServers": {
"proton-mcp": {
"command": "npx",
"args": ["-y", "@rndmjoker/proton-mcp"]
}
}
}Nothing else belongs in that entry. Credentials are not passed here, see Signing in.
From this repository
To run the current development state, or to change something. A setup script does the work on either platform.
Linux and macOS
git clone https://github.com/RndmJoker/proton-mcp.git
cd proton-mcp
./scripts/setup.shWindows
git clone https://github.com/RndmJoker/proton-mcp.git
cd proton-mcp
.\scripts\setup.ps1The script checks that Node.js is new enough, installs the dependencies, builds the server, runs the tests, and creates the credentials file with placeholders if it does not exist yet. It prints the exact line to register the server with your client. It never touches an existing credentials file.
Updating
Same script, one flag:
./scripts/setup.sh --update # Linux and macOS.\scripts\setup.ps1 -Update # WindowsThat pulls the latest commits, then installs, builds and tests again. If you have uncommitted changes in the working copy, the script stops instead of discarding them.
Signing in
Nothing needs to be entered before the first start. Register the server with your client, then ask the assistant anything about your mail. It will answer with a 127.0.0.1 address, or open it for you if your client supports that. Sign in there, in your browser, and repeat the request.
The page asks for two things: your Proton address and the Bridge password, which is the one the Bridge generates and not your Proton account password.
Then it asks where the password should be kept. All four options are supported and none is a fallback; what differs is the drawback you accept:
Option | On disk | You type something | Suits |
System keyring | Protected by the operating system | Rarely | A desktop machine you use yourself. The ordinary case, and the default when it works |
Encrypted file | Encrypted with your master password | Every server start | Servers, containers, headless machines. The only option that behaves the same everywhere |
This session only | Nothing | Every server start | A shared machine, or a one-off look |
Plain file | Unencrypted, readable by your account | Never | Automation, where nobody can type a password |
The interface probes the keyring at runtime rather than guessing from the operating system, because the same Linux with and without a graphical session is a different machine as far as a keyring is concerned. An option that cannot work is shown greyed out with the actual reason.
If you pick the encrypted file, every later start shows an unlock page asking for that master password alone. Forget it and nothing anywhere can recover the file; the page offers to throw it away so you can sign in afresh.
Without a browser
Environment variables still work and take precedence over anything stored:
BRIDGE_USER=your.address@example.com
BRIDGE_PASS=the-password-the-bridge-generatedPut them in ~/.config/proton-mcp/env (on Windows %USERPROFILE%\.config\proton-mcp\env), which the setup script creates with placeholders and which the server reads itself. Do not put them in your AI client's configuration, where they would sit in plain text and be read on every start. The file is created readable only by you, and the server warns on startup if that stops being true.
All variables are documented in .env.example.
Registering it with a client
Claude Code:
claude mcp add proton -- node /absolute/path/to/proton-mcp/dist/server.jsAny client that reads a JSON configuration (Claude Desktop, Cursor and others):
{
"mcpServers": {
"proton": {
"command": "node",
"args": ["/absolute/path/to/proton-mcp/dist/server.js"]
}
}
}Use an absolute path; ~ is not expanded by every client. The setup script prints the correct line for your machine.
Checking that it works
Without any client, by speaking the protocol directly:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"check","version":"1"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_folders","arguments":{"kind":"system"}}}' \
| node dist/server.jsYou should see your system mailboxes. If instead you get a message saying the Bridge is probably not running, start and unlock the Bridge and try again. The server explains what to do in its error messages rather than printing error codes.
Security
The content of someone else's email is text written by strangers, and it is handed to a language model that can call tools. A crafted message may try to instruct the assistant. This server assumes that will happen:
Message content is marked as untrusted in every answer, enclosed in markers and separated from anything the server itself says. The markers carry a label drawn per answer, because a fixed one is written down in this repository: a sender could put the closing marker in the body and have everything after it read as though the server wrote it. A label that changes every time cannot be copied. For the same reason a subject, a display name or an attachment name never breaks across lines, however many line breaks it was sent with; the break is shown, not removed.
Nothing is filtered out of the content and no attempt is made to detect attacks. Such filters pretend to offer safety they cannot deliver.
Sending always requires your confirmation, enforced in the server rather than in a system prompt. There is no switch to turn it off, and a client that cannot ask the question cannot send.
There will be no permanent deletion, and no tools for filters, forwarding, account or key settings. A hijacked session must not be able to leave behind lasting access.
HTML is never passed through raw and external content is never fetched. Tracking pixels stay ineffective.
No message ever touches your disk. No cache, no index, no copies. Anything this server wrote down would be your mail in plain text, outside the encryption you pay Proton for.
Writing to your mailbox
Every tool that changes something is bounded on purpose:
PROTON_MCP_READ_ONLY=truerefuses every write, and the interface reports the mode it is in. Reading keeps working.Nothing is ever deleted for good.
trash_messagesmoves messages to the trash, where Proton keeps them and wheremove_messagescan take them back out. There is no tool that empties the trash.Removing a label is an expunge inside the label's own mailbox. The same command aimed at a folder would delete mail, so the code refuses any path outside
Labels/, checked again in the line immediately before the expunge. A label name containing a path separator is rejected rather than repaired.Labels are never created, only applied. Create them in Proton itself.
A label is not a destination for
move_messages. Moving a message into a label was measured to apply the label and leave the message where it is, so the tool refuses it instead of reporting a move that did not happen.A batch touches at most 50 messages, and every one of them is accounted for in the answer, including the ones that failed and why.
A draft is the safe default. The assistant prepares, you decide.
Messages can be plain text or formatted. A formatted one may use headings, paragraphs, emphasis, lists, tables, links and images, with colour, font, alignment, spacing and borders. Images work the ordinary way: an <img> at an https address, at any size, inside a link, animated if you like. Only a data: address is refused, because it would carry a whole file inside the markup where nothing lists it.
Anything outside the permitted set is refused with a reason rather than quietly removed, because a message that was silently altered is no longer the message anyone agreed to. The refusal names the way forward, which is usually markupLevel: "extended".
Formatting changes what a confirmation has to show, and that is the interesting part. In plain text a link's visible text and its target are the same string, so showing you the body shows you everything. In markup they are two strings, which is the shape of every phishing mail ever written. Neither fits in a dialog, so both live on the preview page, never shortened:
Every address in the message, links and images alike, each with the text it is shown as.
Text a recipient can read that the body does not show. Measured: an image's alt text never appears in the converted body, and mail clients block remote images by default, so the alt text is frequently what the recipient actually reads. That was a way past the confirmation, and it is closed.
Every property beyond ordinary formatting, when
extendedwas used, with the ones that really put something out of sight marked as such.
What no level permits is style blocks, script, event handlers, data: addresses and comments. Comments look inert and are not: Outlook runs conditional ones, so a <style> block inside a comment reaches those recipients as markup while the confirmation and the preview see a comment. Addresses are checked wherever they appear, a CSS background: url(...) included, and every one of them is listed on the preview page. The line runs between describing a document and running inside it or reaching out of it: a <style> in the quote of a reply restyles the answer written above it, and 66 percent of real formatted mail carries one. Attachments from files on this machine are deliberately not supported either: a path named by a model, read by the server and carried out by the next confirmation is not a feature, it is a way out for anything on your disk. Forwarding loses nothing all the same, because the original message travels along whole, embedded images included.
One thing worth knowing about drafts: Proton rewrites the thread headers of anything it stores. A reply is built with In-Reply-To and References, and what comes back has neither, only Proton's own internal thread id. Threading a stored draft is therefore Proton's business rather than this server's.
Writes need a moment to settle. A move takes roughly fifteen seconds to reconcile with Proton, and until then the Bridge lists the message in both places, so the tools say so rather than reading back an intermediate state and calling it the result. Flags are the exception: those hold immediately.
Sending, and the question you cannot switch off
Mail is text written by strangers, handed to a model that can call tools. A message can politely ask to be forwarded somewhere, and other projects answer that with a line in a system prompt. A system prompt is not a security boundary. This one lives in the server:
Nothing is sent without a person saying yes. The first call never sends. It returns a question through your client naming the sender, every recipient separated into To, Cc and Bcc, and the subject. Only the second call, carrying your answer, sends.
The question is short, and the message is somewhere with room. It carries no part of the message itself; it links to a page in the local interface where you can read it as the recipient will see it, with every address in full. That split exists because the question used to grow with the message: 74 lines for a newsletter-shaped one, at which point the confirm button sat below the bottom of the dialog and nothing could be sent at all.
Recipients are never summarised, however many there are. Seeing three of eight would mean agreeing to a send to five strangers.
The answer goes through your client, never through the browser. That page has no confirm button and will not get one: the address of the interface reaches your assistant, so a control there could be operated by the thing it is supervising.
There is no setting that turns the question off. Not an environment variable, not an argument, not a mode.
A client that cannot ask cannot send. If your client does not support form elicitation, sending is refused and you are pointed at drafts instead. That is deliberately different from the sign-in prompt, which falls back to text: the worst outcome there is an inconvenience, and here it is a message that cannot be recalled. What counts as "can ask" is the capability your client declares, read from the request when the protocol revision carries it there and from the handshake otherwise.
Your yes covers one specific message. A fingerprint of the sender, every recipient, the subject and the body is sealed with an HMAC whose key exists only in the running process, and it travels with the question. On the way back the fingerprint is recomputed from what is being asked for now. If a single address was added in between, nothing is sent. A confirmation is also bound to the tool that asked, so one cannot be reused for another.
Blind copies stay blind. They travel in the envelope and never appear in the headers the recipients see. Verified against a real delivery.
The Bridge password is protected on the way out too. SMTP is verified against the same pinned certificate as IMAP, and STARTTLS is required rather than merely attempted.
Replying and forwarding exist as sending tools of their own, not just as drafts. The reason is measured: Proton rewrites the thread headers of anything it stores, so a reply written to a draft and sent later loses the chain, while a reply handed straight to SMTP keeps its In-Reply-To and stays in the conversation.
After a send, nothing checks its own work. The Bridge took about six and a half seconds to accept a message and the message appeared over IMAP about three seconds after that, so an immediate look would describe a state that is not the outcome.
What lands on your disk
Only these, all under ~/.config/proton-mcp/ (on Windows %USERPROFILE%\.config\proton-mcp\) and all written readable by your account alone:
File | When | What is in it |
| On the first connection | The Bridge's certificate and its fingerprint |
| When you change the ports in the interface | Two port numbers |
| Only if you chose the encrypted file | Your Bridge password, encrypted with your master password |
| Only if you put it there yourself | Your Bridge password in plain text |
Choosing the system keyring puts the password where the operating system keeps your other passwords, and nothing of it in a file. Choosing "this session only" writes nothing at all.
No mail, no subjects, no sender addresses, no folder names, no search terms. The activity list in the interface records tool names and timings, in memory only, and is gone when the server stops.
The web interface
It binds to 127.0.0.1 and there is no option to change that. It is not remote access and cannot be made into it. Four defences, each against a specific attack:
A token in the URL, generated per process and never written down. Any other program on your machine can reach
127.0.0.1; without the token it can do nothing here.A Host header check. A page on the internet can point its own domain at
127.0.0.1and have your browser send requests there, which the browser then treats as same-origin with that domain. Only loopback names are accepted. This is DNS rebinding, and it is the attack local servers most often forget.An Origin check and a per-action CSRF token on everything that changes something, so a token for one action cannot be replayed against another.
A strict Content-Security-Policy. Nothing may be loaded from anywhere and nothing can reach the network. The interface also serves no files from disk at all, which removes path traversal as a category rather than guarding against it.
It shows no message content, with exactly one exception: a message waiting for you to confirm it. That page renders it as the recipient will see it, and lists every address, alt text and attachment. The message is held in memory only, dropped as soon as you answer either way, and gone after fifteen minutes. Somebody else's markup, which a reply carries as its quote, is rendered in a frame with no permissions at all, and images from the internet are not loaded: a tracking pixel must not tell its author that you looked.
What you actually expose
In Proton, folders and labels belong to the account, not to an individual address. If you connect the server using a secondary address, the complete folder structure of your account is still visible, even though the messages inside belong to the other address and stay hidden.
The names alone say something. A folder Banking/Revolut reveals your bank, Tax your dealings with the tax office, a company name your employer. Separating both content and structure requires a separate Proton account, not just a second address.
The Bridge certificate
The Bridge generates its own certificate and keeps it inside its encrypted vault rather than on disk, so it cannot be trusted in advance. Switching verification off would mean anything that manages to listen on the port gets your Bridge password.
Instead the certificate is pinned on first use. The first connection records its fingerprint in ~/.config/proton-mcp/known-certificate.json and reports it on stderr. The web interface shows it too, along with the date it was recorded. Every connection after that is verified against that one certificate, which is stricter than the public trust store: a certificate signed by a recognised authority would not do.
If the certificate ever changes, the server refuses to connect and says so:
The certificate of the Bridge at 127.0.0.1:1143 has changed. Refusing to connect.
expected: 93:87:F5:3D:...
received: 11:22:33:44:...Reinstalled the Bridge or moved machines? Then delete the file and the next connection records the new certificate. Changed nothing? Then do not delete it, because something else may be listening on that port.
The one moment this cannot protect you is the very first connection, which is what trust on first use means. If that matters to you, compare the fingerprint the server prints against the certificate the Bridge shows.
Development
Area | Choice |
Language | TypeScript, Node.js 24 or newer |
MCP |
|
IMAP |
|
MIME |
|
SMTP |
|
Schemas |
|
Tests | Vitest, |
Every dependency that ships is permissive: mostly MIT, with MIT-0 (nodemailer), BSD-2-Clause (the htmlparser2 family), BSD-3-Clause (deepmerge-ts), ISC (split2) and one dual MIT OR EUPL-1.1+ (@zone-eu/mailsplit). Development dependencies add Apache-2.0 and MPL-2.0.
npm run build # compile into dist/
npm test # run the tests once
npm run test:watch # run the tests in watch mode
npm run test:coverage # with coverage report
npm run typecheck # type check only, no outputThe tests run without a Bridge, so they work in CI. Real credentials and real mail content have no place in test files.
Layout
src/
├── bridge/ connection handling, certificate pinning, error translation
├── credentials/ the four places a password can be kept, and the runtime probe
├── mail/ listing, reading, searching, identifiers, attachments
├── mime/ parsing, HTML to text, budgeting
├── tools/ the MCP tools and their output formatting
├── web/ the local interface and its security layer
├── activity.ts what is running and what ran, in memory only
├── config.ts the environment variables, read once at startup
├── in-flight.ts the counter that holds shutdown until answers are out
├── session.ts whether we are signed in, and where from
├── settings.ts the Bridge ports, when changed in the interface
└── server.ts entry point and transportThe web interface has one property worth knowing before touching it: Referrer-Policy must not be no-referrer. Under that policy browsers send Origin: null, which the CSRF defence rightly refuses, and the interface then refuses its own forms. The tests set the Origin header themselves and cannot catch it. There is a comment in src/web/security.ts and a test asserting the cause.
Checks before a pull request
The CI in .github/workflows/ci.yml runs the tests, the type check, the build, an audit of the dependencies and a scan for accidentally committed secrets. All of it runs locally too:
npm test
npx tsc --noEmit
npm run build
npm audit --omit=devMeasured behaviour of the Bridge
Some of what this server does looks odd until you know what the Bridge actually does, as opposed to what a standard IMAP server would do. The comments in src/bridge/, src/mail/ and src/mime/ name the measurement behind each decision. The three that surprise people most:
FETCH 1:*on an empty mailbox is answered withBAD no such messageinstead of an empty result, so a message count has to be checked first.UIDs are per mailbox and change when a message is moved. The Message-ID is the only stable handle.
A median message costs roughly 16000 tokens in its raw form and a fraction of that as text, which is why nothing raw is ever passed through.
Worth reading before changing anything in those directories.
License
MIT
Available Tools
6 toolsget_attachmentRead an attachmentARead-only
Reads one attachment of a message by index. The index comes from the attachment list in get_message. Textual attachments are returned as text, binary ones are refused with a reason rather than dumped into the context.
| Name | Required | Description | Default |
|---|---|---|---|
| index | Yes | Index of the attachment, as shown by get_message. Starts at 0. | |
| offset | No | Where to continue reading a long attachment. The answer names the next offset. | |
| mailbox | No | Optional: the mailbox to look in first. | |
| messageId | Yes | The message id the attachment belongs to. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, the description discloses significant behaviors: textual attachments return as text, binary ones are refused with a reason, and long attachments can be continued with offset. It also explains that binary content is not dumped into context, which is valuable for context management.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two concise sentences, front-loaded with the action and resource. Every clause adds value, with no repetition of schema or annotation details.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the absence of an output schema, the description adequately covers return behavior (text vs. binary refusal), offset continuation, and the source of the index. It gives enough information for an agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides descriptions for all 4 parameters (100% coverage). The description adds minimal new parameter information, mainly reinforcing that the index comes from get_message, which the schema already states. Thus a baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Reads one attachment of a message by index') and clarifies how the index is obtained from get_message, distinguishing it from sibling tools that list or search messages.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly explains that the index comes from get_message, implying a prerequisite workflow, and provides a when-not by noting binary attachments are refused. This gives clear usage context and exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_messageRead a messageARead-only
Reads one message by its id and returns it as readable text. HTML is converted to text, images and styling are dropped and no external content is fetched. Long bodies are shortened with a visible note. The body is marked as untrusted third-party content.
| Name | Required | Description | Default |
|---|---|---|---|
| mailbox | No | Optional: the mailbox to look in first. Saves a lookup. Without it the search goes through "All Mail". | |
| maxChars | No | Optional character budget for the body. Higher values cost context. | |
| messageId | Yes | The message id from list_messages or search, passed on unchanged. | |
| textOffset | No | Where to continue reading a long message. The answer names the next offset when something was left out, so a long message can be read in parts instead of raising the budget until it fits. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes beyond the readOnlyHint annotation by explaining exactly how the message is processed: HTML to text, images/styling dropped, no external content fetched, truncation with a visible note, and untrusted content marking. This gives a complete safety and behavior profile.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three concise sentences, front-loaded with the core action, followed by important behavioral details. No redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool lacks an output schema and has 4 parameters, the description covers the essential return behavior (readable text, truncation, untrusted marking) and relevant processing. It could benefit from mentioning what other fields are returned (e.g., subject), but the current coverage is solid for an agent's decision-making.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so parameters are already fully documented. The description adds useful context about long-body truncation that relates to maxChars and textOffset, but it doesn't add meaning to each parameter individually. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Reads one message by its id and returns it as readable text,' specifying the verb, resource, and selection method. This distinguishes it from sibling tools like list_messages and search_messages, which operate on collections.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage when you already have a message id, and the schema reinforces this by noting the id comes from list_messages or search. It doesn't explicitly compare against alternatives, but the context is clear enough for an agent to infer when to use this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_foldersList mailboxesARead-only
Lists the folders and labels of the Proton account. Proton distinguishes folders (a message lives in exactly one) from labels (a message can carry any number). Over IMAP both appear as directories.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | Which mailboxes to list. "system" covers Inbox, Sent, Trash and so on, "folder" the user-created folders, "label" the labels. | all |
| onlySelectable | No | When true, only mailboxes that can hold messages are listed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, and the description adds valuable domain context about Proton's distinction between folders and labels and how they appear over IMAP. This helps an agent interpret results beyond what annotations provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences, each earning its place: purpose, a key distinction, and a behavioral nuance. It is front-loaded with the main action and contains no fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple list tool with two optional params and no output schema, the description plus schema provides adequate context for use. It lacks details about return format but is otherwise complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds no additional parameter-specific meaning beyond what the schema already documents, which is sufficient.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists folders and labels of the Proton account, using a specific verb and resource. It implicitly distinguishes itself from sibling tools like list_messages by targeting mailboxes rather than messages.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context on what the tool does but does not explicitly mention when to use it versus alternatives. It implies use for retrieving mailbox structure but lacks explicit exclusions or sibling tool comparisons.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_messagesList messagesARead-only
Lists the messages of a mailbox, newest first by the date the sender wrote, which also holds across pages. Returns headers only: sender, subject, date, size and the message id. It never returns message bodies, because a single message averages around 16000 tokens raw. Use get_message with an id to read one. Ordering reads one date per message in the mailbox, so a very large mailbox takes a moment; the answer says so when it does.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | How many messages to return, at most 100. | |
| offset | No | How many of the newest messages to skip. Used for paging. | |
| mailbox | No | The mailbox path, for example "INBOX", "Archive" or "Folders/Work". Use list_folders to see them. | INBOX |
| unreadOnly | No | When true, only unread messages are listed. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, and the description adds useful behavioral details: stable ordering across pages, headers-only returns, and a latency caveat for large mailboxes. It discloses why bodies are excluded without contradicting annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is five sentences, each adding value: purpose, ordering, return format, reasoning for headers-only, alternative, and performance caveat. It is front-loaded and free of fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the schema covers parameters, annotations cover safety, and no output schema is needed, the description fully covers return format, ordering, pagination, performance, and alternatives. It is complete for a listing tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, with each parameter already well-documented. The description adds context about ordering stability across pages, which relates to limit/offset, but does not significantly redefine parameters. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists mailbox messages newest first, returns headers only, and distinguishes itself from get_message (for bodies) and list_folders. It is specific about the resource and scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly says to use get_message with an id to read a message body, and notes this tool never returns bodies. It also mentions performance behavior for large mailboxes, giving clear when-to-use context and an alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
open_configurationOpen the configuration interfaceARead-only
Returns the address of the local configuration interface, which runs for as long as this server does. Use it when the user asks how to sign in, where to change settings, or when another tool reports that no credentials are available. The address contains an access token and changes every time the server restarts.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, the description discloses that the address contains an access token, changes on server restart, and is valid for the server's lifetime. This is important behavioral context that helps the agent handle the returned value safely and appropriately.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loaded with the core action, and every clause contributes meaning. It avoids redundancy and is well-structured for quick parsing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity (no parameters, no output schema), the description is complete: it explains what is returned, when to use it, and the important caveats about the token and restart behavior. No critical information is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so there are no parameter semantics to explain. The description adds value by clarifying the nature of the returned address and its token, which is sufficient given the absence of parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('returns') and resource ('address of the local configuration interface'), clearly distinguishing the tool from sibling tools that deal with messages and folders. It also explains the purpose of the interface, leaving no ambiguity about what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly tells when to use this tool: when the user asks how to sign in, where to change settings, or when another tool reports no credentials are available. This provides clear context for invocation and implicitly separates it from the message/folder tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_messagesSearch messagesARead-only
Searches a mailbox by text, subject, sender, recipient, date range, read state, star or size. At least one criterion is required. Returns headers only, newest first across pages, with paging. A full-text search walks the local database of the Bridge and takes a few seconds on a large mailbox, while criteria such as unread or date are fast. Searching "All Mail" covers every mailbox including trash.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Substring of a recipient address. | |
| from | No | Substring of the sender address or display name. | |
| text | No | Free text, matched against body and headers. This is the slow criterion. | |
| limit | No | How many results to return. | |
| since | No | Only messages on or after this date, as YYYY-MM-DD. | |
| before | No | Only messages before this date, as YYYY-MM-DD. | |
| offset | No | How many results to skip, for paging. | |
| mailbox | No | Which mailbox to search. Defaults to "All Mail", which holds every message. | All Mail |
| subject | No | Substring of the subject. | |
| largerThan | No | Minimum size in bytes. Useful for finding messages with large attachments. | |
| unreadOnly | No | When true, only unread messages. | |
| starredOnly | No | When true, only starred messages. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses output format (headers only), ordering (newest first), paging, performance characteristics, and scope of 'All Mail'. This significantly extends beyond the readOnlyHint annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three concise sentences with no redundancy. Front-loaded with core function and each sentence adds essential context.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, description explains return shape and paging. It covers performance and mailbox scope, making the tool's behavior sufficiently clear for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema already covers all parameters with descriptions (100%). Description adds context that 'text' is slow and groups criteria types, aiding understanding without duplicating schema details.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states it searches a mailbox by multiple criteria, using a specific verb and resource. It distinguishes from siblings through criteria and header-only return, but does not explicitly name alternatives.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
States 'At least one criterion is required', implying use for filtered searches. Provides performance guidance and 'All Mail' scope, but does not explicitly contrast with list_messages or other siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
6 tool updates
v0.3.0- First observed
get_attachment - First observed
get_message - First observed
list_folders - First observed
list_messages - First observed
open_configuration - First observed
search_messages
TDQS
Scored across 6 tools
The tools are mostly distinct: list_folders, get_message, get_attachment, and open_configuration have clear purposes. list_messages and search_messages overlap somewhat, but descriptions clarify that list_messages is for simple listing and search_messages is for criteria-based search, reducing confusion.
All tool names follow a consistent verb_noun pattern with lowercase and underscores: list_folders, get_message, get_attachment, open_configuration, list_messages, search_messages. No deviations.
With 6 tools, the server is well-scoped for a read-oriented Proton email interface. Each tool serves a distinct function and the count is appropriate for the apparent domain.
The tool set covers reading, searching, and config, but lacks any send, modify, delete, or folder-management operations. This is a significant gap for an email server, as agents cannot perform basic lifecycle actions like sending or updating messages.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Email infrastructure for AI agents — send, receive, search, and reply to email over MCP.
Email inboxes for AI agents: send, receive, reply, search, and manage threaded email over MCP.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
Related MCP Servers
- AlicenseAqualityDmaintenanceA read-only MCP server that connects to Proton Mail via Proton Bridge, enabling AI assistants to search, list, and read emails securely without leaving your machine.4231MIT
- FlicenseNot gradedqualityFmaintenanceAn MCP server that connects to ProtonMail via Bridge, enabling AI assistants like Claude to manage your email.3-
- AlicenseAqualityAmaintenanceAn MCP server for managing Proton Mail, Pass, Drive, and Calendar through local IMAP/SMTP, pass-cli, and Proton Drive CLI, enabling AI agents to operate the mailbox, manage passwords, sync files, and classify emails.39AGPL 3.0
- AlicenseAqualityCmaintenanceMCP server for reading and searching Proton Mail via Proton Mail Bridge, providing tools to list mailboxes, list/search messages, and get message details with TLS certificate pinning.4MIT