error.code,以及 result.content[0].text 中的软行为信封 —— 一次失败可能触及其中一个、两个或全部三个层面。请由外向内排查:HTTP 状态码 → JSON-RPC 代码 → 软信封。
关于投影语法本身,请参阅 JMESPath 指南。关于各工具的参数与新鲜度预算,请参阅 工具参考。
快速指引
- HTTP 状态码是传输层的回答。大多数 JSON-RPC 回复 —— 无论成功还是错误 —— 按 JSON-RPC 2.0 惯例都会以 HTTP 200 返回。只有当失败属于通用 HTTP 客户端必须响应的情况(鉴权、每日上限、服务不可用)且受益于
Retry-After/WWW-Authenticate头时,handler 才会升级状态码。 - JSON-RPC
error.code是应用层的回答。共使用九个代码:-32001、-32002、-32003、-32004、-32029、-32600、-32601、-32602、-32603。handler 不会发出其他代码 —— 如果你看到了其他代码,请将其视为协议 bug 并提交 issue。 - 软行为信封是高频失败模式。
tools/call在 JSON-RPC 层成功(HTTP 200,无error字段),但位于result.content[0].text中的 JSON 携带了一个_budget_exceeded或_jmespath_error判别字段。只检查 JSON-RPC 信封的客户端会默默地把这些当作成功 —— 请解析result.content[0].text,并在将该负载作为数据消费前检查是否存在前导下划线_键。 - 已执行的调用仍计费。
_budget_exceeded、_jmespath_error和工具执行错误(-32603)都发生在工具已运行之后,因此它们会消耗 Pro 每日配额槽位。只有预分发失败(如每日上限拒绝或配额预留服务失败)才不消耗槽位。 - 两条 401 路径都设置了
WWW-Authenticate,包含realm="worldmonitor"以及指向/.well-known/oauth-protected-resource的resource_metadata指针。支持 RFC 9728 的客户端(Claude Desktop、MCP Inspector)会凭此头自动跳转 OAuth 流程,无需进一步干预。
JSON-RPC 错误代码
下面的小节给出每个代码的字面负载、触发站点以及应对方式。
-32001 —— 未鉴权 / 凭证无效
由 api/mcp/auth.ts 的鉴权解析路径触发,总是配对 HTTP 401 和 WWW-Authenticate 头。用户可见的触发点按客户端命中顺序如下:
- 既无
Authorizationbearer 也无X-WorldMonitor-Key—— 客户端在调用/mcp时未携带任何凭证。 Authorization: Bearer <token>但<token>无效或已过期 —— 令牌无法解析到上下文(被撤销 / TTL 过期 / 从未由/api/oauth/token签发)。X-WorldMonitor-Key: <key>但<key>不在有效集合中 —— API key 错误。- OAuth 令牌可解析但 Pro MCP 令牌行缺失或跨绑定 ——
mcpTokenId不再映射到该 userId。通常是 Settings → Connected MCP clients 中的撤销操作所致。
-32002 / HTTP 403;无法确定或可重试的校验故障使用 -32603 / HTTP 503。
示例线上负载(情况 1):
/api/oauth/token 使用新的授权码,或用有效 refresh token 做刷新授权)。对于 API key 客户端,复核 X-WorldMonitor-Key 头 —— 用户签发的 wm_ key 与运维签发的企业 key 必须放在该头中,而不是作为 Authorization: Bearer。WWW-Authenticate 头中的 resource_metadata 指针是从零开始发起发现流程的权威入口。
-32002 —— 终止性权益拒绝
权益拒绝通常为 HTTP 403,带 Cache-Control: no-store,且不带 WWW-Authenticate:凭证仍有效,但当前权益不允许该调用,重新鉴权无法改变结果。error.data.reason 区分两种情况:
lapsed-subscription:这是一个罕见的执行中竞态:订阅在权益预检查通过之后、Pro 工具的下游抓取之前失效。如果权益预检查时已有服务方确认的失效结果,账户会被重新分类到受限免费账户路径,不会在预检查中发出此拒绝。较晚出现的下游BillingDenialError会由api/mcp/dispatch.ts重新发出,并带X-Billing-Verification: subscription_lapsed,因为该次执行中的 Pro 操作已无法完成。upgrade-required:免费额度账户调用了subscription工具,或经确认的非免费权益不足(例如已过期或停用的付费权益行)。这类拒绝发生在额度预留之前,不消耗调用槽位。
worldmonitor://account/mcp-allowance 时,-32002 位于 HTTP 200 的普通 JSON-RPC 错误中,不带 data 负载。
如何处理。 对于已发出的 -32002,不要重试,也不要重新运行 OAuth,两者都会重现同一拒绝。向用户显示权益状态:执行中失效的操作需要恢复订阅后再发起,upgrade-required 需要改用 free-account 工具或订阅 Pro。后续预检查若已确认覆盖期结束,会改走受限免费账户路径,客户端可继续使用 free-account 工具。如果订阅仍在服务方复核中,服务器会改为返回可重试的 -32603 / HTTP 503,并附带 Retry-After。
-32003 —— 必需数据输入不可用
工具已开始运行,但无法读取所需的上游种子数据(例如 Redis 瞬时故障,或 seeder 尚未发布)。响应位于 HTTP 200 中,并通过结构化 data 负载列出问题输入:
retryable: true 就是此契约。如果某个工具持续返回 -32003,请检查 data 中列出的输入及 status.worldmonitor.app。
-32004 —— 找不到 SSE 重放光标
带 Last-Event-ID 的 GET /mcp 尝试恢复一个当前 edge 实例不再保留的流:有界内存重放缓冲可能已过期,或重连落到了另一个实例。返回 HTTP 404。
如何处理。 重新发出原始 POST,不要继续恢复;将 SSE 重放视为容许丢失的传输层恢复,而非持久化存储。若重放 GET 缺少 Accept: text/event-stream,会返回 HTTP 406;若缺少有效 Mcp-Session-Id,则会在到达此检查前以 -32600 / HTTP 400 失败。
-32029 —— 被限流(每分钟或每日)
每分钟与每日上限触发条件共用此代码;由 HTTP 状态码区分。
每分钟节流 —— HTTP 200。 滑动窗口限流器为 60 次请求 / 分钟,按 API key(Starter 及以上)、按 Pro 用户(该用户所有令牌合计)或按 IP(用于匿名公开发现)键控。已鉴权请求在鉴权之后受限。无凭证的公开发现方法(initialize、notifications/initialized、tools/list、resources/list、resources/templates/list)与匿名公开资源读取无需鉴权即可服务,但仍会经过匿名发现限流器。无凭证的数据 / 配额方法,或该公开集合之外的元数据方法,不使用匿名发现 —— 它们以 -32001 / HTTP 401 故障关闭。以 HTTP 200 内的 JSON-RPC 错误返回,因为限流器位于任何按 id 关联的上游。在 Upstash 瞬时错误时故障开放 —— 限流器后端的偶发延迟尖峰不会拖垮整个 API。
当每分钟限流器拒绝时,handler 会发出一条持久的 mcp.rate_limit_hit 遥测事件,其身份形态经过允许列表过滤。套餐限制扫描器用该事件做持续突发通知;它不会从原始 Upstash 限流器内部推断面向客户的 MCP 突发通知。
message 文本标识了是哪个限流器触发。有三种不同字符串:
示例负载(Pro 变体 —— env_key 客户端得到相同信封形状,但使用 API-key 的 message 字符串):
Retry-After。 硬性每日上限(默认 50 次配额消耗调用 / UTC 日)由工具运行之前的原子 Redis 预留执行,因此恰好跨越边界的那次调用会被拒绝。只有 tools/call 和对数据承载 URI 模板实例化的 resources/read(鉴权对称的 resources 路径)计数。控制面板签发的 wm_… API-key 调用者使用每日 50 次默认值。OAuth 额度按套餐解析;API Starter 和 API Business 当前使用相同默认值,企业 OAuth 可以不设上限。只有部署许可名单中的旧版运营方密钥不进入每日预留路径。豁免每日上限: describe_tool、get_sources、tools/list、prompts/list、prompts/get、resources/list、resources/templates/list、logging/setLevel、initialize、notifications/initialized、ping,以及对公开(具体、仅元数据)资源的 resources/read,例如 worldmonitor://seed-meta/freshness。(这些方法仍计入已认证调用的每分钟限制;匿名 get_sources 改用独立的每 IP 每分钟 10 次失败关闭限额。)
Retry-After(该值为 距 UTC 午夜的秒数)。若 MCP 每日上限是批量工作的瓶颈约束,请使用 REST/API 路径,或联系 Enterprise 获取自定义 MCP 限制。
付费套餐客户还会在持续用量越过套餐阈值时收到账户通知与有界节奏的邮件。这些通知绝不意味着自动升级、自动超额收费或自动迁入 API Business;支持或结账动作是显式的。
-32600 —— 请求信封无效
当请求体不是合法 JSON,或是合法 JSON 但缺少字符串类型的 method 字段时触发。位于 api/mcp/handler.ts 的两个站点。严格来说是客户端编码器 bug —— 格式良好的 JSON-RPC 客户端在生产中永远不会看到它。
method,以及(除 notifications/* 外的任何方法都需要的)id 字段。如果你从已知良好的客户端库看到 -32600,请针对本服务器提交 issue —— 它不应到达你这一侧。
-32601 —— 方法未找到
method 字段是字符串但未匹配到任何 handler。本服务器支持的方法:initialize、notifications/initialized、ping、tools/list、tools/call、prompts/list、prompts/get、resources/list、resources/templates/list、resources/read、logging/setLevel。
initialize 响应中 capabilities 块里存在的方法。注意 resources/subscribe 未实现(initialize 握手明确宣告 resources.subscribe: false)—— 尝试调用它的客户端会得到 -32601。
-32602 —— 参数无效
最常见的错误代码,跨 tools/call、prompts/get、resources/read 和 logging/setLevel 共用。六种具体触发条件:
message —— 它总是告诉你缺少或错误了什么。对于工具,名称在 tools/list 中。对于提示,名称 + 参数 schema 在 prompts/list 中。对于资源,具体 URI 在 resources/list 中,参数化 URI 模板在 resources/templates/list 中。对于 logging/setLevel,有效级别是上面列出的 RFC 5424 子集。
-32603 —— 内部错误
四类不同条件共用此代码;HTTP 状态码以及计费校验场景下的 X-Billing-Verification 响应头用于区分重试方式。
HTTP 200 —— 工具执行失败。 工具分发器抛出异常。最常见情形:工具读取的每个 Redis key 都返回 null(cache_all_null —— 瞬时 Redis 抖动或仍在预热的种子程序),或一个同侪内部抓取在调用中途失败。Pro 配额不会回滚:工具已执行,因此重试会再消耗一个槽位。
MCP_INTERNAL_HMAC_SECRET,或 Pro/免费账户额度预留 Redis 失败时使用固定 Retry-After: 5。续订校验进行中或失败时也返回 503,但携带 X-Billing-Verification 与动态 Retry-After;权益后端不可达使用固定 5 秒。
message 文本标识触发条件:
非计费校验基础设施故障遵循固定
Retry-After: 5。计费校验行会携带 X-Billing-Verification,其 data.code 与该响应头一致;renewal_verification_pending / renewal_verification_failed 使用与实际提供方复查相匹配的动态 Retry-After(1–60 秒),客户端必须遵循响应头而不是假定 5 秒。entitlement_verification_unavailable 表示权益后端没有给出结论,固定使用 Retry-After: 5。续订校验状态表示本地订阅刚到期、服务器正在向计费提供方复核;已续订账户通常会在一到两次重试内恢复。
HTTP 200 —— resources/read 负载为空或不可解析。 resources/read 内部的防御性检查,针对内部 tools/call 分发器返回的 content[0].text 为空或非 JSON 文本的不应发生情形。
如何处理。 对于 HTTP 200 工具错误:约 1 秒后重试一次;若某个工具持续返回 -32603,请在 status.worldmonitor.app 查看相关种子程序。对于 HTTP 503:遵循 Retry-After。对于 resources/read 防御性情形:提交 issue —— 它表明你调用上游存在分发器契约违规。
HTTP 状态码
MCP handler 可能返回的每个状态码。大多数 JSON-RPC 回复 —— 包括大多数错误 —— 按惯例是 HTTP 200;下表标出 handler 升级状态码的情况。
有一个 HTTP 状态码不属于 JSON-RPC 错误:
- 405 携带空主体来自 JSON-RPC 之前的方法校验。handler 接受
POST(JSON-RPC 路径)、GET(Last-Event-IDSSE 重放;无 SSEAccept时也可返回 200 markdown 指南)、HEAD(与 GET 相同的路由:重放确认、指南响应头,或非/mcp路径上的 JSON 200 探针确认)和OPTIONS(CORS 预检)。只有带 SSEAccept但缺少Last-Event-ID的GET/HEAD才以 405 表示“不提供独立流”;其他不支持的方法也返回 405 +Allow: POST, GET, HEAD, OPTIONS。该端点不强制Origin允许列表:它通告通配 CORS,并通过显式Authorization/X-WorldMonitor-Key头鉴权,因此浏览器来源客户端(任意来源)均被接受。
软行为信封
软信封是高频失败模式,也是只检查 JSON-RPC 层的客户端最常遇到的解析 bug。tools/call 返回 HTTP 200 且没有 error 字段,result.content[0].text 可解析为 JSON,所得对象带有一个前导下划线判别键。务必:
- 将
result.content[0].text解析为 JSON。 - 检查解析后的对象顶层是否含有
_budget_exceeded或_jmespath_error键。若有,视为错误,不要将同侪字段当作数据消费。 - 否则,将解析后的对象视为该工具的正常响应(缓存工具会将其包成
{ cached_at, stale, data };RPC 工具返回其声明的形状)。
_budget_exceeded —— 响应超出每工具预算
每个工具声明一个每工具输出预算(_outputBudgetBytes),其大小设定为使响应能容纳在典型 agent 上下文窗口内。当序列化响应在所有每工具过滤器、summary 和 JMESPath 都已应用之后仍超出该预算时,分发器会用此信封替换超限负载 —— 仍在正常 MCP result 中,仍为 HTTP 200,仍无 isError:
text 负载:
_budget_exceeded: true—— 判别字段。始终字面为true;绝不会出现在成功响应中。budget_bytes: number—— 响应所对照检查的每工具预算。actual_bytes: number—— 所有收窄后序列化响应的 UTF-8 字节长度。hint: string—— 恢复建议。文本因调用者是否已传入jmespath参数而异;两种措辞都要求你收窄结果。
country、since、limit),或两者并用。JMESPath 指南 有投影的示例。summary: true 标志(每个缓存工具都接受)返回一个服务端构建的计数与样本摘要,始终在预算以内。
_jmespath_error —— 投影失败
三种失败类型,都以相同信封形状返回。_jmespath_error 的值是一个字符串(不是对象);其内容为 <kind>: <details>。判别依据是第一个 : 之前的开头 kind 词元。
original_keys 是未投影响应的顶层键(上限 50 项,截断时带有 ...<N more> 哨兵)。其存在正是为了让 LLM 能在下一次 tools/call 时自我纠正而无需重新抓取 —— 投影失败了,但工具抓取本身是成功的。
配额。 Pro 每日配额槽位不回滚。工具抓取已成功;失败的是用户提供的投影。一个错误表达式每次尝试消耗一个配额槽位,这正是 original_keys 存在的原因 —— 让重试在额外一次调用内自我纠正,而非在 N 次调用上盲目猜测。
三种类型:
expression_too_long
JMESPath 表达式本身超过 1024 个 UTF-8 字节(JMESPATH_MAX_EXPR_BYTES)。此上限有意设得宽松 —— 真实表达式通常为 50–200 字节 —— 而一个 1024+ 字节的表达式几乎总是意味着误把整个负载复制粘贴进了参数。
invalid_expression
JMESPath 引擎解析表达式时抛出 —— 语法错误、未闭合的方括号、未知函数。kind 词元之后的 details 是解析器错误信息的逐字内容。
[?country == "Iraq"]),而 JMESPath 要求单引号([?country == 'Iraq']);(b) 用了裸数字字面量([?deathsBest > 0]),而 JMESPath 要求反引号([?deathsBest > \0`]`)。JMESPath 指南 涵盖了这两个坑。
projection_too_large
表达式解析并运行成功,但投影输出在字符串化后超过 256 KB(JMESPATH_MAX_OUTPUT_BYTES)。几乎总是表明一个失控的 multiselect-hash 或 multiselect-list 在大型数组上重复复制字段。
[?...]),或对结果切片([0:N])。管道组合子(见 JMESPath 指南 示例 12)在此组合良好。
其他工具专属信封
少数工具在content[0].text 内返回自己的应用层错误信封,而非通过 JSON-RPC -32602。这些在 工具参考 中按工具记录 —— 本目录列出它们以便客户端识别该模式:
describe_tool返回{ "error": "missing_tool_name", "hint": "..." }或{ "error": "unknown_tool", "requested": "...", "available": [...] }。配额豁免 —— 错误输入不消耗配额槽位。见 工具参考 →describe_tool。
_budget_exceeded、_jmespath_error)为键,对每工具信封以顶层 error: string 为键。
路线图
- 预算超限时自动摘要。 未来的协议修订可能让
_budget_exceeded响应在信封之外内联附带一个服务端构建的摘要(一个带注解的内容块),适用于摘要定义良好的那部分工具。推迟到生产遥测数据能证明每工具权衡合理之时。
另请参阅
- MCP Server 概览 —— 端点、鉴权模式、OAuth 配置、套餐与配额。
- JMESPath 投影指南 —— 投影语法 + 12 个示例;学习如何修复
_jmespath_error以及从_budget_exceeded恢复的正确去处。 - MCP 工具参考 —— 每工具参数、响应形状及每工具软信封(例如
describe_tool)。 - MCP 快速入门 —— 五分钟从零到首次调用的入门。
- JSON-RPC 2.0 规范 —— 本目录通篇引用的线上信封形状。
- RFC 9728 —— OAuth 2.0 Protected Resource Metadata ——
WWW-Authenticate的resource_metadata指针的含义。
