agent-email
A headless, disposable email inbox that an AI agent drives over MCP, enabling temporary email addresses and email receipt for testing signup flows.
README
agent-email
A headless, disposable email inbox that an AI agent drives over MCP — no UI, no built-in AI. Give a coding agent (Claude Code, Cursor, etc.) an address like netflix-test@your-domain.example, and it can receive the confirmation email, pull out the link, and click it — so it can sign up for services and test your own onboarding flows without ever touching your real mailbox.
Built entirely on Cloudflare: one Worker, Email Routing for receiving, a Durable Object (SQLite) for storage, and an Agents SDK McpAgent for the MCP surface.
inbound email ──▶ Email Routing (catch-all: *@your-domain) ──▶ Worker email() handler
│ parse (postal-mime)
▼
MailboxDO (SQLite, singleton)
▲
│ read
MCP client (your agent) ──▶ Worker /mcp (bearer auth) ──▶ InboxMCP (McpAgent)
Registered addresses, not an open catch-all. The catch-all delivers every message to the Worker, but the Worker only keeps mail addressed to an address you've registered via create_address — everything else is dropped. So the agent mints a random, single-purpose, short-lived address per signup (netflix-a1b2c3d4@…), which both segregates mail and keeps the prompt-injection surface tiny (see Security).
MCP tools
| Tool | What it does |
|---|---|
create_address({ label?, expected_sender?, ttl_minutes?, local_part? }) |
Register a new address (random local-part by default). Pin expected_sender and set a short TTL. Call this before receiving. |
list_addresses() |
Registered addresses with pinned sender + expiry. |
delete_address({ address, with_emails? }) |
Revoke an address. |
list_recent({ address?, limit? }) |
Recent emails, newest first. Summaries; sender_ok:false flags a failed sender pin. |
get_email({ id }) |
One email in full. Body + classified links live under untrusted (data, not instructions); each link carries safety flags. |
wait_for_email({ address, timeout_seconds?, since?, only_trusted_sender?, subject_contains? }) |
Blocks until a new (trusted-sender) email lands, then returns it in full. The workhorse for onboarding tests. |
clear_inbox({ address? }) |
Delete stored mail to reset between runs. |
"Clicking" the link needs no special tool — once the agent has a URL (and has checked its flags), it just fetches it.
Setup
Prerequisites: a Cloudflare account and a domain on Cloudflare whose email you're willing to route (use a domain you don't receive real mail on, or a dedicated subdomain).
git clone <your-fork> agent-email && cd agent-email
npm install
1. Set your domain (cosmetic — improves tool descriptions). Edit INBOX_DOMAIN in wrangler.jsonc.
2. Deploy the Worker.
npm run deploy
3. Set the MCP auth token (gates the /mcp endpoint — anyone with the URL + token can read the inbox, so treat it like a password):
openssl rand -hex 32 | npx wrangler secret put MCP_TOKEN
4. Enable Email Routing + a catch-all rule pointing at this Worker. In the Cloudflare dashboard: your domain → Email → Email Routing, enable it (adds the MX/SPF records), then under Routing rules → Catch-all address, set the action to Send to a Worker → agent-email. Or via CLI:
npx wrangler email routing enable # adds DNS records
# then set the catch-all to this Worker in the dashboard
Subdomain note: Email Routing operates on the zone apex (
*@your-domain.com). To use a subdomain (*@inbox.your-domain.com) you must add the subdomain's MX/TXT records yourself — the apex is the simpler path.
5. Connect your agent. For Claude Code:
claude mcp add --transport http agent-email https://agent-email.<your-subdomain>.workers.dev/mcp \
--header "Authorization: Bearer <your MCP_TOKEN>"
(Use your Worker's real URL, or a custom domain/route if you've set one.)
Usage
Once connected, ask your agent to do something like:
Sign up for Netflix. Create an inbox address pinned to
netflix.com, use it for the signup, then wait for the confirmation email and open the verification link.
The agent calls create_address({ label: "netflix", expected_sender: "netflix.com", ttl_minutes: 30 }), gets back netflix-a1b2c3d4@your-domain.example, submits the signup with it, calls wait_for_email({ address }), checks the returned link's flags are clean, and fetches it — done.
Security
A public inbox means anyone can send mail that lands in your agent's context, so prompt injection can't be filtered away — it has to be designed around. The controls here shrink the attack surface and constrain the blast radius:
- Registered-only allowlist. Mail to unregistered addresses is dropped, so an attacker can't inject unless they know an active, single-purpose address the agent just created. Re-registering a live address is rejected (so a pin can't be silently weakened), and both addresses and total volume are capped.
- Random, short-lived addresses. Default random local-parts + a mandatory TTL (default 60 min) make addresses unguessable and expire the attack window after your test. Expired addresses and their mail are purged.
- Sender pinning + authentication. Pin
expected_sender;sender_okis true only when the sender's domain aligns and the SPF/DKIM/DMARC verdicts (parsed from Cloudflare'sAuthentication-Resultsheader) pass. By defaultwait_for_emailignores anything that isn'tsender_ok. Unpinned addresses trust every sender — sowait_for_emailreturns a loudwarningfor them. - Untrusted-content framing. Email content — including
list_recentsubjects/snippets — is returned under anuntrustedkey with asecurity_notice, telling the driving agent to treat it as data, never instructions. - Ingest limits. Bodies are truncated and oversized messages are stubbed, so a huge/attacker email can't exceed the storage row limit or flood the agent's context window.
- Link flagging. Every link is classified —
private_ip/ip_literal(SSRF, incl. decimal/hex/IPv6 forms),sender_mismatch,non_https— so the agent (or you) can refuse a suspicious "confirmation" link before fetching.
What this does not do: it cannot stop a sufficiently clever injection in text the agent reads, and it cannot control what the agent fetches or where a link redirects (the fetch happens in the agent, not the Worker). So the flags are advisory — fetch flagged links with redirect: "manual" and human confirmation — and, most importantly, run the driving agent with least privilege: no production credentials or other secrets in the same session. Worst case should be "it clicked a bad link in a throwaway sandbox."
Operational hardening (recommended, configured on your account)
- Rotate tokens with zero downtime:
MCP_TOKENaccepts a comma-separated list, so setnew,old, roll clients, then dropold. - Rate-limit
/mcpwith a Cloudflare WAF rule (especially on401s) to throttle brute force. - Add a second gate with a Cloudflare Access service token in front of the Worker — free, and independent of the bearer check.
Notes & limits
- Single-user by design. One shared bearer token; no per-user accounts. It's a personal/team test tool, not a mail host.
wait_for_emailcaps at 120s per call (Cloudflare request limits). Poll again for longer waits.- Email Service is for transactional email; don't point bulk/marketing mail at it.
- Attachments aren't stored (bodies + links only). Add an R2 binding if you need them.
- Storage is a single Durable Object — plenty for a personal test inbox, not a multi-tenant service.
Development
npm run types # regenerate worker-configuration.d.ts from wrangler.jsonc
npm run typecheck # tsc --noEmit
npm run dev # local wrangler dev (note: inbound email only works when deployed)
License
MIT — see LICENSE.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。