agent-email

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.

Category
访问服务器

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_ok is true only when the sender's domain aligns and the SPF/DKIM/DMARC verdicts (parsed from Cloudflare's Authentication-Results header) pass. By default wait_for_email ignores anything that isn't sender_ok. Unpinned addresses trust every sender — so wait_for_email returns a loud warning for them.
  • Untrusted-content framing. Email content — including list_recent subjects/snippets — is returned under an untrusted key with a security_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_TOKEN accepts a comma-separated list, so set new,old, roll clients, then drop old.
  • Rate-limit /mcp with a Cloudflare WAF rule (especially on 401s) 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_email caps 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

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选