调试、检查器与可观测性
MCP 是"进程 + 协议"的组合,问题往往藏在线程之间:握手没完成、传输不匹配、日志进了协议流。本章介绍官方检查器 MCP Inspector、服务器日志的正确姿势,以及把 MCP 调用纳入链路追踪的思路。
用 MCP Inspector 调试服务器
官方检查器让你在图形界面里连接服务器、浏览工具并逐个试调:
pip install "mcp[cli]"
mcp dev server.py # 启动服务器并自动打开 Inspector
# 输出:Inspector 已启动:http://127.0.0.1:6274
操作步骤:页面选传输类型(stdio 填启动命令,或直接填远程 HTTP URL)→ Connect → 左侧看到工具/资源/提示词清单 → 逐个工具填参数试调,观察校验错误与返回内容。改服务器代码后重启 mcp dev 再连。也可用 npx 直接起检查器:npx @modelcontextprotocol/inspector。
服务器日志:stdio 场景必须走 stderr
stdio 传输下,stdout 是协议通道(承载 JSON-RPC 消息),print 会把协议流污染成乱码、直接导致握手失败。自己的日志一律输出到 stderr:
# log_server.py 片段
import logging
import sys
logging.basicConfig(level=logging.INFO, stream=sys.stderr) # 必须 stderr
log = logging.getLogger("my-server")
@mcp.tool()
def add(a: int, b: int) -> int:
log.info("add called: %s + %s", a, b) # 进 stderr,安全
return a + b
宿主日志会捕获服务器 stderr(如 Claude Desktop 的日志目录、IDE 的输出面板,位置以各家文档为准),报错信息从那里看。
常见错误速查
| 症状 | 常见原因 | 排查方向 |
|---|---|---|
| 连接即断开/握手失败 | 服务器进程没起来,或 initialize 未完成 | 单独运行启动命令看 stderr |
| 传输不匹配 | 用 stdio 客户端连 HTTP 服务,或反之 | 确认两端传输一致(stdio / streamable-http) |
| 工具调用报不存在 | 服务器未注册该工具,或名称拼写不一致 | list_tools 核对名称与参数名 |
| 参数校验失败 | 实参与 inputSchema 不符 | Inspector 里看 schema 再试调 |
| 权限/路径被拒 | 服务器白名单外的路径 | 调整白名单根目录 |
| 输出乱码或挂起 | print 污染了 stdout 协议流 | 日志改走 stderr |
| 两端行为不一致 | MCP SDK 版本差异过大 | 对齐 mcp 版本后再测 |
把 MCP 调用纳入可观测性
生产环境建议给每次工具调用打 trace:在 call_tool 外包一层 span,记录服务器名、工具名、入参与出参(注意脱敏)。思路如下:
# 观测封装示意:接入 OpenTelemetry / Langfuse 时套用
def traced_call(session, tool_name, args):
span = tracer.start_span(f"mcp.tool.{tool_name}")
span.set_attribute("mcp.server", server_name)
span.set_attribute("mcp.args", preview(args)) # 截断 + 脱敏
try:
result = session.call_tool(tool_name, args)
span.set_attribute("mcp.result", preview(result))
return result
finally:
span.end()
Langfuse 等平台也支持把 MCP/LLM 调用建模为 gen_ai 类型的 span,直接把工具名、模型、耗时与费用一起可视化;落地细节(SDK 版本、导入路径)以对应平台的文档为准。 小结:调试 MCP 先上 Inspector(mcp dev / npx @modelcontextprotocol/inspector),stdio 服务器日志必须走 stderr;把 call_tool 包成 span 接入 OpenTelemetry/Langfuse 就能在链路里看到每次工具调用。