// Verify an email MCP server · capability, not promises

Don't trust an email MCP server. Read its tool list.

Before you connect an inbox to any MCP server, you can establish what it is actually able to do in about five minutes — without reading its source, taking its word, or trusting its marketing. Every MCP server must answer tools/list over the wire, and that answer is the attack surface: an agent can only do what a tool lets it do. If no tool sends, the server cannot send — no matter what a persuasive email tries to talk it into.

This is a method for evaluating any email MCP server. RadMail is used as the worked example below only because it publishes the commands to check it. It names no other product, and it verifies capability, not intent — the limits of that are stated at the bottom rather than buried.

Why people started asking.

The security coverage of 2026 turned on a supply-chain pattern rather than a protocol flaw: an email MCP package published to a public registry, installed in good faith, whose compiled build quietly added a hidden recipient to every message it handled so a copy reached a domain the developer never chose. The agent could not tell — the tool call succeeded normally, and nothing in the readable source said it would.

The advice that followed was sound and generic: audit the publisher, scope credentials tightly, watch your outbound DNS, enforce DMARC. All worth doing. But none of it answers the question the person connecting an inbox is actually asking, which is narrower and answerable: what is this server able to do to my mail at all? That one has an objective answer, and you can get it in one request.

Five checks, before you connect.

radmail :: evaluating any email MCP server
  • 1. Read the tool list backDo not read the README — call the server. An MCP server must answer tools/list over the wire, and that answer is the actual attack surface: an agent can only do what a tool lets it do. Anything the docs promise and the tool list contradicts, the tool list wins.
  • 2. Look for the capability, not the promiseAsk which model of restraint you are being offered. Is the dangerous tool absent, narrowed by a configuration someone can widen again, or merely discouraged by instructions a model is asked to follow? These are very different guarantees and only the first survives a compromised agent.
  • 3. Ask for a machine-readable contractA prose safety page cannot be diffed, monitored, or checked by your own tooling. A published contract at a stable URL — forbidden actions, enforcement model, and the exact commands to disprove it — can be fetched on a schedule and alerted on when it changes.
  • 4. Verify the artifact, not the repositoryThe published incidents of 2026 were not bad source code; they were a published package whose build did something the readable source did not. Run the thing you will actually install, from the registry you will actually install it from, and inspect what it reports about itself.
  • 5. Re-check on every upgradeA tool list is a fact about one version. Pin the version, re-run the check when you bump it, and prefer servers whose tool descriptions are frozen under a hash the server itself refuses to serve past on a mismatch.

Three ways to be told “it won't do that”.

Check 2 is the one people skip, and it is the one that decides whether the guarantee survives a bad day. These describe enforcement models generically — they are a vocabulary for reading any server's posture, not a claim about anyone in particular.

capability-absentradmail

The tool that would perform the forbidden action does not exist on the server, so it cannot be called. There is nothing to configure and nothing to bypass.

config-restricted

The capability exists and is narrowed by configuration — an allowlist, a scope, a policy file. Whoever holds the configuration can widen it again.

policy-gated

The capability exists and is restrained by instructions, prompts, or documented policy that a model is asked to follow.

Only the first one holds when the agent itself is compromised or successfully injected, because the other two both depend on something an attacker who already has the agent may be able to reach: a configuration, or a model's willingness to follow instructions.

Worked example: run it on RadMail.

RadMail publishes both commands in its machine-readable safety contract at https://radmail.ai/.well-known/agent-safety.json — so the check below is not something we are inviting you to take on faith, it is the same text a machine can fetch and diff on a schedule. Do not take this contract's word for it — call the server and read the tool list back. Every tool is read, triage, search, explain, draft, or read-only diagnostic. If you find one that sends money, changes banking, or makes first contact, this contract is wrong and security@radmail.ai wants to hear about it.

tier 1 :: hosted sandbox — no account, no keyunauthenticated
curl -s -X POST https://radmail.ai/api/mcp/sandbox -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
A tool list containing no send-capable tool. draft_reply produces a draft for a human to review and send; it does not send.
tier 2 :: the published package — the larger surfacethe harder check
The command above checks the hosted sandbox, which serves 6 core triage tools over a fixed demo inbox. The `draftRefusalClasses` guarantee and the frozen tool manifest are scoped to the radmail-mcp PACKAGE — a larger surface the sandbox does not expose — so verify that tier directly rather than inferring it from the sandbox. This is the harder surface to check and it is the one the stronger claims live on.
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"verify","version":"1"}}}' '{"jsonrpc":"2.0","method":"notifications/initialized"}' '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' | npx -y radmail-mcp
A tool list containing no send-capable tool — every entry is read, triage, search, explain, draft, or read-only diagnostic. draft_reply's own description states: "DRAFT ONLY — never auto-sent. REFUSES (human-only) for money / changed-banking / first-contact / decision / injection." check_send_domain is named for what it inspects, not what it does: it is a read-only SPF/DMARC/DKIM DNS lookup and sends nothing. Observed 13 tools on the published package as of 2026-08-24; the count is an observation, the absence of a send tool is the claim.
Since 0.5.0 the package's tool names, descriptions, and input schemas are frozen under a sha256 manifest that the server recomputes at startup and refuses to serve on any mismatch — a fail-closed defense against MCP tool-description poisoning. That means the tool list you read back is the audited text or the server does not answer at all.

The source is MIT and readable at https://github.com/radmail-ai/radmail-mcp. Read it if you like — but check 4 exists precisely because reading a repository is not the same as running the build a registry hands you, and it is the build that ends up next to your mail.

Check the artifact, not the repository.

This is the check that the 2026 incidents were actually about, and it is worth being concrete about how ordinary the failure is. Build metadata and published source drift apart all the time for boring reasons — a release step that did not run, a compiled file that kept a stale constant. Most of the time it is harmless. The reason it matters anyway is that you cannot tell a boring drift from a deliberate one by reading the repository, because in both cases the repository looks correct.

An honest example, because a page telling you to check the artifact would be worthless if it hid what checking ours returns: as of 2026-08-29, running the tier-2 command above against the published RadMail package returns a server that reports an older version number than the one npm serves — a build-metadata drift we documented in our own contract notes rather than waiting to be asked, with the fix on the public main branch. It changes none of the tool-list facts on this page (the tool list read back from that same published build contains no send-capable tool), and it is exactly the sort of thing this check is for. Dated observation, not a permanent state: re-run the command and believe what it says, not what we said about it.

What this does not tell you.

A tool list establishes what an agent can be asked to do. It does not tell you what a read tool does with the data after it reads it, whether the network path is what it claims, or whether the next version will behave the same way. Nothing on this page makes any server — including RadMail's — immune to a compromised build; it makes the capability question answerable, which is a smaller and more useful thing.

Run it alongside the rest: pin the version, scope credentials read-only where the server supports it, watch what the process talks to, keep SPF, DKIM and DMARC enforcing, and keep call-back verification for banking changes and dual authorization for wires. RadMail is a tool that narrows what an autonomous agent can be tricked into doing. It is not a guarantee, it does not replace your controls, and compliance is a shared responsibility.

Status: RadMail is pre-release. The engine is live in a test bed on two real businesses today; the commercial multi-tenant product is launch-gated. The MCP sandbox you connect to is real and runnable now (heuristic, in-memory, free, no credentials), but it is the sandbox engine — not the production multi-tenant engine.

Common questions.

How do I verify an email MCP server can't send email on my behalf?

Call the server and read its tool list back over the wire, rather than reading its documentation. Every MCP server must answer a tools/list request, and that response is the complete set of things an agent connected to it can do — so if no tool in the list sends, drafts-and-sends, forwards, or replies outbound, the server cannot send, no matter what any prompt or email tries to talk it into. Do this against the exact build you intend to install, not against a hosted demo or a repository.

tl;dr Call tools/list. The tool list is the real attack surface; the README is a claim.

What actually happened in the malicious email MCP server incidents?

The pattern publicly reported through 2026 is a supply-chain one: a package published to a public registry, installed by developers in good faith, whose compiled build quietly added a hidden recipient to every message it handled so a copy of the mail reached an attacker-controlled domain. The agent using it could not tell — the tool call succeeded normally. The lesson is not that MCP is unsafe; it is that a server's trustworthiness is a property of the artifact you run and the capabilities it exposes, not of the vendor's description of itself.

tl;dr A published build did something its readable source did not. Verify the artifact.

What are the three ways an MCP server can restrain a dangerous action?

The capability can be absent — the tool that would perform the action does not exist on the server, so there is nothing to configure and nothing to bypass. It can be config-restricted — the capability exists and is narrowed by an allowlist, scope, or policy file that whoever holds the configuration can widen again. Or it can be policy-gated — the capability exists and is restrained by instructions a model is asked to follow. These are three different guarantees, and only the first one holds when the agent itself is compromised or successfully injected.

tl;dr Capability-absent, config-restricted, or policy-gated. Only the first survives a compromised agent.

Which model does RadMail's MCP server use?

RadMail's MCP server publishes capability-absent as a machine-readable value in its safety contract at https://radmail.ai/.well-known/agent-safety.json: the tools that would move money, change banking details, or make first contact do not exist on the server. There is no setting, feature flag, plan tier, or environment variable that turns them on, because enabling one would require shipping a tool that is not there. Money, changed banking, first contact, decisions, and suspected prompt injection are human-only, forever.

tl;dr RadMail publishes capability-absent — and publishes the command to disprove it.

How do I run this check against RadMail specifically?

Two commands, both published in RadMail's own safety contract. The zero-auth hosted sandbox answers an unauthenticated HTTP tools/list with no account and no key, and returns its six core triage tools. The radmail-mcp package — the larger surface where the draft-refusal classes and the frozen tool manifest are scoped — is checked by piping an initialize and a tools/list into npx. If either list ever contains a tool that sends money, changes banking, or makes first contact, the contract is wrong and security@radmail.ai wants to hear about it.

tl;dr Two published commands, one unauthenticated. The claim is built to be disproven, not trusted.

Does passing this check mean an email MCP server is safe?

No, and treating it that way would be the mistake this method is meant to prevent. A tool list tells you what an agent can be asked to do; it does not tell you what a read tool does with the data it reads, whether the network path is what it claims, or whether a future version will differ. It is one strong, cheap, objective check to run before you connect an inbox — alongside pinning versions, scoping credentials to read-only where the server supports it, watching what the process talks to, and your existing controls. RadMail is a tool that narrows what an agent can be tricked into doing; it is not a guarantee, and compliance remains a shared responsibility.

tl;dr It verifies capability, not intent. One strong check, not a safety certificate.

Should an email MCP server be read-only?

For inbox work, read-only is the stronger default and it costs less capability than people expect: triage, ranking, search, explaining why something surfaced, tracking commitments, and preparing a draft for review are all read-shaped. The send is the irreversible step, and it is the step business email compromise is actually aiming at — so leaving it with a human removes the loss without removing the usefulness. RadMail's connected mode is read-only by construction, and every field derived from real mail is taint-marked untrusted-email-body so an agent treats it as data rather than instructions.

tl;dr Everything useful about an inbox is read-shaped. The send is the only irreversible part.

Run the check on us first. The hosted sandbox answers an unauthenticated tools/list — no account, no key, no card. Read the list back and decide for yourself.

› The zero-auth sandbox
radmail@inbox:~$ tools/list --read-it-back --enforcement=capability-absent