调试、检查器与可观测性

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 就能在链路里看到每次工具调用。

笔记加载中…