资讯详情

MCP Python SDK 服务端错误处理权威指南:ToolError、MCPError 与崩溃边界的正确选择

📅 2026/9/20 19:41:59 | 华诺云谱 👁 阅读
MCP Python SDK 服务端错误处理权威指南:ToolError、MCPError 与崩溃边界的正确选择
MCP Python SDK 服务端错误处理权威指南ToolError、MCPError 与崩溃边界的正确选择【免费下载链接】python-sdkThe official Python SDK for Model Context Protocol servers and clients项目地址: https://gitcode.com/gh_mirrors/pythonsd/python-sdk导读本文是 MCP Python SDKpython-sdk 官方文档docs/servers/handling-errors.md的深度展开系统讲解 MCP 服务端在工具Tool与资源Resource执行失败时的三种错误处理路径抛出ToolError让模型读到消息并自我纠错、抛出MCPError让整个请求以JSON-RPC 协议错误失败、或放任其他异常变成崩溃仅由日志记录。读完本文你将掌握一套清晰的决策标准更聪明的模型能否避免这个错误、完整的可运行示例以及错误在客户端、服务器日志和测试中的真实表现能够为自己的 MCP Server 写出行为可预期、对 LLM 友好、便于排查的错误处理代码。一、三种失败方式一个框架一个工具Tool在运行中可能以三种方式失败SDK 对每一种的处理都完全不同抛出的异常谁看到消息请求结果服务器日志ToolError模型消息出现在content中成功返回is_errorTrue的结果一条INFO无 tracebackMCPError协议层 / Host 应用整个tools/call以 JSON-RPC 错误失败无 traceback其他任何异常模型只看到执行失败返回is_errorTrue消息被脱敏ERROR级别 完整 traceback本质上这一页讨论的是一件事选择——在不同的失败场景下你应该让哪一层模型、协议、还是日志去看见这个错误。下文分别展开。二、模型可修复的错误抛出ToolError2.1 最小示例查无此书设想一个查询书籍作者的工具查不到时让查询落空。完整代码见 docs_src/handling_errors/tutorial001.pyfrom mcp.server import MCPServer from mcp.server.mcpserver.exceptions import ToolError mcp MCPServer(Bookshop) CATALOG {Dune: Frank Herbert, Neuromancer: William Gibson} mcp.tool() def get_author(title: str) - str: Look up the author of a book in the catalog. if title not in CATALOG: raise ToolError(fNo book titled {title!r} in the catalog.) return CATALOG[title]ToolError的导入路径是mcp.server.mcpserver.exceptions。从源码 src/mcp/server/mcpserver/exceptions.py 看它是MCPServerError的子类其 docstring 明确说明其语义你预见到的工具失败——抛出后调用返回is_errorTrue、你的消息出现在content中供模型读取服务器以INFO级别记录、无 traceback。2.2 客户端视角请求成功错误是结果用文档目录下教程配套的测试 tests/docs_src/test_handling_errors.py 可以验证用目录中不存在的书名调用该工具客户端拿到的是一个正常返回的结果result.is_error # True result.content # [TextContent(textError executing tool get_author: No book titled Nothing in the catalog.)] result.structured_content # None三个关键观察这个请求本身是成功的。存在一个结果对象调用方没有抛任何异常。is_error为True你的消息以工具名get_author作为前缀出现在content中——这正是模型读取的地方。structured_content为None。失败的调用没有返回值可供结构化。这就是工具错误tool error在绝大多数场景下这正是你想要的。2.3 为什么这对 Agent 是对话中的一轮调用工具的是模型参数是它选的。因此一个ToolError相当于对话中的一次回合模型读到No book titled Nothing in the catalog.意识到自己猜错了书名然后带着更准确的参数再次调用。你只写了一个raise就得到了一个能自我纠正的 Agent。而在服务器端ToolError只在日志中留下一行INFO没有 traceback。你已经预见到这个错误自然没有值得深挖的东西——这正是它与崩溃的本质区别见第四节。要点永远不要用return返回错误消息。一个返回的字符串其is_errorFalse对模型以及对任何客户端 UI而言看起来就像是工具正常工作、这个字符串就是答案。必须raise——is_error标志才是信号。三、模型不可修复的错误抛出MCPError3.1 替换异常类型把ToolError换成MCPError见 docs_src/handling_errors/tutorial002.pyfrom mcp import MCPError from mcp.server import MCPServer from mcp.types import INVALID_PARAMS mcp MCPServer(Bookshop) CATALOG {Dune: Frank Herbert, Neuromancer: William Gibson} mcp.tool() def get_author(title: str) - str: Look up the author of a book in the catalog. if title not in CATALOG: raise MCPError(codeINVALID_PARAMS, messagefNo book titled {title!r} in the catalog.) return CATALOG[title]MCPError是 SDK 的协议错误protocol error。它是工具包装器唯一不捕获的异常它会一路向外传播导致整个tools/call请求以一个 JSON-RPC 错误告终而不是以结果告终。3.2 线上传输的错误客户端或任何遵守 JSON-RPC 的 Host收到的错误形如{ code: -32602, message: No book titled Nothing in the catalog. }没有结果。没有content、没有is_error——模型没有任何东西可读。Host 应用拿到这个错误就像这个工具根本不存在一样。code、message、data三个字段原样送达。INVALID_PARAMS即-32602mcp.types实际定义在 src/mcp-types/mcp_types/jsonrpc.py将其余标准 JSON-RPC 错误码也导出为常量你永远不需要手写魔法数字PARSE_ERROR -32700INVALID_REQUEST -32600METHOD_NOT_FOUND -32601INVALID_PARAMS -32602INTERNAL_ERROR -326033.3 客户端视角调用抛出而非返回同一份查书逻辑、同样的落空但这次客户端侧的行为截然不同——调用抛异常而不是返回结果对应测试 test_mcp_error_makes_the_call_itself_failmcp.shared.exceptions.MCPError: No book titled Nothing in the catalog.第一版给模型一句可以回应的句子这一版什么也没给它。对get_author这个例子来说这显然更差——这正是下一节要讲的决策标准。3.4 MCPError 的源码结构MCPError定义在 src/mcp/shared/exceptions.py构造函数签名为MCPError(code: int, message: str, data: Any None)。它内部把参数封装进ErrorData来自mcp_types并暴露code、message、data三个只读属性__str__直接返回message。你塞进构造函数的任何内容客户端就收到什么——SDK 对抛出的MCPError是逐字转发不做任何净化sanitise。官方还允许直接from mcp import MCPError无需深挖内部包路径。四、如何选择一条决策标准两条路径回答的是两个不同的问题抛出ToolError用于执行本身失败你的工具想做的事没做成。调用是模型选的所以模型应当看到后果、并有机会补救。拼错的书名、上游 API 超时、查询不存在的行——全都是工具错误。抛出MCPError用于拒绝请求本身客户端缺少你的工具依赖的 capability、服务器当前状态无法服务任何人、调用方跳过了必要步骤。这些情况模型重试多少次都无济于事把消息交给它没有任何收益。唯一的判断问题一个更聪明的模型本可以避免这个错误吗能 →ToolError不能 →MCPError。按此标准get_author的第二版MCPError版做出了错误的选择换个更好的书名就能解决问题所以模型理应看到这条消息。那一版存在的意义是演示机制而不是推荐用法。五、其他任何异常崩溃路径5.1 让字典查找自然失败现在移除检查让字典查找自己失败见 docs_src/handling_errors/tutorial004.pyfrom mcp.server import MCPServer mcp MCPServer(Bookshop) CATALOG {Dune: Frank Herbert, Neuromancer: William Gibson} mcp.tool() def get_author(title: str) - str: Look up the author of a book in the catalog. return CATALOG[title]CATALOG[title]会抛出KeyError。你没预料到它所以 SDK 把它当作崩溃处理。测试 test_any_other_exception_is_a_crash 精确验证了客户端与日志两端的表现。5.2 客户端与服务器各看到什么result.is_error # True result.content # [TextContent(textError executing tool get_author)]调用仍然返回is_errorTrue模型知道它失败了、可以继续下一步。但模型拿不到异常文本你代码里的一个KeyError或三层库之下的驱动抛出的整串 SQL都可能泄露服务器的内部实现因此它永远不会离开服务器。而你开发者拿得到。服务器以ERROR级别记录崩溃附带完整 traceback日志消息为Tool get_author raised an unexpected exception。由此产生一个非常实用的运维结论一个设为WARNING级别的生产日志会在所有ToolError期间保持安静而一旦真有东西坏了就立刻发声——崩溃与预期错误在日志里泾渭分明。5.3 源码中的实现证据在 src/mcp/server/mcpserver/exceptions.py 中UnexpectedToolError是ToolError的子类但你永远不会主动抛它——SDK 在工具或解析器崩溃、或返回值无法完成输出转换时自己抛出它。它的消息只有Error executing tool name嵌套工具或资源崩溃时同理原始异常作为__cause__被记录在服务器日志中。因此在MCPServer.call_tool()外层用except ToolError可以捕获所有工具失败含崩溃用except UnexpectedToolError则可以区分崩溃与有意的ToolError。六、资源Resource不存在时的错误处理6.1 资源同样有边界且自带命名异常资源处理函数画的是同一条线并为最常见的情况提供了一种命名异常。见 docs_src/handling_errors/tutorial003.pyfrom mcp.server import MCPServer from mcp.server.mcpserver.exceptions import ResourceNotFoundError mcp MCPServer(Bookshop) CATALOG {Dune: Frank Herbert, Neuromancer: William Gibson} mcp.resource(books://{title}) def book(title: str) - str: The catalog entry for one book. if title not in CATALOG: raise ResourceNotFoundError(fNo book titled {title!r} in the catalog.) return f{title} by {CATALOG[title]}books://{title}是一个模板template它匹配任何书名。因此URI 格式正确与这本书存在是两个不同的问题而只有你的函数能回答第二个问题。6.2 ResourceNotFoundError → -32602 并携带 URI当函数回答不了时抛出ResourceNotFoundError。SDK 会把它转成规范为资源缺失分配的协议错误-32602并把被请求的 URI 放进data这样客户端就知道哪一次读取失败了{ code: -32602, message: No book titled Nothing in the catalog., data: {uri: books://Nothing} }测试 test_resource_not_found_error_maps_to_invalid_params 断言客户端收到的ErrorData与上述逐字段一致。注意这里没有is_errorTrue的半成品结果。资源读取要么返回内容、要么失败资源只有协议路径protocol path。6.3 ResourceError 与资源的崩溃路径ResourceError用于不是找不到的失败-32603 你的消息。ResourceNotFoundError是它的子类源码见 exceptions.py两者在日志中都只是一行INFO。除MCPError外的任何其他异常都是崩溃客户端只会收到-32603、消息只点名 URI原始文本被扣留traceback 以ERROR级别进入你的日志。这个包装由UnexpectedResourceError完成同样是 SDK 自己抛出、你从不主动 raise原始异常挂在__cause__上。关于 URI 模板与资源的完整内容见 资源Resources。七、你永远不需要 raise 的错误输入 Schema 校验非法参数根本到不了你的函数。给get_author传一个不是字符串的titleSDK 会在调用你之前就按输入 Schema 拒绝它——拒绝方式与工具错误完全相同is_errorTrue、消息在content里模型可以读到并纠正。测试 test_a_bad_argument_never_reaches_the_function 传入{title: 42}断言返回is_errorTrue且消息包含Input should be a valid stringtest_a_bad_argument_is_an_info_line_not_a_crash 进一步验证这条拒绝在日志中只是INFO、无 traceback与崩溃严格区分。这意味着一整类你不需要写的raise不要重复校验自己的类型注解。参数校验失败被 SDK 视为预期内的失败——工具包装器同样会为未知工具名和参数校验失败抛出ToolError见 exceptions.py 的 docstring。工具Tools 中展示了用Field(le50)约束触发同样拒绝的写法。测试时的等价性本页客户端看到的一切你写测试用的内存Client也能看到。即使设置raise_exceptionsTrue失败工具的异常也不会回抛给调用方——等这个标志有机会生效时你的异常早已变成is_errorTrue的结果测试 test_raise_exceptions_does_not_turn_a_tool_error_into_a_traceback 验证了这一点。所以对结果做断言。若需要崩溃的 traceback它在服务器的日志里pytest 的caplog可以捕获。这一模式详见 测试Testing。八、总结错误处理决策速查在工具中抛出ToolError→ 调用返回is_errorTrue你的消息在content中。模型读到它并可以重试。抛出MCPError→ 调用本身以 JSON-RPC 错误失败。模型什么也看不到由 Host 处理。code、message、data原样穿透。决策问题一个更聪明的模型本可以避免这个错误吗能 →ToolError不能 →MCPError。任何其他异常都是崩溃→ 模型只得到Error executing tool name的is_errorTrue结果你得到一条带 traceback 的ERROR日志。从资源处理器抛出ResourceNotFoundError→ 协议的-32602URI 放入data。非法参数在函数运行前就被 Schema 拒绝这类场景不需要你写raise。导入方式from mcp import MCPErrorfrom mcp.server.mcpserver.exceptions import ToolError, ResourceError, ResourceNotFoundError错误码常量来自mcp.types如INVALID_PARAMS、INTERNAL_ERROR底层定义于 src/mcp-types/mcp_types/jsonrpc.py错误处理完毕——这正是一个服务器对外暴露的全部。而每个处理器handler在运行期间能读取什么、能对客户端做什么属于下一个主题处理器内部Inside your handler。至于你最可能遇到的 SDK 错误的确切文本、含义以及一招修复请查阅 故障排查Troubleshooting。延伸阅读仓库内四个教程源码tutorial001.py、tutorial002.py、tutorial003.py、tutorial004.py异常类实现src/mcp/server/mcpserver/exceptions.py、src/mcp/shared/exceptions.py逐条验证本文全部断言的测试tests/docs_src/test_handling_errors.py官方英文原文docs/servers/handling-errors.md【免费下载链接】python-sdkThe official Python SDK for Model Context Protocol servers and clients项目地址: https://gitcode.com/gh_mirrors/pythonsd/python-sdk创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。