1. 为什么 MCP 需要可观测性
一次 tools/call 的失败,可能是模型选错了工具、参数 schema 不匹配、传输断连、服务器内部异常,甚至是外部依赖超时。调用链跨越客户端、传输层、服务器、下游系统四层,任何一环出问题都表现为「Agent 行为不对」。
一句话:没有可观测性的 MCP 系统,排查问题时就像在黑屋里找一只不叫的猫——你只知道「不对劲」,不知道「哪里不对劲」。
1.1 调用链分层
Agent 决策 → 客户端(协议层) → 传输层 → 服务器(协议层) → 业务逻辑 → 外部依赖
↑ |
└──────────────────── 返回链路 ◄─────────────────────────────┘
每一层都要能回答三个问题:发生了什么、花了多久、结果是什么。
2. 请求链路追踪
给每条 MCP 消息贯传一个 traceId,是打通多层的第一步。JSON-RPC 的 id 只能关联请求与响应,无法串联「一次 Agent 决策引发的多轮调用」,所以需要额外的追踪字段。
2.1 在客户端注入追踪头
// 为每次 tools/call 生成 traceId,并记录到本地 span
import { randomUUID } from "crypto";
function withTracing(call: () => Promise<any>, meta: Record<string, unknown>) {
const traceId = randomUUID();
const spanId = randomUUID();
const start = Date.now();
return call()
.then((result) => {
emitSpan({
traceId, spanId,
parentId: meta.parentId,
tool: meta.tool,
status: result.isError ? "error" : "ok",
durationMs: Date.now() - start,
});
return result;
})
.catch((err) => {
emitSpan({ traceId, spanId, tool: meta.tool, status: "error", durationMs: Date.now() - start, error: err.message });
throw err;
});
}
2.2 服务器端透传
// 服务器端从请求头读取 traceId,注入业务日志
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
const traceId = request.params._meta?.traceId ?? randomUUID();
const logger = logger.child({ traceId, tool: request.params.name });
logger.info("收到工具调用");
// ... 业务处理
logger.info("工具调用完成", { durationMs });
});
一句话:traceId 就像快递单号——从下单到收货的每一站都扫一下码,才能定位是哪个中转站卡住了。
3. 日志埋点
链路追踪解决「串起来」,日志埋点解决「看得清」。MCP 服务器应覆盖三类日志。
3.1 协议层日志
initialize握手成功/失败(记录协议版本协商结果)- 每次
tools/list、resources/read、prompts/get的耗时 - 传输断连与重连事件
// 统一的请求日志中间件
function requestLogger(handler: RequestHandler): RequestHandler {
return async (request) => {
const start = Date.now();
const method = request.method as string;
try {
const result = await handler(request);
console.log(
JSON.stringify({
ts: new Date().toISOString(),
level: "info",
method,
durationMs: Date.now() - start,
outcome: "success",
})
);
return result;
} catch (err) {
console.error(
JSON.stringify({
ts: new Date().toISOString(),
level: "error",
method,
durationMs: Date.now() - start,
outcome: "error",
error: err instanceof Error ? err.message : String(err),
})
);
throw err;
}
};
}
3.2 业务层日志
业务日志要记录工具调用的入参摘要(脱敏后)与出参体积,方便还原现场:
def log_tool_invocation(tool: str, args: dict, result: str, duration_ms: float):
logging.info({
"event": "tool_invocation",
"tool": tool,
"args_preview": truncate(sanitize(args), 200), # 脱敏 + 截断
"result_bytes": len(result),
"result_preview": truncate(result, 100),
"duration_ms": duration_ms,
})
4. MCP Inspector 用法
@modelcontextprotocol/inspector 是官方图形化调试工具,能省掉 80% 的本地联调时间。
4.1 启动与连接
# 全局安装并启动
npx @modelcontextprotocol/inspector
# 在浏览器中打开的配置界面里填入服务器命令:
# Command: npx
# Args: -y tsx /path/to/mcp-server/src/index.ts
4.2 Inspector 的核心能力
| 功能 | 说明 | 排查场景 |
|---|---|---|
| 工具列表浏览 | 查看服务器注册的 tools 与 schema | schema 错误、缺少工具 |
| 手动调用 | 填参数直接发 tools/call | 复现业务 bug |
| 资源读取 | 测试 resources/read | 资源 URI 拼写 |
| 消息原始视图 | 查看完整 JSON-RPC 帧 | 协议层异常 |
| 错误面板 | 展示所有 error 响应 | 定位错误码 |
# 命令行快速冒烟:列出工具
npx @modelcontextprotocol/inspector --transport stdio --cmd "npx -y tsx ./src/index.ts" --list-tools
一句话:Inspector 相当于给 MCP 服务器装的「体检仪」——不用写测试代码就能逐个能力按一按、点一点、看响应。
5. 错误分类与定位
生产环境的错误五花八门,先分类再定位能显著缩短 MTTR。
5.1 错误分类矩阵
| 类别 | 错误码/信号 | 定位手段 | 典型根因 |
|---|---|---|---|
| 协议层 | -32601/-32602 | Inspector + 原始帧 | method 拼写、schema 不匹配 |
| 传输层 | 断连/超时 | traceId + 网络监控 | 负载均衡、防火墙 |
| 工具层 | isError=true | 业务日志 | 下游接口变更 |
| 模型层 | 未调用/调错工具 | Agent 侧日志 | 描述歧义、工具过多 |
5.2 自动定界脚本
def classify_error(error: dict, log_lines: list) -> str:
code = error.get("code")
if code in (-32700, -32600):
return "parse/invalid_request"
if code == -32601:
return "method_not_found"
if code == -32602:
return "invalid_params"
if code in (-32002, -32003, -32004):
return "resource_missing"
if any("ETIMEDOUT" in line or "ECONNRESET" in line for line in log_lines):
return "transport_timeout"
return "tool_internal_error"
6. 生产监控指标
生产环境需要三类核心指标,建议对接 Prometheus + Grafana,与现有运维体系打通。
6.1 核心指标定义
| 指标 | 类型 | 含义 | 建议告警阈值 |
|---|---|---|---|
mcp_calls_total{tool,status} | Counter | 调用量与成功率 | 错误率 > 5% |
mcp_duration_seconds{method} | Histogram | P50/P95/P99 延迟 | P95 > 2s |
mcp_active_sessions | Gauge | 并发会话数 | 逼近资源上限 |
mcp_tool_result_bytes{tool} | Histogram | 返回体积 | 均值异常增长 |
6.2 Prometheus 指标暴露
// 用 prom-client 暴露 MCP 服务器指标
import { Histogram, Counter, Gauge } from "prom-client";
export const callCounter = new Counter({
name: "mcp_calls_total",
help: "MCP 工具调用总量",
labelNames: ["tool", "status"],
});
export const durationHist = new Histogram({
name: "mcp_duration_seconds",
help: "MCP 请求耗时分布",
labelNames: ["method"],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5],
});
export const sessionGauge = new Gauge({
name: "mcp_active_sessions",
help: "当前活跃会话数",
});
// 在调用处理器里埋点
callCounter.labels(toolName, result.isError ? "error" : "ok").inc();
durationHist.labels(method).observe((Date.now() - start) / 1000);
6.3 告警策略
- 可用性告警:错误率连续 5 分钟 > 5%,触发 P1。
- 延迟告警:P95 连续 10 分钟 > 2s,触发 P2。
- 结果异常:
mcp_tool_result_bytes均值相比基线翻倍,提示上下文膨胀风险。
7. 回归测试
可观测性解决「出了事能查」,回归测试解决「出了事不复发」。用 InMemoryTransport 做协议级测试是性价比最高的方式。
7.1 协议冒烟测试
// tests/protocol.spec.ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
import { server } from "../src/server";
async function makeClient() {
const client = new Client({ name: "test", version: "1.0" });
const [ct, st] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(ct), server.connect(st)]);
return client;
}
it("工具 schema 与调用一致", async () => {
const client = await makeClient();
const { tools } = await client.listTools();
const target = tools.find((t) => t.name === "get_weather")!;
expect(target.inputSchema.required).toContain("city");
const res = await client.callTool({
name: "get_weather",
arguments: { city: "Beijing" },
});
expect(res.isError).toBe(false);
});
7.2 回归基线
- 每个工具至少一条成功用例与一条失败用例;
- 每个错误码分支至少一条用例;
- 把手动 Inspector 联调时发现的 bug 固化为测试,防止复发。
8. 总结
MCP 可观测性是一条从开发到生产的完整链路,每一环都有明确的工具与指标:
| 阶段 | 手段 | 产出 |
|---|---|---|
| 本地开发 | mcp-inspector 手动调试 | 快速复现、schema 校验 |
| 链路贯通 | traceId 贯传 + span 采集 | 全链路定位 |
| 日志 | 协议层 + 业务层埋点 | 现场还原 |
| 生产 | 调用量/延迟/错误率指标 | 自动告警 |
| 持续 | 协议级回归测试 | 防复发 |
把可观测性做到位,Agent 在线上才能「跑得明白」。最后把视野拉远,看看 MCP 在整个生态中的位置——官方服务器、云托管方案,以及与 A2A 协议的竞合关系。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。