失败有两种形状,混淆它们会白费时间。
| 形状 | 含义 | 在哪里决定 |
|---|---|---|
HTTP 4xx,没有 JSON-RPC 结果 | 调用没有到达工具 | 方法白名单或凭证,见第 1、2 道闸 |
HTTP 200,带 "isError": true | 调用到达了工具;操作失败了 | 路径策略或 provider,见第 3、4 道闸 |
200 不等于成功。每次 tools/call 都要检查 result.isError。
传输层失败
/mcp 上的 401
HTTP/1.1 401 Unauthorized
www-authenticate: Bearer resource_metadata="https://<host>/.well-known/oauth-protected-resource"
{"error":"Unauthorized"}无论什么原因,正文都一样。三种原因会产生它:
| 成因 | 怎么判断 | 修法 |
|---|---|---|
| 匿名调用写工具 | 你没发 Authorization 头 | 跟着挑战走 —— 授权客户端 |
| 方法不在匿名白名单上 | 该方法不是 initialize / tools/list / resources/list / prompts/list / 被允许的 tools/call | 认证,或改用被允许的方法 |
| 凭证被拒 | 你发了头,但它格式错误、未知,或由另一个 blocklet 签发 | 从这个 host 取凭证。凭证与实例绑定 |
匿名状态下调一个不存在的工具名也返回 401,而不是「没有这个工具」—— 白名单在查找工具之前就检查了。如果你在 tools/list 里看到过的工具名返回 401,那它是特权工具,不是拼写错误。
授权流程失败
全部是 400,带 RFC 6749 错误码。
| 请求 | 响应 |
|---|---|
POST /oauth/register 缺 redirect_uris | {"error":"invalid_redirect_uri","error_description":"Missing redirect_uris"} |
POST /oauth/register 用了不支持的 scheme | {"error":"invalid_redirect_uri","error_description":"redirect_uris must be https, RFC 8252 loopback http, or a private-use scheme …"} |
POST /oauth/token 用未知或已过期的 device_code | {"error":"invalid_grant","error_description":"Invalid or expired device_code"} |
POST /oauth/token 用其他任何 grant | {"error":"unsupported_grant_type","error_description":"Only device_code, authorization_code, and refresh_token grants are supported"} |
另有两种你应当预期、但上表没有展示的:
- 注册按来源地址限流。 每次启动都重新注册的客户端最终会被限流。注册一次,把
client_id存下来。 - 从未在一次完成的授权中被使用的注册,七天后被清除。 长期存在但从未获得同意的客户端需要重新注册。
device code 上的 invalid_grant 多数意味着五分钟窗口过期了,而不是码写错了。重新发起流程即可。
调用内失败
这些以 200 加 isError 返回。文本是一个错误码,后跟路径与解释。
AFS_FORBIDDEN
AFS_FORBIDDEN: Forbidden at /instance/notes.txt: network clients cannot write base paths;
use a resolver overlay or internal code该 blocklet 没有把这条路径向网络客户端开放。各操作的变体:
| 操作 | 文案 |
|---|---|
| write | cannot write base paths; use a resolver overlay or internal code |
| read | cannot read base paths; declare a networkRead rule or use an overlay |
| list | cannot list base paths; declare a networkRead rule or use an overlay |
| stat | cannot stat base paths; declare a networkRead rule or use an overlay |
凭证修不了这个。 owner 身份的调用方得到同样的拒绝。只有 blocklet 自己的声明能改变它 —— 见访问分档第 3 道闸。
AFS_NOT_FOUND
AFS_NOT_FOUND: Path not found: /new该路径在这个 host 上不存在。路径按 host 划定作用域,因此同一条路径可能在一个 blocklet 上解析得到、在另一个上不存在,即使两者挂载了同名 provider。先对父路径做 afs_list 确认,再判断 provider 是否缺失。
按顺序排查
顺着闸往下走,每一步都排除掉它上面的全部可能。
- 匿名
tools/list返回200吗? 否 → host 不可达,或者它不是一个 ARC blocklet。检查 URL 与/.well-known/mcp.json。 - 这个工具出现在
tools/list里吗? 否 → 若是内容工具,说明该 blocklet 没有声明集合;见声明 agent 能看到什么。 - 调用它返回
401吗? 是 → 第 1 或第 2 道闸。跟着WWW-Authenticate挑战走。 - 返回
200且带AFS_FORBIDDEN吗? 是 → 第 3 道闸。凭证没问题,是路径对网络关闭。 afs_stat的capabilities里有这个操作吗? 否 → 第 4 道闸。provider 没有实现它。
刷新凭证
device grant 返回的凭证没有有效期,也没有 refresh token,所以没有需要刷新的东西。如果凭证失效,用你的工具重新登录一次即可,见接入你的工具。