e2a — email for AI agents

io.github.Mnexa-AI由 GitHub 账号 Mnexa-AI 发布v0.3.2更新于 Oct 24, 2026

Authenticated email gateway for AI agents — per-agent inboxes, HITL approval, SPF/DKIM verified.

已验证Streamable HTTP可网页运行效率与工作流沟通与协作

概览

AI 生成的概览

通过 MCP 为 AI 代理提供专属邮箱:收发、回复、转发邮件,并可将外发邮件交由人工审核。

可以做什么
  • 读取与管理邮件:列出、查看、打标签、移入回收站与恢复邮件,获取附件下载链接
  • 发送邮件:新建邮件、保留会话的回复、转发,以及可选的定时发送
  • 管理代理邮箱:列出、查看、创建与恢复已验证域名或共享域名下的代理地址
  • 人工审核:列出被暂缓的收件与外发邮件,并批准或拒绝
  • 管理配置:域名与 DNS 记录、代理级 API 密钥、联系人与外联、退订/屏蔽名单、邮件模板
适用场景
当代理需要一个真实且通过认证的邮箱身份与人或系统通信时使用,尤其适合外发邮件需先经人工审核的场景。也适合需要轮询收件箱或通过 webhook 接收邮件的代理框架与 MCP 宿主。
运行要求
需要托管式 Streamable HTTP 端点,或本地 npm 包以 stdio 运行。必须认证:浏览器 OAuth 2.1,或在 Authorization 头中提供 e2a API 密钥(E2A_API_KEY)。单代理账户可不填 E2A_AGENT_EMAIL;E2A_BASE_URL 用于指向自建后端。自建 HTTP 传输还需 E2A_API_URL 与主机白名单。
安装前请注意
该凭据可真实发信并访问收件箱;账户级密钥还会开放管理工具。发信、删除、吊销密钥、解除收件人屏蔽都会改动数据,部分需 confirm: true。删除密钥不可恢复,新密钥明文仅显示一次。移除真实的退信屏蔽可能损害发信信誉。外部收件人可能需审批,部分功能需付费方案。webhook 投递必须校验签名。

安装

在 SourceWeft 中

  1. 打开 控制台中的 e2a — email for AI agents,将其添加到工作区。
  2. 连接 所需的 凭据。它们会被私密存储,绝不会在公开页面上展示。
  3. 为需要使用其工具的对话启用该服务。

Web executable,通过 Streamable HTTP。 远程服务在工作区中配置后即可从网页运行时运行。

交给智能体安装

把这段提示词粘贴到 Claude Code、Codex、Cursor 等智能体,或 SourceWeft 对话中。智能体会读取该服务器的安装指南,先向你展示发布者、端点和登录方式,经你同意后再把它添加到你的客户端。密钥不会写进任何命令。

阅读 https://sourceweft.com/mcp/io.github.Mnexa-AI%2Fmcp-server/install.md,并按其中说明把这个 MCP 服务器接入我的客户端。修改任何配置之前,先告诉我它的发布者、端点和登录方式,等我确认。不要把 API 密钥写进命令或我会提交的文件;问我密钥应从哪里获取。

查看智能体所依据的安装指南

其他 MCP 客户端

把它添加到你客户端的 mcpServers 配置中。 客户端会在首次连接时完成 OAuth 登录。

{
  "mcpServers": {
    "e2a-email-for-ai-agents": {
      "type": "http",
      "url": "https://mcp.e2a.dev/mcp"
    }
  }
}

README

@e2a/mcp-server

Model Context Protocol server for e2a — gives any MCP-aware AI agent its own email inbox to send, receive, reply, and (optionally) review held outbound mail before it ships.

Works with Google ADK, LangChain, OpenAI Agents SDK, Claude Desktop, Cursor, Cline, and any other MCP host.

Connect

e2a's MCP server is hosted. Point your MCP host at the Streamable HTTP endpoint:

https://api.e2a.dev/mcp

Two ways to authenticate:

  • OAuth 2.1 (recommended for interactive hosts) — add e2a as a connector and authorize in the browser. No key is pasted into config.
  • Bearer API key (programmatic / self-host) — send your e2a dashboard API key in the Authorization: Bearer <e2a API key> header.

An agent-scoped credential resolves its agent server-side. Account-scoped callers pass the agent email per tool call.

Quick start

Google ADK (Python)

python
import os
from google.adk.agents import Agentfrom google.adk.tools.mcp_tool.mcp_toolset import McpToolsetfrom google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams
root_agent = Agent(    model="gemini-flash-latest",    name="e2a_agent",    instruction="Help the user manage their email. Reply to threads with `reply_to_message` to preserve threading headers.",    tools=[        McpToolset(            connection_params=StreamableHTTPConnectionParams(                url="https://api.e2a.dev/mcp",                headers={                    "Authorization": f"Bearer {os.environ['E2A_API_KEY']}",                },                timeout=30,            ),        ),    ],)

LangChain (Python)

Using langchain-mcp-adapters:

python
import os
from langchain.agents import create_agentfrom langchain_mcp_adapters.client import MultiServerMCPClient
client = MultiServerMCPClient({    "e2a": {        "transport": "http",        "url": os.getenv("E2A_MCP_URL", "https://api.e2a.dev/mcp"),        "headers": {            "Authorization": f"Bearer {os.environ['E2A_API_KEY']}",        },    },})
tools = await client.get_tools()agent = create_agent(    "anthropic:claude-sonnet-4-6",    tools=tools,    system_prompt="Reply with reply_to_message; pending_review is accepted, so do not retry it.",)

OpenAI Agents SDK (Python)

python
import os
from agents import Agent, Runnerfrom agents.mcp import MCPServerStreamableHttp
async with MCPServerStreamableHttp(    name="e2a",    params={        "url": os.getenv("E2A_MCP_URL", "https://api.e2a.dev/mcp"),        "headers": {            "Authorization": f"Bearer {os.environ['E2A_API_KEY']}",        },        "timeout": 30,    },    cache_tools_list=True,    max_retry_attempts=3,) as e2a:    agent = Agent(name="e2a_agent", mcp_servers=[e2a])    result = await Runner.run(agent, "Reply to the latest unread email politely.")

Claude Desktop / Cline / Cursor

Add e2a as a remote MCP server in the host's config (claude_desktop_config.json, cline_mcp_settings.json, etc.):

json
{  "mcpServers": {    "e2a": {      "url": "https://api.e2a.dev/mcp",      "headers": {        "Authorization": "Bearer YOUR_E2A_API_KEY"      }    }  }}

Hosts that support OAuth connectors can instead add https://api.e2a.dev/mcp as a connector and authorize in the browser — no key pasted.

Tools

The server exposes up to 80 tools spanning agents, messages, human-in-the-loop approval, attachments, domains, events, webhooks, API keys, contacts/outreach, email templates (beta), suppressions, and external sending access requests (beta). The visible set depends on your credential's scope: an agent-scoped credential sees the 21 runtime/inbox tools (read, send, reply, restore messages, per-agent outreach); an account-scoped credential also sees the 59 admin/setup tools (agent/domain/webhook/event/template/API-key/contact/ suppression/sending-access management — and HITL review discovery plus approve/reject, which is an account-owner action, never agent self-approval) — all 80. Every tool carries MCP annotations (readOnlyHint/destructiveHint/ idempotentHint) so hosts can auto-approve reads and flag destructive actions. The tables below highlight the most commonly used ones — your MCP host's tool list shows the set your scope allows, with per-tool descriptions.

Identity

ToolDescription
whoamiGet the authenticated account's identity — user, scope, plan/limits; for an agent-scoped credential, also the bound agent address. On a deployment that reports it, also includes the additive beta sending_access object (see External sending access below).
list_agentsList agent inboxes; pass deleted:true to list the 30-day trash. (Admin/account-scoped.)
get_agentGet one agent inbox by its full email address.
create_agentRegister a new agent by its full email address — on a verified domain you own, or the deployment's shared domain. No delivery "mode": inbound is always available via list_messages (poll) or a create_webhook subscription. (Admin/account-scoped.)
restore_agentRestore a soft-deleted agent and its configuration. (Admin/account-scoped.)

Webhook deliveries are signed — verify them. Push delivery is a top-level resource (create_webhook), not a per-agent mode. e2a HMAC-signs every webhook delivery against the webhook's signing secret (returned once from create_webhook / rotate_webhook_secret). Your handler must verify the signature on every request — the e2a SDK exposes constructEvent(rawBody, signatureHeader, secret) which verifies and returns a typed event in one call (throws on a bad signature). Or skip webhooks entirely and poll via list_messages.

Messages

Scheduled sending via send_at / scheduled_at is beta and may change before it is declared stable.

External sending access (beta). On a deployment that restricts it, an account may be limited to sending through the shared identity only to its verified account email and to agent inboxes in the same account. A send_message / reply_to_message / forward_message (or the deprecated send_email) call with any other To/Cc/Bcc recipient refuses the WHOLE send with external_sending_not_enabled (403) — nothing is queued, and the identical call will not succeed on retry. whoami's sending_access object reports the current state (enforcement_applies, shared_external_approved, paid_external_sending_entitled, owner_recipient_verified) and available_unlocks — what can lift the restriction on this deployment: operator_approval always; verified_domain (send from your own verified domain) and paid_entitlement (a paid plan) only where listed. The hosted service lists only operator_approval. To ask for approval, call request_sending_access once (account scope; also available in the dashboard at the error's recovery_url): resubmitting while a request is pending returns the same request, and rate_limited (3 requests per 30 days) must not be retried. An operator decides and the decision is emailed to the account owner; get_sending_access_request shows the current state. No MCP tool can grant approval.

ToolDescription
request_sending_accessBeta. File one request for an operator to review external sending access (use_case, recipients, expected_daily_volume). Mutating: refused with account_read_only on a frozen account. (Admin/account-scoped.)
get_sending_access_requestBeta. The account's latest request and its state (pending / approved / declined). Read-only. (Admin/account-scoped.)
ToolDescription
send_messageSend a new email. Immediate queueing returns status: accepted; future send_at returns status: scheduled plus scheduled_at; a review hold returns status: pending_review. All three are durable success outcomes — do not re-send.
reply_to_messageReply to a message — one the agent received (replies to its sender) or one it sent (continues the thread to the original recipients). Preserves In-Reply-To / References for thread continuity and accepts the same optional send_at as send_message.
forward_messageForward a message into a new thread, carrying its existing attachments by default. Accepts the same optional send_at as send_message.
list_messagesList mail; pass deleted:true to list trash. Filter by read_status (unread / read / all) and sender with reserved-word-safe from_; cursor-paginated (cursor + limit in, next_cursor out).
delete_messageMove a message to the trash (restorable for ~30 days). Requires confirm: true. Per-message permanent deletion is deliberately not exposed over MCP — use the REST API/SDK. (Purging a whole inbox is available to account scope via delete_agent's permanent: true.)
restore_messageRestore a soft-deleted message and resume its retention clock.
get_messageFetch full body, headers, and attachment metadata for one message.
get_attachmentGet one attachment's metadata + a short-lived download_url (fetch the bytes out of band); inline: true returns base64 data for small files (≤256 KB).
update_message_labelsAdd or remove labels on a message.

MCP message output intentionally omits the REST message-read beta thread_id field and adds no thread filter or endpoint. list_conversations / get_conversation remain caller-owned conversation_id application views, not RFC email-thread enumeration. Use reply_to_message with the original message ID to preserve wire topology.

Human-in-the-loop approval

list_reviews/get_review surface every account-scoped hold: outbound drafts awaiting send approval and inbound messages held by a screening gate. The email.review_requested webhook is an additional push notification for either direction and carries the same message_id. The approve_review/reject_review tools branch on direction: approving an inbound hold releases it to the agent's inbox (no send, no draft, override fields ignored); rejecting one drops it so it never reaches the agent.

ToolDescription
list_reviewsList inbound and outbound messages awaiting human review across the account, soonest-expiring first.
get_reviewGet the full held message (body, recipients, and screening context where applicable).
approve_reviewOutbound: send a held message, optionally with reviewer edits (subject / body / recipients). Inbound: release the screening hold to the agent's inbox (overrides ignored). Account-scoped — never agent self-approval.
reject_reviewOutbound: discard a held message. Inbound: drop the screening hold so it never reaches the agent. The optional reason is stored for audit. Account-scoped.

Domains

ToolDescription
register_domainRegister a custom sending domain; returns the MX + TXT DNS records to publish. (Admin/account-scoped.)

API keys

All admin/account-scoped. create_api_key mints agent-scoped keys only — the key is bound to one inbox and can act only as that agent. Account-scoped (workspace-admin) keys cannot be created over MCP; mint those from the dashboard or the raw API, where a human is in the loop.

ToolDescription
list_api_keysList the account's API keys — metadata only (secrets are shown once, at creation).
create_api_keyMint a new agent-scoped key bound to an inbox; returns the plaintext key ONCE — store it immediately.
delete_api_keyRevoke a key permanently (requires confirm: true).

Contacts (beta)

The people an account corresponds with (list_contacts / get_contact / create_contact / update_contact / delete_contact, admin/account-scoped) and one agent's per-contact outreach state — stage, next action, reply/ suppression facts (list_outreach_contacts / get_outreach_contact / set_outreach_contact / delete_outreach_contact, available to account scope or an agent-scoped credential for its bound inbox). Import is inert — it records identity and enrolls outreach without sending anything.

ToolDescription
list_contacts / get_contactList account contacts (filter by provenance/import batch/creation window) or fetch one by address.
create_contact / update_contact / delete_contactCreate, partially update (etag-guarded via if_match), or delete a contact's identity and all of its per-agent outreach rows. Deleting a contact does not remove suppressions.
import_contactsBulk-import contacts from rows, optionally enrolling them into an agent's outreach.
delete_contact_importReverse an import batch, removing untouched contacts and the enrolments it created.
list_outreach_contacts / get_outreach_contactList or fetch the contacts an agent is working, with server-derived reply/delivery facts.
set_outreach_contactEnroll a contact in an agent's outreach, or update the agent-owned fields (stage, next action).
delete_outreach_contactUn-enroll a contact from an agent's outreach; the contact and its suppressions survive.

Suppressions

Recipient block lists at two scopes, all admin/account-scoped (the server rejects agent-scoped credentials on every one — an agent must not read or edit blocklists). The account list is auto-populated by hard bounces and complaints and applies to sends from every agent; the agent list (beta) holds unsubscribe/manual blocks for one exact sending agent.

ToolDescription
list_suppressionsList the account-wide suppression list (address, source bounce/complaint/manual, reason, source message id).
delete_suppressionUn-suppress a recipient account-wide. Requires confirm: true; only use it for addresses known to be deliverable — removing a genuine bouncer hurts sender reputation.
list_agent_suppressionsList one agent's blocked recipients (unsubscribe/manual).
create_agent_suppressionIdempotently add a manual block for one agent; other agents can still mail the address.
delete_agent_suppressionRemove only the exact agent-scoped block. Requires confirm: true because the block may represent an unsubscribe.

Templates (beta)

Reusable email templates with {{variable}} interpolation (a flat Mustache subset — no loops/sections; missing variables render as empty strings), plus a read-only catalog of pre-built starters (welcome, verify-code, password-reset, receipt, agent-status, daily-digest, approval-request). Send with a template via send_message's template_id / template_alias + template_data (mutually exclusive with literal subject/body). All template management tools are admin/account-scoped. Beta — shapes may change before templates are declared stable.

ToolDescription
list_templates / get_templateList the account's stored templates (summary rows); get_template returns the full body sources.
create_templateCreate a template from literal source — or copy a starter verbatim with from_starter.
update_template / delete_templateEdit (re-parses changed parts) or delete a template.
validate_templateDry-run source: parse errors, a rendered preview against test_data, and suggestedData placeholders.
list_starter_templates / get_starter_templateBrowse the starter catalog; the detail view includes full body sources and per-variable metadata.

Errors

Every failed tool call returns an MCP result with isError: true and two representations of the error:

  • structuredContent — the machine-branchable form. Branch on this, not the text:

    json
    {  "code": "domain_not_verified",  "retryable": false,  "status": 403,  "request_id": "req_abc123",  "retry_after_seconds": 30,  "details": { "field": "…" }}
    FieldPresenceMeaning
    codealwaysStable snake_case token from the API error envelope (e.g. domain_not_verified, rate_limited, message_not_pending). Errors raised by the MCP layer itself (bad/missing arguments, confirm:true guards) carry invalid_request.
    retryablealwaystrue when a retry could plausibly succeed (rate limit, 5xx, connection failure).
    statusAPI errors onlyHTTP status of the API response (0 = connection-level failure). Absent when no request was made (MCP-layer validation).
    request_idwhen availableServer request id — quote it in support requests.
    retry_after_secondswhen availableBack-off hint for retryable errors.
    detailswhen availableStructured field-level detail from the envelope (omitted if oversized).
  • content (text) — the human-readable form, unchanged and stable: e2a error [<code>] (retryable)?: <message> for API errors, e2a error: <message> for MCP-layer errors. Existing agents that parse this text keep working, but new integrations should read structuredContent instead.

Successful results are unaffected (JSON in a text block; list tools return a domain-named array plus next_cursor while more pages remain).

Operating the HTTP server

Self-hosters run the HTTP transport via mcp/Dockerfile or the mcp service in the repo's docker-compose.yaml. Day-2 operations (SLOs, failure classes, correlation) are covered by docs/runbooks/mcp-server.md; this section is the reference for configuration and surface.

Environment variables

VariableDefaultDescription
PORT3000Listen port.
E2A_API_URLrequiredBase URL of the e2a API server this transport forwards bearers to (and probes for readiness). Canonical name; E2A_URL and E2A_BASE_URL are legacy aliases, still accepted with a deprecation warning. No default — the process refuses to start (ConfigError, exit 2) if none of the three are set, rather than silently forwarding your users' bearer tokens to the operator's api.e2a.dev.
MCP_ALLOWED_HOSTSderived from MCP_PUBLIC_URL, else requiredComma-separated Host allowlist (DNS-rebinding guard). Requests to /mcp and the discovery document with a Host outside the list get 421. Ports are stripped before comparison. No literal default; if unset it falls back to the host of MCP_PUBLIC_URL, and refuses to start if neither is set.
MCP_PUBLIC_URLunsetExternally reachable URL of this MCP server, used verbatim in the RFC 9728 protected-resource metadata and the 401 WWW-Authenticate challenge, and (when set) as the default for MCP_ALLOWED_HOSTS. Set when the fronting proxy's view of the URL differs from the inbound Host (local dev: http://localhost:8765).
MCP_AUTHORIZATION_SERVER_URL$E2A_API_URLAuthorization-server URL advertised in protected-resource metadata. Override when the bearer-forwarding URL is container-internal but OAuth clients must reach the AS from the host.
E2A_TRUST_PROXYloopbackExpress trust proxy setting — which hops' X-Forwarded-* headers are honored for discovery URLs. true/false, a hop count, or a preset/subnet list.
MCP_RESOLVE_TIMEOUT_MS5000Positive-int bound (ms) on the whoami probe that resolves a bearer to its principal, with at most 1 retry. On timeout/backend 5xx the request is served fail-closed at least-privilege agent scope, uncached, and logged as auth_resolution result=fallback.
MCP_SESSION_IDLE_MS300000Legacy name — sizes the resolve cache, not sessions. TTL (ms) of a cached bearer→principal resolution. The transport is stateless; there are no sessions to time out.
MCP_MAX_SESSIONS500Legacy name — sizes the resolve cache, not sessions. Max cached bearer→principal entries; oldest evicted past the cap. A miss costs one whoami, never a disconnect.

Health endpoints

All three are unauthenticated:

  • GET /healthz — process liveness only, never touches the backend: { "ok": true }. Wire to your liveness probe.

  • GET /readyz — readiness: probes {E2A_API_URL}/api/health (2s timeout, result cached 10s so a scraping fleet can't cause a probe storm). 200 { "ok": true, "checks": { "api": "ok" } } when reachable; 503 { "ok": false, "checks": { "api": "unreachable" }, "request_id": "…" }

    • Retry-After: 10 (the failure-cache TTL) when not. Wire to your readiness probe.
  • GET /metrics — Prometheus text exposition. Example lines:

    mcp_http_requests_total{route="mcp",status_class="2xx"} 1523mcp_http_request_duration_seconds_bucket{route="mcp",le="0.5"} 1480mcp_auth_resolutions_total{result="cache_hit"} 1490mcp_tool_executions_total{tool="list_messages",outcome="ok"} 412mcp_readyz_checks_total{result="ok"} 60mcp_resolve_cache_entries 7

    Route labels are bounded: mcp | healthz | readyz | metrics | discovery | other.

Structured logs

Single-line JSON on stderr (GCE-shaped severity / event / message plus fields). Request-scoped events carry request_id (the response's X-Request-Id): auth_resolution (result, duration_ms, scope?; invalid/fallback at WARNING), http_request (route, method, status, duration_ms; successful probe/scrape traffic on healthz/readyz/metrics is metered but not logged), tool_execution (tool, outcome, duration_ms, error_code?), terminal_error. Bearer tokens are never logged; the resolve cache is keyed by SHA-256 fingerprints. An inbound X-Request-Id matching ^[A-Za-z0-9_-]{1,64}$ is honored; otherwise the server mints mcpreq_<12hex>. The id is echoed on every response but is not forwarded to the backend API.

Client retry policy

Failed tool calls return structuredContent (see Errors): branch on code / retryable / retry_after_seconds. Retry only retryable: true errors with exponential backoff + jitter, ~3 attempts max. Never retry 4xx (401 → re-authenticate; 421 → fix the URL). accepted, scheduled, and pending_review on send tools are success outcomes — do not retry them. A future send_at to the sending agent's own address returns 400 invalid_request when direct loopback applies; review holds take precedence and drop the schedule. The transport is stateless, so "reconnect" after an interruption is simply re-POSTing; MCP clients do this automatically.

Links

License

Apache-2.0

来源:mcp/README.md,提交 29831a7

工具

0
该服务器需要登录,因此 SourceWeft 无法在没有账号的情况下读取其工具,也未收录。已连接账号所能看到的内容可能因账号及其权限而异。

版本历史

1
  1. v0.3.2最新Sep 16, 2026

评分

已满足适用检查项的 85%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通必需25 分
  • 已声明认证方式必需10 分
  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分