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 本身不提供任何内置可观测性。这带来了三个具体痛点:

  1. 延迟黑盒:当一次工具调用耗时 3 秒,你无法判断瓶颈是在业务逻辑、下游 API,还是数据层。
  2. 错误缺乏上下文:调用失败时,你只能看到错误消息,却不知道服务器在崩溃前正在执行什么。
  3. 性能无基线:新增工具后,没有历史数据可供对比,无法判断其性能是否合理。

这些问题与任何后端微服务面临的挑战完全相同,而业界成熟的解决方案就是:OpenTelemetry 分布式追踪

从插桩视角看,MCP 服务器没有任何特殊之处——它就是标准进程。接入 OTel SDK、在工具处理器周围定义 Span、将追踪数据发送到后端,这套流程完全通用。唯一需要学习的是该用哪些 Span 名称和属性,才能让追踪数据具备可读性和一致性。


4. 整体架构:从"产生数据"到"消费数据"的闭环

本次实践的完整数据流如下:

调用工具

查询追踪

OTLP Traces

ES&|QL 查询

分析结果

自然语言总结

Claude Desktop

Instrumented MCP Server
everything-mcp-server

Elastic Agent Builder MCP

Elastic APM / Elasticsearch

闭环逻辑: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.namegen_ai.operation.name 等属性被正确识别和映射。

--import 参数实现了零代码自动插桩,但自动插桩的边界需要清楚:

  • 能自动捕获的:HTTP 请求、数据库查询、Node.js 内置库等标准操作。
  • 无法自动捕获的:MCP 工具调用属于应用层业务逻辑,必须通过手动 Span 进行包装。

6. 手动埋点:工具调用的 Span 包装模式

6.1 语义约定详解

OpenTelemetry 已定义 MCP 的语义约定(Semantic Conventions)。遵循这些约定,能确保追踪数据在不同工具、团队和后端之间保持一致性和可搜索性。

Span 命名规范

格式为 {mcp.method.name} {target}。对于工具调用,应命名为 tools/call echotools/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)"目标,而非已稳定。

替代方案:在生产环境中使用时,建议:

  1. 通过 OTEL_SEMCONV_STABILITY_OPT_IN 显式启用实验性约定(见 5.2 节配置)。
  2. 同时附加自定义属性作为冗余备份(如 custom.mcp.tool.name),以防官方约定在后续版本中发生破坏性变更。
  3. 关注 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,附带耗时、状态、错误详情。

下游服务 (HTTP/DB) 业务逻辑 withToolSpan (手动Span) MCP Server Claude Desktop (MCP Client) 下游服务 (HTTP/DB) 业务逻辑 withToolSpan (手动Span) MCP Server Claude Desktop (MCP Client) tools/call echo startActiveSpan("tools/call echo") 执行业务逻辑 HTTP/DB 请求 (自动插桩Span) 返回 结果 span.end() 响应

6.3 安全注意事项:敏感数据过滤

OTel 规范定义了两个可选属性:

  • gen_ai.tool.call.arguments:工具调用参数
  • gen_ai.tool.call.result:工具返回结果

两者均标记为可能包含敏感数据。例如 everything 服务器中的 get-env 工具会返回所有环境变量,其中可能包含 API 密钥和凭证。

建议策略

  1. 默认不采集:除非确认数据安全,否则不要设置这两个属性。
  2. SDK 层过滤:在 OpenTelemetry Collector 或 SDK 层面配置处理器,对敏感字段进行脱敏或丢弃。
  3. 字段级掩码:如果必须采集,使用正则或路径匹配对密码、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 echo
  • tools/call get-sum
  • tools/call trigger-long-running-operation

你可以直接看到每个工具的延迟、吞吐量和错误率,无需任何额外配置

这正是统一命名规范的价值:如果五位开发者为同一个 MCP 服务器添加工具,只要大家都遵循 tools/call {toolName},APM 界面就会自动保持整洁。

Kibana APM
Transactions视图

tools/call echo
p50: 2ms

tools/call get-sum
p50: 5ms

tools/call trigger-long-running-operation
p50: 3.2s

延迟分布

错误率: 0%

延迟分布

错误率: 0%

延迟分布
p95: 8.5s

错误率: 2%

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 授予这些索引的读取权限,闭环即告完成。

消费数据

产生数据

用户提问

Claude Desktop

调用 everything MCP
工具执行

EDOT产生Span

Elastic APM
Elasticsearch

用户追问性能

Claude Desktop

调用 Agent Builder MCP

ES|QL查询
traces-apm-*

返回原始数据

Claude自然语言总结

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 生态的差异化在于闭环能力

  1. Agent Builder 完成闭环:因为 APM 数据存储在 Elasticsearch 中,而 Elasticsearch 可通过 Agent Builder MCP 被同一会话的 AI 查询,AI 能够基于自己的历史性能数据进行反思和异常定位。

  2. APM UI 为分布式追踪而生:Kibana 的 APM 界面原生支持命名事务、瀑布链路、延迟分位线、错误追踪和服务地图,与 MCP 追踪数据模型高度契合。

  3. 原生 OTLP 接收:Elastic APM Server 自 8.x 起直接支持 OTLP 协议,无需额外 Collector 转换,配置 OTEL_EXPORTER_OTLP_ENDPOINT 即可直联。

  4. 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.namemcp.method.namegen_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(社区先行实践)
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐