资讯动态

MCP Python SDK 日志实践:标准库 logging、log_level 配置与 stdio 进程的 stdout/stderr 分流

发布时间:2026/9/20 19:01:33 来源:尧图企业网站定制
MCP Python SDK 日志实践标准库 logging、log_level 配置与 stdio 进程的 stdout/stderr 分流【免费下载链接】python-sdkThe official Python SDK for Model Context Protocol servers and clients项目地址: https://gitcode.com/gh_mirrors/pythonsd/python-sdk本篇技术指南围绕 MCP 官方 Python SDKmcp包的日志处理展开协议层的 logging capability 在 2026-07-28 规范修订中被弃用且无替代因此 SDK 推荐的唯一模式是使用 Python 标准库logging。读完本篇你将掌握工具内打日志的标准写法、MCPServer(log_level...)的底层配置机制、stdio 服务器下 stdout/stderr 的归属规则以及如何用 MCP Inspector 验证日志不会泄漏到协议链路。核心结论用标准库打日志不要用协议层日志能力MCP 协议本身曾提供一种日志能力logging capability服务器可以通过Context对象上的方法把日志消息以通知notification形式推送给客户端。2026-07-28 版本的规范弃用了该能力且不提供替代方案因此官方文档含本仓库的 法语版日志文档 与 英文原版不再教授它。所有已弃用功能的完整清单及替代做法见 已弃用功能英文对照deprecated。正确的做法就是在 MCP 服务器里打日志和在任何其他 Python 程序里一样——使用标准库logging。一个会打日志的工具完整示例官方文档给出的完整示例位于 docs_src/logging/tutorial001.py原文如下可直接复制运行import logging from mcp.server import MCPServer logger logging.getLogger(__name__) mcp MCPServer(Bookshop) mcp.tool() def search_books(query: str) - str: Search the catalog by title or author. logger.info(Searching for %r, query) return fFound 3 books matching {query!r}.要点logging.getLogger(__name__)给你一个以模块名命名的 logger。在文件顶部创建一次即可不要在每次调用里重复创建。在工具函数内部调用logger.info(...)与任何普通函数无异——不需要注入、不需要await、没有任何 MCP 专属 API。调用该工具并检查完整结果文档原文给出的验证方式result.content # [TextContent(textFound 3 books matching dune.)] result.structured_content # {result: Found 3 books matching dune.}注意日志行不在结果的任何位置。日志是为你服务器的运维者打的模型永远看不到它。如果模型需要读到某段内容请用return返回它。这个结论有测试背书tests/docs_src/test_logging.py 中test_the_log_line_never_reaches_the_client用inline_snapshot精确断言了call_tool返回的CallToolResult只包含content与structured_content不含任何日志痕迹test_the_tool_logs_through_the_standard_library则用 pytest 的caplog捕获到一条名为docs_src.logging.tutorial001、级别为INFO、消息为Searching for dune的标准库日志记录——证明工具内的日志走的就是普通 stdlib 通道。日志去哪里stdio 服务器下 stdout 与 stderr 的归属对于stdio服务器这个问题格外重要宿主host把你的服务器作为子进程启动并从其stdout上读取 MCP 协议消息。stderr 是你自己的。标准库默认行为恰好正确日志输出默认流向sys.stderr。你的logger.info(...)会落在终端或宿主收集子进程 stderr 的地方协议流保持干净。不要在 stdio 服务器里print()文档特别警告不要在 stdio 服务器中使用print()。print写入stdout而 stdout 属于协议。SDK 对此有底层防护。从源码结构看src/mcp/server/stdio.py 实现了一套“流认领stream claim”机制stdio 传输启动时通过_claim_fd(1, sys.stdout, ...)认领 fd 1并把协议链路放到一个私有 fd 副本上原始 fd 被重定向到 stderr 一侧_open_stdout_diversion()即os.dup(2)失败则退回/dev/null。这意味着服务期间真正被flush到 stdout 的内容会被 SDK 转移到 stderr无法污染协议链路但在块缓冲block-buffered进程中print()的输出通常滞留在sys.stdout的缓冲区里直到解释器退出时一次性冲刷——那一刻它会直接倒在协议流上即便被转移了这些行也是裸文本没有级别、没有 logger 名、无法过滤混杂在日志输出里。相比之下logger.debug(got here)是一行同样的工作量且日志处理器会逐条 flush每条记录去向也正确。日志级别log_level参数与configure_logging的底层实现你不需要自己调用logging.basicConfig()。构造MCPServer时 SDK 已经替你做了。从源码看这条调用链MCPServer.__init__的log_level参数定义为Literal[DEBUG, INFO, WARNING, ERROR, CRITICAL]默认值INFO见 src/mcp/server/mcpserver/server.py服务器构建/启动时执行configure_logging(self.settings.log_level)server.pyconfigure_logging的实现在 src/mcp/server/mcpserver/utilities/logging.py优先创建RichHandler(consoleConsole(stderrTrue), rich_tracebacksTrue)handler 显式指向stderr且带 rich 格式化与 traceback 美化rich不可用时退回logging.StreamHandler()最后调用logging.basicConfig(levellevel, format%(message)s, handlershandlers)。因此MCPServer(Bookshop, log_levelDEBUG)一行就足以让你看到logger.debug(...)的输出。两条重要的规则性事实均有测试覆盖logging.basicConfig()从不替换已存在的 handler。如果你在创建服务器之前自己配置了日志你的配置优先——SDK 不会覆盖它。tests/docs_src/test_logging.py 中test_an_existing_logging_configuration_wins先给 root logger 装上NullHandler并置WARNING再构造MCPServer(Bookshop, log_levelDEBUG)断言级别与 handler 均未被改动test_log_level_configures_the_root_logger则验证在无既有配置时 root 被设为DEBUG且恰好挂上 1 个 handler。不必在每个 handler 里try/except只为记录失败。当工具或资源函数抛出异常时SDK 会替你记录日志。记录内容与级别详见 错误处理英文对照Handling errors。动手验证用 MCP Inspector 跑一遍用 MCP Inspector 启动服务器uv run mcp dev server.py在Tools标签页调用search_books。Inspector 只展示返回值日志行Searching for dune走的是 stderr——去终端而不是协议链路。这正是上文“日志对模型不可见”的运行时验证方式。需要的是“追踪”而非“日志”如果你真正想要的是追踪每个请求、耗时、是否失败那你要的不是日志行而是spans。你的服务器已经在发出它们SDK 默认用 OpenTelemetry 为每条消息打追踪。参见 OpenTelemetry英文对照OpenTelemetry。小结MCP 协议的日志能力已被 2026-07-28 规范弃用且无替代不要在其上构建任何东西。模块级logger logging.getLogger(__name__)工具内logger.info(...)——这就是完整模式。日志输出永远不会到达模型只有return的值会。stderr 是你的stdout 属于协议。服务期间 SDK 会把被 flush 的“越界” stdout 转移到 stderr但未被 flush 的print()仍可能在进程退出时倒在协议流上且被转移的行没有任何标签请使用logging——其处理器会逐条 flush。MCPServer(..., log_levelDEBUG)设置级别你先配置好的日志设置会被原样保留。需要“服务器有东西变了工具列表、资源通知客户端”时那是 订阅Subscriptions英文对照subscriptions的职责。【免费下载链接】python-sdkThe official Python SDK for Model Context Protocol servers and clients项目地址: https://gitcode.com/gh_mirrors/pythonsd/python-sdk创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价