跳转至

日志

机器翻译

本页由英文文档自动翻译而来,以英文页面为准。如果有读起来不对的地方,翻译页面说明了如何反馈。

在工具里记录日志,和在其他任何 Python 函数里一样:用标准库。

MCP 在协议层面有一个日志能力(logging capability):服务器可以通过 Context 对象上的方法,把自己的日志消息作为通知推送给客户端。规范的 2026-07-28 修订版弃用了这个能力,而且没有提供替代方案,所以本文档不讲它。哪些内容已弃用、该用什么代替,完整清单见 已弃用的功能

取而代之的做法,就是你在其他所有 Python 程序里的做法:标准库。

一个会记录日志的工具

server.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 f"Found 3 books matching {query!r}."
  • logging.getLogger(__name__) 返回一个以模块名命名的 logger。在文件顶部创建一次即可。
  • 在工具内部调用 logger.info(...),和在其他任何函数里一样。不用注入什么,不用 await 什么,也没有任何 MCP 特有的东西。

Check

调用这个工具,看看完整的结果:

result.content             # [TextContent(text="Found 3 books matching 'dune'.")]
result.structured_content  # {'result': "Found 3 books matching 'dune'."}

里面哪儿都没有那行日志。日志是给——运维这个服务器的人——看的。模型永远看不到它。如果某些内容应该让模型读到,就 return 它。

日志去哪了

stdio 服务器来说,这个问题比平时更要紧。宿主把你的服务器作为子进程启动,并从它的 stdout 读取 MCP 消息。标准错误才是你的。

标准库默认就做对了:日志输出默认写到 sys.stderr。你的 logger.info(...) 会落在终端里(或者宿主收集子进程 stderr 的任何地方),协议流保持干净。

Tip

不要在 stdio 服务器里 print()print 写的是 stdout,而 stdout 属于协议。在服务期间,SDK 会把真正被刷新(flush)出去的 stdout 转到 stderr,所以它不会破坏线路;但在块缓冲的进程里,print() 的内容通常会一直留在 sys.stdout 的缓冲区里没有刷新,直到解释器在退出时把它排空——直接排到协议流上。即使被转走了,这一行也是原样混在日志输出当中,没有级别、没有 logger 名称,也没办法过滤。

logger.debug("got here") 同样只是一行的功夫,而且会去到正确的地方。

日志级别

不需要自己调用 logging.basicConfig()。构造 MCPServer 时已经调用过了:配了一个指向标准错误的 handler,级别就是你通过 log_level= 传入的值。所以只要 MCPServer("Bookshop", log_level="DEBUG"),就能看到你的 logger.debug(...) 输出。

默认值是 "INFO"

logging.basicConfig() 永远不会替换已经存在的 handler。如果你在创建服务器之前自己配置了日志,以你的配置为准。

试一试

用 MCP Inspector 运行服务器:

uv run mcp dev server.py

Tools 标签页调用 search_books。Inspector 显示的结果只有返回值。这一行

Searching for 'dune'

去了标准错误:终端,而不是线路。

Info

如果你真正想要的是追踪(每个请求、耗时多久、是否失败),那你要的不是日志行,而是 span。你的服务器已经在产出它们了:SDK 默认就用 OpenTelemetry 追踪每一条消息。见 OpenTelemetry

回顾

  • MCP 协议的日志能力已被 2026-07-28 规范弃用,且没有替代。不要基于它构建。
  • 模块级写 logger = logging.getLogger(__name__),工具里写 logger.info(...)。整个模式就这些。
  • 日志输出永远到不了模型那里。只有你 return 的值才会。
  • 标准错误是你的;stdout 属于协议。服务期间 SDK 会把已刷新的零散 stdout 转到 stderr,但没刷新的 print() 仍可能在退出时排到线路上,而且被转走的行没有任何标记;用 logging,它的 handler 每条记录都会刷新。
  • MCPServer(..., log_level="DEBUG") 设置级别;你先做好的日志配置不会被改动。

告诉已连接的客户端服务器上有东西变了(工具列表、某个资源),见 订阅