MCP 客户端能做的一切,脚本都能用普通 HTTPS 完成。一个端点,一个 API 密钥,JSON 进,JSON 出。本指南涵盖身份验证、你需要的三个调用、错误、重试、限制,以及新邮件的 webhook。
每个请求都是发往 https://mcpemails.com/api/mcp 的 JSON-RPC 2.0 POST。这与 claude.ai、ChatGPT 和其他 MCP 客户端使用的是同一个端点,工具相同,权限相同,所以脚本和聊天助手看到的是同一个产品。
Content-Type: application/json 和 Accept: application/json。响应是一个 JSON 对象。Authorization 头。structuredContent 对象,你不必解析自然语言。同样的数据也以 JSON 字符串形式放在 content[0].text 中。mcpe_ 加 64 个十六进制字符。它只在创建时显示一次,之后只保存哈希。请当场复制;丢失的密钥无法找回,只能替换。Authorization: Bearer mcpe_... 的形式发送。切勿把密钥放进 URL 或查询字符串。每个密钥只拥有你在创建时勾选的权限。之后可以通过 编辑访问权限 修改,改动在下一次调用时生效,无需重新连接。
| 权限范围 | 允许的操作 |
|---|---|
read:email | 列出、阅读和搜索邮件,下载附件,列出收件箱和文件夹。 |
search:email | 为旧密钥保留。搜索已由 read:email 覆盖。 |
send:email | 发送、回复和转发。 |
manage:drafts | 创建、更新和删除草稿。发送草稿还需要 send:email。 |
manage:folders | 移动、复制、标记和归档邮件;创建、重命名和删除文件夹。 |
delete:email | 删除和批量删除邮件。 |
manage:contacts | 从最近的邮件中搜索联系人。 |
schedule:email | 定时发送以及管理已定时的邮件。 |
manage:automations | 创建按计划运行、无需有人看管的规则。 |
从 read:email 开始,只在工作流确实需要时再添加写入权限。超出密钥权限范围的调用会在我们联系你的邮件服务器之前被拒绝。工作区中的查看者只能持有读取权限。
默认情况下,密钥可以访问工作区中的所有收件箱。要把它限定在其中几个,请在密钥上打开 编辑访问权限,在 收件箱访问 下选择收件箱。这一限制在每次调用时都会检查,所以限定在支持收件箱的密钥读不到财务收件箱,无论请求怎么写。
由用户自行连接的应用,例如 claude.ai、ChatGPT 和其他 MCP 客户端,使用 OAuth 而不是密钥登录:动态客户端注册、带 PKCE 的授权码,以及一个由用户选择权限范围和收件箱的授权页面。参见 OAuth 连接。对于你自己的脚本、服务器和工作流,API 密钥更简单。
MCP 客户端以 initialize 开始。脚本可以跳过它,因为服务器不保存会话,但它是检查密钥是否可用、读取服务器说明的低成本方式。
curl -s https://mcpemails.com/api/mcp \
-H "Authorization: Bearer $MCPEMAILS_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"clientInfo": { "name": "my-agent", "version": "1.0" },
"capabilities": {}
}
}'tools/list 返回该密钥可以调用的每个工具,包括描述、输入 schema 和输出 schema。请在启动时读取它,而不是硬编码工具名或参数:列表按密钥的权限范围过滤,而且工具会随时间增加参数。
curl -s https://mcpemails.com/api/mcp \
-H "Authorization: Bearer $MCPEMAILS_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }'按名称调用工具并传入参数。大多数工具接受一个 action 来决定做什么,例如 email_read 上的 list、read 或 search。用 inbox(邮箱地址)或 inbox_id 指定邮箱;inbox_list 会同时返回两者。
curl -s https://mcpemails.com/api/mcp \
-H "Authorization: Bearer $MCPEMAILS_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "email_read",
"arguments": { "action": "list", "inbox": "you@example.com", "unread": true, "limit": 20 }
}
}'一个成功的结果:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [{ "type": "text", "text": "{\"messages\":[...],\"total\":42, ...}" }],
"structuredContent": {
"messages": [{ "id": "...", "subject": "...", "from": { "name": "...", "email": "..." } }],
"total": 42,
"total_is_estimate": false,
"has_more": true,
"next_offset": 20
}
}
}错误有两类。完全无法执行的请求以带 HTTP 状态码的 JSON-RPC error 返回。到达了工具但被工具拒绝的请求以普通 result 返回,并带 isError: true,这样 AI 模型可以读到原因。请根据 data.error_code 或 _meta 中的代码分支,不要依赖消息文本。
| 响应 | 何时出现 | 如何处理 |
|---|---|---|
HTTP 401, -32001 | 密钥缺失、格式错误、已撤销或已过期。 | 修正 Authorization 头或创建新密钥。重试没有用。 |
HTTP 403, -32004, insufficient_scope | 密钥缺少此工具操作所需的权限范围。data.required_scopes 列出了可以放行的权限。 | 通过 编辑访问权限 添加该权限范围,或换用另一个密钥。 |
HTTP 429, -32003, rate_limit_exceeded | 请求过多。data.window 说明哪个窗口已满。 | 等待 Retry-After 秒(也在 data.retry_after 中),然后重试同一请求。 |
-32602 | 未知的工具名。data.available_tools 列出该密钥可以调用的名称。 | 使用 tools/list 中的名称。 |
isError: true, invalid_arguments | 参数未通过工具的 schema 校验。什么都没有执行。文本会指出每个错误的参数,如果参数名是猜的,还会给出实际存在的那个。 | 修正参数。同样的参数会以同样的方式被拒绝。 |
isError: true, usage_limit_reached | 免费工作区已用完当月的邮件操作额度。 | 等待 _meta 中的重置日期,或升级套餐。付费套餐没有月度上限。 |
isError: true, 其他 | 工具已运行但失败:邮件不存在、服务商拒绝、收件人无效等。 | 阅读 content[0].text。它会说明发生了什么,以及是否有邮件被发出。 |
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope", ...
{
"jsonrpc": "2.0",
"id": 4,
"error": {
"code": -32004,
"message": "Insufficient scope: the 'send:email' scope is required to call email_compose.",
"data": { "error_code": "insufficient_scope", "required_scopes": ["send:email"], ... }
}
}HTTP/1.1 429 Too Many Requests
Retry-After: 12
{
"jsonrpc": "2.0",
"id": 5,
"error": {
"code": -32003,
"message": "Rate limit exceeded",
"data": { "error_code": "rate_limit_exceeded", "window": "per_minute", "retry_after": 12, ... }
}
}{
"jsonrpc": "2.0",
"id": 6,
"result": {
"isError": true,
"content": [{ "type": "text", "text": "Invalid arguments for email_read. The call was rejected before it ran, ..." }],
"_meta": {
"com.mcpemails/invalid_arguments": {
"error_code": "invalid_arguments",
"retryable": false,
"errors": [{ "path": "arguments.since", "message": "..." }]
}
}
}
}每个列表和搜索结果都以同样的四个字段结尾:
has_more:还有下一页时为 true。只有 has_more: false 才表示你已看完全部。next_offset:把它作为 offset 传回,其他参数保持不变,即可获取下一页。has_more 为 false 时它为 null。total:匹配数量,服务商无法统计时为 null。它永远不会与你手上的这一页矛盾。total_is_estimate:当 total 是估计值或下限而非精确计数时为 true。过长的邮件正文以同样方式分段:当 body_truncated 为 true 时,用 body_next_offset 作为 body_offset 再读一次。
发送后连接中断,你就无法知道邮件是否已发出。传入 idempotency_key 并用同一个键重试:操作只执行一次。
curl -s https://mcpemails.com/api/mcp \
-H "Authorization: Bearer $MCPEMAILS_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "email_compose",
"arguments": {
"action": "send",
"inbox": "you@example.com",
"to": ["customer@example.com"],
"subject": "Your order has shipped",
"body": "Hi, your order is on its way.",
"idempotency_key": "order-1042-shipped"
}
}
}'tools/list 会在每个接受它的工具的输入 schema 中显示 idempotency_key。idempotent_replay: true、原始的 status,以及 result 中的原始结果,包括你可能丢失的 message_id。idempotency_key_conflict 被拒绝。新操作请用新键。status: "processing"。稍后再试。status: "unknown"。在用新键再次发送之前,请先检查已发送文件夹。{
"idempotency_key": "order-1042-shipped",
"idempotent_replay": true,
"status": "succeeded",
"result": { "message_id": "...", "inbox_id": "...", "status": "sent" },
"message": "This logical request was already processed. ..."
}任何收件箱都可以暂留每一次发送,等人工审批。此时脚本的发送会返回带 status: "pending_approval" 的 approval_id 而不是直接发出,等有人在控制台批准后才会发送。这在所有套餐上都可用,是新自动化在证明自己之前的好默认设置。用同一个 idempotency_key 重试会报告该审批,而不是再创建一个。
每个套餐对整个工作区都有一个每分钟上限。单个密钥可以用满套餐的每分钟限额;多个密钥则共享它。
| 套餐 | 每分钟请求数 | Webhook |
|---|---|---|
| Free | 60 | 否 |
| Personal | 120 | 否 |
| Pro | 300 | 是 |
| Business | 1,000 | 是 |
tools/call 的响应带有 RateLimit-Limit、RateLimit-Remaining 和 RateLimit-Reset(秒),对应最接近满额的那个窗口,客户端可以据此在触发限制前自行控制节奏。data.window 中指出哪个窗口已满。Retry-After。等待这么久,然后重试同一请求。在 Pro 和 Business 套餐上,当邮件到达收件箱时,MCPEmails 可以向你的 URL 发送 POST,让工作流由新邮件触发,而不必轮询。在控制台创建端点,选择它的收件箱,并复制只显示一次的签名密钥。URL 必须是 HTTPS,端口为 443 或 8443。用测试按钮发送一个带签名的 ping 事件。
POST /your/webhook HTTP/1.1
Content-Type: application/json
MCPEmails-Event-Id: evt_...
MCPEmails-Event-Type: message.received
MCPEmails-Signature: t=1792843200,v1=5f2b...
{
"id": "evt_...",
"type": "message.received",
"created_at": "2026-10-24T09:20:00.000Z",
"workspace_id": "...",
"inbox_id": "...",
"folder": "INBOX",
"count": 1,
"message_ids": ["..."]
}type 为 message.received。message_ids 列出该收件箱 INBOX 文件夹中的新邮件,每个事件最多 10 条;count 是到达的数量,可能更多。messages 列表,含每封邮件的 id、from 和 subject。email_read(action: "read"、inbox_id 和 message_id)获取邮件。该密钥的权限范围和收件箱访问照常生效。每次投递都带有 MCPEmails-Signature: t=<unix 秒>,v1=<hex>,其中 v1 是以端点密钥计算的 HMAC-SHA256,输入为时间戳、一个点和原始请求体。密钥是显示的完整 secret,包括 whsec_ 前缀。对原始请求体重新计算签名,用恒定时间比较,并拒绝超过五分钟的时间戳。
import crypto from 'node:crypto';
// rawBody: the request body exactly as received, before JSON.parse.
export function verifyMcpemailsSignature(rawBody, header, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(
String(header ?? '').split(',').map((part) => part.trim().split('=')),
);
const timestamp = Number(parts.t);
if (!Number.isFinite(timestamp)) return false;
if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest();
const received = Buffer.from(parts.v1 ?? '', 'hex');
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}MCPEmails-Event-Id 忽略已经处理过的投递。重试会带同一个事件 id。POST 的 Webhook 触发节点,并把它的生产 URL 注册为 MCPEmails 控制台中的 webhook 端点。POST,URL https://mcpemails.com/api/mcp,身份验证选 Generic Credential Type → Header Auth,名称为 Authorization,值为 Bearer mcpe_...。result.structuredContent 中拿到邮件。NODE_FUNCTION_ALLOW_BUILTIN=crypto 放行 crypto 模块。{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "email_read",
"arguments": {
"action": "read",
"inbox_id": "{{ $json.body.inbox_id }}",
"message_id": "{{ $json.body.message_ids[0] }}"
}
}
}若要让 AI Agent 节点自己读取和发送邮件,请参阅 n8n 指南。
通过请求头中的密钥添加服务器。这适合没有浏览器完成 OAuth 登录的无头机器和 CI:
claude mcp add --transport http mcpemails https://mcpemails.com/api/mcp \
--header "Authorization: Bearer $MCPEMAILS_KEY"Claude Code 指南 介绍了 OAuth 登录和权限范围的选择。
逐页列出一个收件箱中的所有未读邮件,并在遇到 429 时等待:
// node agent.mjs (Node 18 or later, MCPEMAILS_KEY in the environment)
const ENDPOINT = 'https://mcpemails.com/api/mcp';
let nextId = 1;
async function callTool(name, args) {
const res = await fetch(ENDPOINT, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.MCPEMAILS_KEY}`,
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({ jsonrpc: '2.0', id: nextId++, method: 'tools/call', params: { name, arguments: args } }),
});
if (res.status === 429) {
const wait = Number(res.headers.get('Retry-After') ?? 1);
await new Promise((resolve) => setTimeout(resolve, wait * 1000));
return callTool(name, args);
}
const reply = await res.json();
if (reply.error) throw new Error(`${reply.error.code}: ${reply.error.message}`);
if (reply.result.isError) throw new Error(reply.result.content[0].text);
return reply.result.structuredContent;
}
const inbox = 'you@example.com';
let offset = 0;
do {
const page = await callTool('email_read', { action: 'list', inbox, unread: true, limit: 50, offset });
for (const m of page.messages) console.log(m.id, m.from?.email, m.subject);
offset = page.has_more ? page.next_offset : null;
} while (offset !== null);OpenClaw 默认使用 OAuth 登录;OpenClaw 指南 有完整步骤。若要改用限定权限的密钥,请把它放进服务器条目的 headers 映射中,并且不要设置 auth,因为 OpenClaw 会忽略设为 OAuth 的条目上的静态 Authorization 头。
{
"mcp": {
"servers": {
"mcpemails": {
"url": "https://mcpemails.com/api/mcp",
"transport": "streamable-http",
"headers": { "Authorization": "Bearer mcpe_YOUR_KEY_HERE" }
}
}
}
}