An email MCP server for Claude Code, Codex, and Cursor
Give coding agents a real inbox over MCP: install the agent-temp-mail server with one command, learn its seven tools, and keep the key out of the model.
On this page
Coding agents keep running into email. You ask Claude Code to build a signup flow and test it, or Codex to create an account on a staging service, and the work stalls at “check your inbox.” An MCP server gives the agent an inbox it can check by itself.
Agent Temp Mail ships one in its npm package. It runs locally over stdio, needs no account or API key, and keeps the mailbox’s private key in a file the model never sees. Setup is one command.
What the server does
The server gives your agent a permanent, receive-only address tied to a key file on your machine. On first run it generates an Ed25519 keypair and saves it, readable only by you, at the path you choose. The public key becomes the address. The private key signs every request the server makes for the agent.
That split is the point. The tool calls the model makes contain ordinary arguments, like a message ID. The server adds the signature after the model is done, so nothing that grants access to the inbox passes through the conversation.
Install in Claude Code
mkdir -p "$HOME/.config/agent-temp-mail"
claude mcp add --transport stdio \
--env MAIL_KEY_FILE="$HOME/.config/agent-temp-mail/identity.key.json" \
agent-temp-mail -- npx --yes --package=agent-temp-mail@0.4.0 agent-temp-mail-mcp
Start a new session and ask, “What’s my agent-temp-mail address?” The agent calls inspect_inbox and tells you.
Install in Codex
mkdir -p "$HOME/.config/agent-temp-mail"
codex mcp add agent-temp-mail \
--env MAIL_KEY_FILE="$HOME/.config/agent-temp-mail/identity.key.json" \
-- npx --yes --package=agent-temp-mail@0.4.0 agent-temp-mail-mcp
Cursor, Claude Desktop, and other MCP clients
Any client that reads the standard mcpServers JSON works with the same block. In Cursor that’s ~/.cursor/mcp.json, or .cursor/mcp.json inside a project. In Claude Desktop it’s claude_desktop_config.json.
{
"mcpServers": {
"agent-temp-mail": {
"command": "npx",
"args": ["--yes", "--package=agent-temp-mail@0.4.0", "agent-temp-mail-mcp"],
"env": { "MAIL_KEY_FILE": "/absolute/path/to/agent.key.json" }
}
}
}
Use an absolute path. Many clients don’t expand ~ or $HOME inside JSON.
The seven tools
| Tool | What it does | Effect |
|---|---|---|
inspect_inbox | Shows the address, retention, and what’s stored. Works before any mail arrives. | Read |
list_messages | Lists message summaries oldest first, with since, sender, after, and limit filters. | Read |
get_message | Returns one message as plain text, with candidate codes and links. | Read |
get_candidates | Returns only candidate codes and links, each with its source message ID. | Read |
configure_inbox | Sets retention for new messages, from one hour to seven days. | Changes settings |
delete_message | Deletes one message. | Deletes |
purge_inbox | Deletes all stored messages and settings. The address keeps working. | Deletes |
If your client lets you auto-approve tools, approving the four read tools covers almost every task. Leave the others to ask first.
What a session looks like
Say you’re testing the signup flow on a staging deployment and you ask:
Sign up at https://staging.example.com/signup with my agent-temp-mail address,
finish email verification, and confirm the dashboard loads.
A well-behaved agent will:
- Call
inspect_inboxto get the address. - Note the current time, then submit the form with its browser or HTTP tool.
- Call
list_messageswithsinceset to that time, waiting between empty checks as the response’spoll_after_secondssuggests. - Call
get_candidates, choose the verification link onstaging.example.com, and open it. - Load the dashboard and report what it found.
If the agent skips the timestamp step, ask for it. It’s the difference between verifying today’s email and yesterday’s. How AI agents receive verification codes explains the rest of the loop.
Keep the key file safe
- The key is the inbox. Anyone who copies the file can read the mail. Keep it outside your repository, and add the path to
.gitignoreif it has to live nearby. - Lose it and the address is gone. There’s no recovery and no way to move the address to a new key. Back the file up if the address matters.
- Lock it in after setup. Once the file exists, add
MAIL_REQUIRE_EXISTING_KEY=1to the server’s environment. Without it, a mistyped path silently creates a new key, and with it a new address. - One key per agent. Give separate agents separate files unless you want them to share an inbox.
Local server or hosted endpoint?
There’s also a hosted Streamable HTTP endpoint at https://agent-temp-mail.com/mcp, meant for custom connectors in ChatGPT and Claude where you can’t run a local process. With no local process to hold the key, every protected tool call must carry an _auth proof: a signature over that exact tool name and those exact arguments, valid for 60 seconds and usable once. The SDK builds it for you:
import { loadIdentity, signedToolArguments } from "agent-temp-mail";
const identity = await loadIdentity("./agent.key.json");
const args = signedToolArguments(identity, "list_messages", {
since: new Date().toISOString(),
});
// Pass args as the list_messages tool arguments in your connector.
That makes the hosted endpoint better for occasional, deliberate calls. For an agent that works on your machine, the local server is simpler and safer: it signs automatically and hides _auth from the tools the model sees.
Troubleshooting
- The server exits immediately. Check that
node --versionreports 22.12 or later, thatMAIL_KEY_FILEis set, and that its directory exists. - The address changed. The server started with a different key path. Point it back at the original file and set
MAIL_REQUIRE_EXISTING_KEY=1. - HTTP 429 errors. Each key can make 30 signed calls a minute, and the service shares a daily budget. The error includes
retry_after_seconds; wait that long. Agents that check in a tight loop hit this first. - A 409 replay error. A signed request was reused. The local server signs every call fresh, so this usually means a hosted-connector proof was retried. Sign again.
The full protocol lives in llms.txt, and every supported runtime is listed in integrations.md.