使用 OpenTelemetry 与 Elastic APM 追踪 MCP 服务器工具调用
1. 概述:让可观测性成为 AI 的"自省能力"
MCP(Model Context Protocol)服务器本质上就是一个普通的 Node.js 进程。这意味着,给它加上 OpenTelemetry 追踪,只需要一个 --import 参数即可启动自动插桩。
真正新颖的并非插桩本身,而是数据回流的设计:当链路数据进入 Elastic APM 后,同一次 Claude Desktop 会话可以通过 Elastic Agent Builder MCP 将这些追踪数据查询回来。AI 助手可以分析自己刚刚发起的工具调用延迟、识别慢工具、解释失败原因——全程不离开聊天窗口。
可观测性从此不再是"事后由人类打开仪表盘检查"的被动行为,而是变成了 AI 在工作过程中实时使用的上下文。本文将循序渐进地介绍:
- MCP 的 OpenTelemetry 语义约定
- 工具调用的手动 Span 包装模式
- 如何在 Elastic 侧完成"数据闭环"
2. 前置条件
在开始之前,请确保具备以下环境:
| 组件 | 版本/要求 |
|---|---|
| Elastic Cloud | 9.3+ 或 Serverless 版本 |
| Claude Desktop | 最新版 |
| 示例 MCP 服务器 | @modelcontextprotocol/server-everything(Anthropic 官方参考实现) |
| 插桩工具 | EDOT Node.js(@elastic/opentelemetry-node) |
3. MCP 服务器的可观测性盲区
MCP 服务器作为 AI 应用的基础设施层,为模型提供数据库、API、内部工具和业务数据的访问能力。然而,MCP SDK 本身不提供任何内置可观测性。这带来了三个具体痛点:
- 延迟黑盒:当一次工具调用耗时 3 秒,你无法判断瓶颈是在业务逻辑、下游 API,还是数据层。
- 错误缺乏上下文:调用失败时,你只能看到错误消息,却不知道服务器在崩溃前正在执行什么。
- 性能无基线:新增工具后,没有历史数据可供对比,无法判断其性能是否合理。
这些问题与任何后端微服务面临的挑战完全相同,而业界成熟的解决方案就是:OpenTelemetry 分布式追踪。
从插桩视角看,MCP 服务器没有任何特殊之处——它就是标准进程。接入 OTel SDK、在工具处理器周围定义 Span、将追踪数据发送到后端,这套流程完全通用。唯一需要学习的是该用哪些 Span 名称和属性,才能让追踪数据具备可读性和一致性。
4. 整体架构:从"产生数据"到"消费数据"的闭环
本次实践的完整数据流如下:
闭环逻辑:Claude 执行工具 → 产生 OpenTelemetry 追踪 → 追踪存入 Elastic APM → 同一会话通过 Agent Builder MCP 查询这些追踪 → AI 基于真实数据回答性能问题。
两个 MCP 服务器同时运行在 Claude Desktop 中:
- everything-mcp-server:被插桩的示例服务器,负责产生遥测数据。
- elastic-agent-builder:负责查询遥测数据。
5. 实战:搭建可观测的 MCP 服务器
5.1 示例服务器说明
我们使用 Anthropic 官方发布的 @modelcontextprotocol/server-everything 作为示例。它包含多种典型工具模式:
| 工具 | 类型 | 用途 |
|---|---|---|
echo |
简单请求/响应 | 验证端到端插桩链路是否通畅 |
get-sum |
参数化调用 | 模拟带简单业务逻辑的工具 |
trigger-long-running-operation |
长耗时操作 | 模拟调用下游 API 或执行重计算的场景 |
5.2 Claude Desktop 双服务器配置
在 claude_desktop_config.json 中同时配置两个服务器:
{
"mcpServers": {
"everything": {
"command": "node",
"args": [
"--import",
"/path/to/node_modules/@elastic/opentelemetry-node/import.mjs",
"/path/to/everything/dist/index.js",
"stdio"
],
"env": {
"OTEL_SERVICE_NAME": "everything-mcp-server",
"OTEL_EXPORTER_OTLP_ENDPOINT": "https://<<your-otlp-endpoint>",
"OTEL_EXPORTER_OTLP_HEADERS": "Authorization=ApiKey <your-api-key>",
"OTEL_LOG_LEVEL": "none",
"OTEL_SEMCONV_STABILITY_OPT_IN": "http,database,messaging,genai"
}
},
"elastic-agent-builder": {
"command": "npx",
"args": [
"mcp-remote",
"https://<<your-kibana-url>/api/agent_builder/mcp",
"--header",
"Authorization:ApiKey <your-api-key>"
]
}
}
}
注:由于 MCP 和 GenAI 的语义约定目前仍处于 Development(开发)阶段,尚未达到稳定(Stable)状态,上游 OTel JS SDK 默认可能不启用这些实验性约定。通过设置该变量(如
http,database,messaging,genai),可显式开启最新的语义约定支持,确保mcp.method.name、gen_ai.operation.name等属性被正确识别和映射。
--import 参数实现了零代码自动插桩,但自动插桩的边界需要清楚:
- 能自动捕获的:HTTP 请求、数据库查询、Node.js 内置库等标准操作。
- 无法自动捕获的:MCP 工具调用属于应用层业务逻辑,必须通过手动 Span 进行包装。
6. 手动埋点:工具调用的 Span 包装模式
6.1 语义约定详解
OpenTelemetry 已定义 MCP 的语义约定(Semantic Conventions)。遵循这些约定,能确保追踪数据在不同工具、团队和后端之间保持一致性和可搜索性。
Span 命名规范:
格式为 {mcp.method.name} {target}。对于工具调用,应命名为 tools/call echo、tools/call get-sum 等。这将成为 Kibana APM 中的事务名称(Transaction Name)。
核心属性表:
| 属性 | 值 | 作用 |
|---|---|---|
mcp.method.name |
tools/call |
标识 MCP 协议方法 |
gen_ai.tool.name |
echo |
具体调用的工具名 |
gen_ai.operation.name |
execute_tool |
GenAI 语义约定中的操作类型 |
error.type |
错误类名(如 TypeError) |
仅在失败时设置,用于错误分类 |
注:MCP 语义约定在官方文档中仍标注为 Development 状态;GenAI 核心语义约定在 2026 年路线图中也仅列为"待确认稳定化(Unconfirmed)"目标,而非已稳定。
替代方案:在生产环境中使用时,建议:
- 通过
OTEL_SEMCONV_STABILITY_OPT_IN显式启用实验性约定(见 5.2 节配置)。- 同时附加自定义属性作为冗余备份(如
custom.mcp.tool.name),以防官方约定在后续版本中发生破坏性变更。- 关注 OpenTelemetry Semantic Conventions 仓库 的 MCP/GenAI SIG 动态,待稳定后移除冗余属性。
6.2 包装器代码实现
以下是手动创建 Span 的包装器模式,应在每个工具处理器中复用:
const { trace, SpanStatusCode } = require('@opentelemetry/api');
const tracer = trace.getTracer('everything-mcp-server', '1.0.0');
function withToolSpan(toolName, fn) {
return tracer.startActiveSpan(`tools/call ${toolName}`, (span) => {
// MCP 协议层属性
span.setAttribute('mcp.method.name', 'tools/call');
// GenAI 语义约定属性
span.setAttribute('gen_ai.tool.name', toolName);
span.setAttribute('gen_ai.operation.name', 'execute_tool');
try {
const result = fn();
// 正常结束
span.setStatus({ code: SpanStatusCode.OK });
span.end();
return result;
} catch (err) {
// 记录异常并标记错误状态
span.recordException(err);
span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
span.setAttribute('error.type', err.constructor.name);
span.end();
throw err;
}
});
}
每个工具处理器只需将业务逻辑包裹在 withToolSpan 中:
// 示例:echo 工具
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === 'echo') {
return withToolSpan('echo', () => {
return { content: [{ type: 'text', text: request.params.arguments.message }] };
});
}
// ... 其他工具
});
最终效果:每次工具调用都会在 Elastic APM 中生成一个命名清晰的 Span,附带耗时、状态、错误详情。
6.3 安全注意事项:敏感数据过滤
OTel 规范定义了两个可选属性:
gen_ai.tool.call.arguments:工具调用参数gen_ai.tool.call.result:工具返回结果
两者均标记为可能包含敏感数据。例如 everything 服务器中的 get-env 工具会返回所有环境变量,其中可能包含 API 密钥和凭证。
建议策略:
- 默认不采集:除非确认数据安全,否则不要设置这两个属性。
- SDK 层过滤:在 OpenTelemetry Collector 或 SDK 层面配置处理器,对敏感字段进行脱敏或丢弃。
- 字段级掩码:如果必须采集,使用正则或路径匹配对密码、token、key 等字段进行掩码替换。
7. 在 Kibana APM 中洞察 MCP 性能
启动 Claude Desktop 并触发若干工具调用后,everything-mcp-server 服务将出现在 Kibana Observability > Applications > Services Inventory 中。
7.1 服务总览
服务列表会展示该 MCP 服务器的整体健康度:延迟(Latency)、吞吐量(Throughput)、错误率(Error Rate)。这让你一眼判断服务器是否存在系统性问题。
7.2 事务视图(Transactions)
Kibana 按事务名称对追踪进行分组。由于我们遵循了语义约定,每个工具自动独占一行:
tools/call echotools/call get-sumtools/call trigger-long-running-operation
你可以直接看到每个工具的延迟、吞吐量和错误率,无需任何额外配置。
这正是统一命名规范的价值:如果五位开发者为同一个 MCP 服务器添加工具,只要大家都遵循 tools/call {toolName},APM 界面就会自动保持整洁。
7.3 链路瀑布图(Trace Waterfall)
点击某条具体追踪,进入瀑布图视图。对于单次简单工具调用,瀑布图通常只有一个顶层 Span。但如果工具处理器内部发起了下游 HTTP 请求或数据库查询(且这些库已被自动插桩),它们会以子 Span 的形式嵌套展示。
这使得"业务逻辑耗时 vs 外部等待耗时"的拆解变得一目了然。
7.4 延迟分布与错误追踪
- 延迟分布图:展示 p50、p95、p99 分位线。例如
trigger-long-running-operation会根据请求的步骤数呈现宽泛的分布,这为你设置告警阈值前提供了合理的基线参考。 - 错误面板:失败调用会出现在 Errors 面板中,包含完整堆栈、失败时的 Span 属性,以及发生次数统计。如果你使用了
span.recordException(err),Kibana 会自动将错误与产生它的追踪链路关联起来。
8. 闭环:通过 Agent Builder MCP 让 AI 自省
8.1 闭环架构
Elastic Agent Builder MCP 允许 Claude 在同一会话中查询 Elasticsearch 里的追踪数据。它本质上是一个桥接器:将自然语言问题转换为 ES|QL 查询,执行后返回结果。
由于 APM 追踪存储在 Elasticsearch 的数据流中(如 traces-apm-*),只要为 Agent Builder 的 API Key 授予这些索引的读取权限,闭环即告完成。
8.2 自然语言查询链路
第一步:产生追踪
向 Claude Desktop 提问:
“请使用 echo 工具说 hello,然后用 get-sum 计算 1337+42,最后执行一个包含 3 个步骤的长耗时操作。”
Claude 依次执行三个工具调用,产生三个 Span 落入 Elastic APM。
第二步:查询追踪
不离开聊天窗口,继续提问:
“查询过去 10 分钟的 APM 追踪数据。有哪些工具调用,各自耗时多久?”
Claude 通过 Agent Builder MCP 执行 ES|QL 查询,从 traces-apm-* 索引中检索数据,然后用自然语言总结:
"过去 10 分钟内检测到 3 次工具调用:
echo:耗时 1.2ms,状态成功get-sum:耗时 3.8ms,状态成功trigger-long-running-operation:耗时 4.5s,状态成功"
你可以在 Kibana APM 的事务视图中交叉验证这些数字。
8.3 进阶分析场景
基于同一套数据,你可以提出更深层的问题:
| 自然语言问题 | 背后的分析能力 |
|---|---|
| “这些工具调用中,哪个 p95 延迟最高?” | 分位线统计与排序 |
| “有没有调用失败?错误信息是什么?” | 错误聚合与堆栈检索 |
| “对比过去一小时内 echo 和 get-sum 的延迟” | 跨工具基线比较 |
| “长耗时操作的延迟分布有没有异常突增?” | 时序异常检测 |
每个问题都会被 Agent Builder 翻译为 ES|QL 查询,在 APM 追踪索引上执行。
9. 为什么选择 Elastic 生态做 MCP 可观测性
这个方案的独特价值并非"能用 OpenTelemetry 追踪 MCP"——任何支持 OTLP 的后端都能做到。Elastic 生态的差异化在于闭环能力:
-
Agent Builder 完成闭环:因为 APM 数据存储在 Elasticsearch 中,而 Elasticsearch 可通过 Agent Builder MCP 被同一会话的 AI 查询,AI 能够基于自己的历史性能数据进行反思和异常定位。
-
APM UI 为分布式追踪而生:Kibana 的 APM 界面原生支持命名事务、瀑布链路、延迟分位线、错误追踪和服务地图,与 MCP 追踪数据模型高度契合。
-
原生 OTLP 接收:Elastic APM Server 自 8.x 起直接支持 OTLP 协议,无需额外 Collector 转换,配置
OTEL_EXPORTER_OTLP_ENDPOINT即可直联。 -
EDOT 降低接入成本:EDOT Node.js 打包了 Elastic 特定的增强功能(如 ECS 兼容映射、动态中心配置、稳定 HTTP 语义约定默认启用),减少了手动协调多个 OTel 包的复杂度。
10. 总结与最佳实践
核心观点
MCP 服务器不需要特殊的可观测性工具。它们是标准进程,OpenTelemetry 是观测标准进程的正确工具。关键在于:
- 手动为工具调用创建 Span,因为自动插桩无法感知应用层语义。
- 遵循语义约定命名 Span 和属性,确保跨团队协作时 APM 界面自动有序。
- 启用实验性约定时需加冗余保护,因为 MCP/GenAI 语义约定尚未完全稳定。
落地 Checklist
- 安装 EDOT Node.js:
npm install @elastic/opentelemetry-node - 配置
OTEL_SEMCONV_STABILITY_OPT_IN启用实验性 GenAI/MCP 约定 - 实现
withToolSpan包装器,统一为所有工具处理器添加手动 Span - 设置
gen_ai.tool.name、mcp.method.name、gen_ai.operation.name属性 - 确保异常路径调用
span.recordException()并设置error.type - 默认关闭
gen_ai.tool.call.arguments/result采集,避免密钥泄露 - 在 Kibana 中为 Agent Builder API Key 授予
traces-apm-*读取权限 - 通过自然语言验证闭环:先执行工具,再查询性能
未来演进
当 OpenTelemetry MCP 语义约定正式稳定后,社区可能会出现针对 MCP SDK 的官方自动插桩库(类似 Splunk 已发布的 splunk-otel-instrumentation-fastmcp)。届时,本文中的手动包装器模式可能进化为"一键自动插桩 + 可选自定义属性"的混合模式,进一步降低接入门槛。
参考来源:
- OpenTelemetry Semantic Conventions for MCP(官方开发中规范)
- OpenTelemetry Semantic Conventions 2026 Roadmap
- EDOT Node.js 官方文档与 NPM 包说明
- Elastic Agent Builder MCP 端点配置指南(Microsoft Tech Community)
- Splunk FastMCP Instrumentation(社区先行实践)
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)