One server, seven clients
The server is a single HTTPS endpoint that speaks the Model Context Protocol over Streamable HTTP: one POST carrying JSON-RPC, one JSON reply, no stream held open. There is nothing to install, nothing to run locally and nothing to sign up for on the public domains — the URL is the entire configuration:
MCP endpointhttps://grabmail.io/mcp
Every MCP client takes a remote HTTP server, but each keeps its configuration in a different file with a slightly different key name. The sections below give the exact line for each. The formats are the ones current in September 2026; each client’s own documentation is the authority if one has moved since.
Check it answers, from a shell
Before touching any client, prove the server is there and see what it offers. Because the transport is plain HTTP, one curl is enough:
$ curl -s -X POST https://grabmail.io/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '.result.tools[].name'"create_inbox"
"list_domains"
"list_messages"
"read_message"
"wait_for_message"
"delete_message"If that works, every client below will work too, and a client that then fails is a configuration problem in the client rather than a server problem. If it does not, check that your network allows outbound HTTPS to grabmail.io — that is the whole of its footprint.
Claude Code
One command, from any directory. It registers the server for your user, so it is available in every project:
$ claude mcp add --transport http grabmail https://grabmail.io/mcpTo share it with a team through the repository instead, scope it to the project. That writes a .mcp.json at the root, which is checked in and which teammates get prompted to approve:
$ claude mcp add --transport http --scope project grabmail https://grabmail.io/mcp{
"mcpServers": {
"grabmail": {
"type": "http",
"url": "https://grabmail.io/mcp"
}
}
}Restart Claude Code, run /mcp, and grabmail is listed with its six tools. The server also answers the protocol’s initialize call with a short paragraph of instructions, which Claude Code feeds to the model — so the agent arrives already knowing to hand out the alias and wait on the address.
Claude Desktop
Remote servers are added through the application rather than the configuration file:
- Settings → Connectors → Add custom connector.
- Paste
https://grabmail.io/mcpas the URL and give it a name. - Start a new conversation and the tools appear under the connector.
On a version that only takes local servers in claude_desktop_config.json, bridge the remote endpoint with mcp-remote, which runs as a local process and forwards to the URL:
{
"mcpServers": {
"grabmail": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://grabmail.io/mcp"]
}
}
}Cursor
Cursor reads .cursor/mcp.json in the project (or ~/.cursor/mcp.json for every project). A remote server is a url:
{
"mcpServers": {
"grabmail": {
"url": "https://grabmail.io/mcp"
}
}
}Open Cursor Settings → MCP to see it listed and enable its tools. In Agent mode the model calls them on its own; in the chat you can ask for them by name.
Windsurf
Windsurf keeps its servers in ~/.codeium/windsurf/mcp_config.json, and the key for a remote server is serverUrl rather than url — the one place the shape differs:
{
"mcpServers": {
"grabmail": {
"serverUrl": "https://grabmail.io/mcp"
}
}
}Cascade lists the server after a refresh from the MCP panel. The same file can be reached from Windsurf Settings → Cascade → MCP servers → View raw config.
VS Code
VS Code’s agent mode reads .vscode/mcp.json in the workspace, or the user-level file that MCP: Add Server in the command palette writes. Servers live under servers, not mcpServers, and a remote one declares its transport:
{
"servers": {
"grabmail": {
"type": "http",
"url": "https://grabmail.io/mcp"
}
}
}A small “Start” link appears above the entry in the editor; after that the tools show in the chat’s tool picker, and agent mode calls them without being asked.
Codex CLI and Gemini CLI
Codex CLI keeps its configuration in TOML at ~/.codex/config.toml. A remote server is a table with a url:
[mcp_servers.grabmail]
url = "https://grabmail.io/mcp"Gemini CLI reads ~/.gemini/settings.json (or .gemini/settings.json in the project), and the key for a Streamable HTTP server is httpUrl:
{
"mcpServers": {
"grabmail": {
"httpUrl": "https://grabmail.io/mcp"
}
}
}A version of either that only takes local servers can reach the endpoint through the same mcp-remote bridge shown for Claude Desktop: command = "npx", args = ["-y", "mcp-remote", "https://grabmail.io/mcp"].
The six tools
Whatever the client, the model sees the same six tools with the same names. There is no state to manage between calls, and none of them needs an argument the previous one did not return.
create_inbox- Invents a fresh address and returns it with its alias and a
next_stepsaying which to use where. Nothing is reserved server-side, so it cannot fail. Takes an optional readableprefix. wait_for_message- Blocks until a message arrives at the address, up to 25 seconds, then returns it in full — subject, sender, plain text, HTML. Filter with
subject_containsorfrom_contains; passsince_idto ignore what was already there. After a quiet wait it answerstimed_outand asks to be called again. read_message- One message in full by id. Rarely needed, because the wait already returns the whole message.
list_messages- Everything waiting at an address, newest first, immediately — including when there is nothing.
list_domains- The public domains anyone may use, for when a form has just refused one of them.
delete_message- Removes a message now rather than in 5 days. Idempotent, so a retrying agent costs nothing.
The sign-up loop in four calls
This is the sequence almost every task needs, and the client does not change it:
create_inbox. Back come an address, an alias, and the note saying which is which.- The alias goes into the form. The site gets a working address that reaches the mailbox and cannot be used to open it.
wait_for_messageon the address, immediately after submitting, withsubject_containsset to a word the confirmation mail will carry. It blocks; the agent does not loop.- The code comes out of the message the wait returned. There is usually no further call at all.
Sign up for a trial at https://app.example.com/signup with a fresh GrabMail inbox.
Use the ALIAS in the form, wait for the confirmation code with wait_for_message
on the ADDRESS, enter it, and tell me the resulting login.What to put in the agent’s own instructions
The server tells the model how to use it at connection time, but a model that has read the same four rules in its own project instructions follows them every time rather than most times. Add this to CLAUDE.md, .cursor/rules, .windsurfrules, AGENTS.md or GEMINI.md — whichever the client reads:
## Email
- To receive email, use the `grabmail` MCP server. Call `create_inbox` once per task.
- Put the **alias** it returns into forms; poll the **address** it returns with `wait_for_message`.
- Call `wait_for_message` right after submitting a form, with `subject_contains` set to a word
you expect ("code", "verify", "confirm"). If it returns `timed_out`, call it again — up to
three times — before concluding the mail was not sent.
- Never reuse an inbox across tasks. Never send anything confidential to one: it is public.The two rules that matter most are the ones an agent gets wrong without being told: put the alias in the form and poll the address, and treat timed_out as “call again”, not as “the mail was never sent”. An inbox an AI agent can read goes through both in depth, including why the wait returns before the mail does.
When it does not work
| Symptom | Cause | Fix |
|---|---|---|
| The server is not listed | The configuration file is in the wrong place, uses the wrong key (url / serverUrl / httpUrl / servers), or the client was not restarted. | Copy the block for your client exactly, restart, and run the curl above to rule the server out. |
| The tools are listed but the model never calls them | The tools are disabled in the client’s MCP panel, or the model was not told an email step exists. | Enable them, and add the instructions paragraph above. |
wait_for_message keeps returning timed_out | The form was never submitted, the alias was mistyped, the site refused the domain, or 8 waits are already running. | Call again up to three times; check the form’s own error; read why sign-up forms block disposable email. |
| The site says the address is invalid | The public domain is on a disposable-mail blocklist. | Use a domain of your own (one MX record) or a domain from the pool kept off the lists. |
The bridge (mcp-remote) fails to start | No Node on the machine, or npx cannot reach the registry. | Install Node 18+, or use a client version that takes the URL directly. |
Before you call it done
- The
curlabove lists six tools from your machine. - The server appears in the client’s MCP panel after a restart, tools enabled.
- The instructions paragraph is in the file your client reads.
- A test prompt completed a sign-up: alias in the form, wait on the address, code read out.
- Nothing confidential will ever be sent to one of these inboxes — they are public.
That is the whole setup. The same server works from any framework that speaks MCP, and for agents built without MCP — a plain tool function in LangChain, the OpenAI Agents SDK or your own loop — email for AI agents shows the REST version of the same four steps.
Questions
Do I need an API key or an account for the MCP server?
No. The public domains take no key, no account and no header, and the MCP server exposes exactly what the REST API exposes. Only the paid pool of domains kept off the disposable-mail blocklists uses a bearer token, passed as an Authorization header on the endpoint.
Is it Streamable HTTP or SSE?
Streamable HTTP: one POST, one JSON reply. There is no event stream to hold open, which is why wait_for_message is bounded at 25 seconds — a client that opens a GET expecting SSE is told, in plain JSON, that there is none.
Can several agents share the server at once?
Yes. There is no session state; each call carries everything it needs. The only shared limit is that 8 wait_for_message calls run at once across everyone — past that the tool answers timed_out immediately and asks to be called again, which the instructions paragraph above handles.
Why does wait_for_message return before the mail arrives?
Because a server worker sleeping for minutes is a worker nobody else can use. The wait is bounded at 25 seconds and says timed_out honestly rather than failing; the agent calls again. Three calls is over a minute of waiting, which covers any transactional mail that was actually sent.
Can the agent send email too?
No. The service receives only, by design — a free server with no account that could send would be a spam relay within the hour. An agent that has to send mail needs a sending provider; this one is for reading what comes back.
Is the inbox private to my agent?
No. Anyone who knows the address can read it, on a public domain and on your own. That is why the alias exists: the site gets an address that reaches the mailbox and cannot be used to open it. Never let an agent send anything confidential to one of these inboxes.
Which client configuration is the reference if these change?
Each client’s own documentation. The shapes above are the ones current in September 2026; the server side does not change with them — it is one URL, and any client that can call a remote MCP server over HTTP can call it.


