F3.4 F3

别把错误伪装成空结果

工具失败时,最糟糕的做法是假装它成功了。MCP 的 CallToolResult 结构专门设计了显式的错误信号机制——但前提是你得正确使用它。

结构:content + isError

CallToolResult 有两个字段:

  • content — content block 数组(通常是 text),承载结果数据
  • isError — 布尔值,表示成功(false)或失败(true

成功时,content 装结果数据,isErrorfalse(或省略——默认就是 false)。失败时,content 描述出了什么问题,isErrortrue

content 字段既承载成功数据也承载错误描述。告诉 AI 模型如何解读 content 的是 isError 标记——不是 content 本身。

为什么需要显式标记

没有 isError: true,模型会把 content 当成功结果处理。如果你的工具返回 {"content": [{"type": "text", "text": "Permission denied"}], "isError": false},模型可能会试图从 “Permission denied” 里提取数据,当作正常响应。它没有可靠的方法仅从文本推断失败。

设了 isError: true 就不一样了:模型知道操作失败了,读取错误描述,然后推理如何恢复——换参数重试、试另一个工具、或者告诉用户。

伪装错误反模式

最危险的错误:把真正的错误转成假的”未找到”结果。假设一个客户查询工具碰到了权限错误。返回 {"content": [{"type": "text", "text": "No customer found"}], "isError": false} 在两个层面上都是错的:

  1. 客户可能存在——你只是没权限访问数据
  2. 模型现在告诉用户”没有找到客户”,这是假话

诚实的响应应该是:{"content": [{"type": "text", "text": "Permission denied: insufficient API key permissions"}], "isError": true}。模型了解到了真实问题,能告诉用户实际发生了什么。

MCP 的两层错误

MCP 区分 协议错误工具执行错误

  • 协议错误 — 调用不存在的工具、JSON-RPC 请求格式不对、参数无效。这些是传输层的 JSON-RPC 错误,说明 client 有 bug,不是运行时故障。

  • 工具执行错误 — API 超时、权限不足、资源未找到、触发限流。这些通过 CallToolResult 的 isError: true 传达,送达 AI 模型。模型可以推理并尝试恢复。

划分线在于:协议错误意味着”你调用方式不对”(开发者问题)。工具执行错误意味着”操作在运行时失败了”(可能可恢复)。不要用 JSON-RPC 错误来报告运行时故障——模型需要通过 isError: true 看到它们。

没有自动重试

MCP 不会自动重试失败的工具调用。isError: true 返回后直接送到 client。重试逻辑是 client 的责任——协议只负责报告发生了什么。这是有意为之:client(或 AI 模型)有上下文来判断重试是否合理。


一句话总结: 工具失败时用 isError: true 加描述性 content——绝不把错误伪装成空结果,并记住协议错误(JSON-RPC)和工具执行错误(isError)服务于不同的层。