mcp酒店go
   
这是给谁的?
您正在使用官方的Go-sdk在Go中构建MCP服务器。您已经拥有OTel基础设施(Jaeger、Grafana Tempo、Prometheus、Datadog),并且希望您的MCP服务器向其报告。您不应该为每个工具处理程序编写自定义工具。今天Go没有别的东西了。
安装
go get github.com/olgasafonova/mcp-otel-go/mcpotel用法
server := mcp.NewServer(impl, opts)
server.AddReceivingMiddleware(mcpotel.Middleware(mcpotel.Config{
ServiceName: "my-mcp-server",
ServiceVersion: "1.0.0",
}))三条线。现在,每个传入的MCP方法调用都会生成一个OTel跨度和一个持续时间直方图。
两个错误表面,均被覆盖
MCP工具错误分为两类,大多数仪器只捕获一类。
协议错误 当工具不存在或参数无效时发生。go-sdk将这些作为正常的go错误返回。很容易抓住。
应用程序错误 当工具处理程序返回错误(数据库关闭、API超时、输入错误)时发生。go sdk将这些打包成 CallToolResult{IsError: true} 并返回 nil 对于错误。您的中间件看到一个“成功”的调用。您的仪表板显示绿色。你的用户看到了失败。
这个中间件同时捕获了这两者。它检查 CallToolResult.IsError 每逢 tools/call 并用原始错误消息将跨度标记为错误。
收集什么
| 数据 | 示例 |
|---|---|
| 每个方法调用的跨度 | tools/call miro_create_sticky |
| 方法名称 | mcp.method.name = "tools/call" |
| 工具名称 | gen_ai.tool.name = "miro_create_sticky" |
| 资源URI | mcp.resource.uri = "miro://board/123" |
| 提示名称 | gen_ai.prompt.name = "summarize" |
| 会话ID | mcp.session.id = "abc123" |
| 错误类型(两个表面) | error.type = "*errors.errorString" |
| 持续时间直方图 | mcp.server.operation.duration (秒) |
所有属性名称都跟在后面 MCP的OTel语义约定.
什么没有被收集
默认情况下隐私安全。中间件从不记录:
- 工具参数或返回值
- 资源内容
- 环境变量或文件路径
- IP地址或用户身份信息
- 完整的错误消息(仅Go类型名称,如
*json.SyntaxError,而不是消息文本)
只有方法名称、工具名称、计时、错误类型名称和会话ID。默认情况下会记录资源URI,但可以对其进行编辑(见下文)。
隐私控制
来自工具处理程序的错误消息可以包含PII(例如。, "user john@example.com not found").资源URI可以包含用户可识别的路径(例如。, "user://john.doe/profile").中间件提供了两个编校挂钩来控制到达遥测后端的内容。
错误编校(默认打开)
默认情况下只记录Go错误类型名称(例如。, *json.SyntaxError),而不是完整的错误消息。这是安全的,因为类型名称是由开发人员定义的,从不包含用户数据。
// Default behavior: records "*json.SyntaxError", not "invalid field: email john@example.com"
mcpotel.Middleware(mcpotel.Config{
ServiceName: "my-server",
})只有在已知您的错误没有PII的情况下,才选择接受完整的错误消息:
mcpotel.Middleware(mcpotel.Config{
ServiceName: "my-server",
RedactError: mcpotel.ErrorMessageFull,
})或者提供自己的分类器:
mcpotel.Middleware(mcpotel.Config{
ServiceName: "my-server",
RedactError: func(err error) string {
// Classify by error type, strip PII, or return a fixed string
return "internal_error"
},
})URI编校(选择加入)
默认情况下,资源URI被完整记录。如果您的URI包含用户可识别的路径,请启用仅方案记录:
mcpotel.Middleware(mcpotel.Config{
ServiceName: "my-server",
RedactURI: mcpotel.URISchemeOnly, // "file:///home/john/secret.txt" → "file://"
})数据控制者责任
这个中间件是一个数据处理器。作为MCP服务器操作员,您是数据控制者。您决定:
- 哪个遥测后端接收数据
- 保留的跨度和指标有多长
- 错误消息或URI是否需要为您的用例进行编辑
- 遵守GDPR、CCPA或其他适用法规
会话ID是随机协议标识符,而不是用户标识符。只有当你的遥测后端通过其他方式将它们与用户身份相关联时,它们才会成为匿名数据。
配置
type Config struct {
ServiceName string // Required. OTel service.name
ServiceVersion string // Optional. service.version
TracerProvider trace.TracerProvider // Optional. Defaults to otel.GetTracerProvider()
MeterProvider metric.MeterProvider // Optional. Defaults to otel.GetMeterProvider()
Filter func(method string) bool // Optional. Return false to skip a method
RedactError func(err error) string // Optional. Defaults to Go type name only
RedactURI func(uri string) string // Optional. Nil = full URI recorded
}过滤方法
跳过嘈杂方法的仪器:
mcpotel.Middleware(mcpotel.Config{
ServiceName: "my-server",
Filter: func(method string) bool {
return method != "notifications/initialized"
},
})带上自己的出口商
对遥测技术的发展方向没有意见。像往常一样在启动时配置您的提供商:
exporter, _ := otlptracegrpc.New(ctx)
tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exporter))
otel.SetTracerProvider(tp)
// The middleware picks up the global provider automatically
server.AddReceivingMiddleware(mcpotel.Middleware(mcpotel.Config{
ServiceName: "my-server",
}))或者明确地传递提供者:
server.AddReceivingMiddleware(mcpotel.Middleware(mcpotel.Config{
ServiceName: "my-server",
TracerProvider: myCustomTP,
MeterProvider: myCustomMP,
}))在当地试试
这 examples/otlp/ demo通过gRPC将跟踪和指标导出到 localhost:4317.指向任何与OTLP兼容的后端,无需更改代码。
生成遥测数据 (每个后端都一样):
# Terminal — run the OTLP example via MCP Inspector
npx @modelcontextprotocol/inspector go run ./examples/otlp在Inspector UI中连接,调用 greet 工具几次。然后检查您的后端:
tools/call greet跨度与mcp.method.name,gen_ai.tool.name,mcp.session.id属性mcp.server.operation.duration直方图
集 OTEL_EXPORTER_OTLP_ENDPOINT 覆盖默认值 localhost:4317.
otel tui(无需Docker,无需设置)
直接接收OTLP的终端UI。查看痕迹的最快方式。
brew install ymtdzzz/tap/otel-tui # macOS
# or: go install github.com/ymtdzzz/otel-tui@latest
otel-tui # listens on :4317耶格尔(痕迹)
带有跟踪瀑布图和依赖关系图的Web UI。
docker run -d -p 16686:16686 -p 4317:4317 jaegertracing/jaeger:latest打开 http://localhost:16686。选择 example-server 服务以查看痕迹。
Grafana+Tempo+普罗米修斯(痕迹+指标)
带有仪表板的完整可观察性堆栈。创建一个 docker-compose.yml:
services:
tempo:
image: grafana/tempo:latest
command: ["-config.file=/etc/tempo.yaml"]
volumes:
- ./tempo.yaml:/etc/tempo.yaml
ports:
- "4317:4317"
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin添加最小值 tempo.yaml:
server:
http_listen_port: 3200
distributor:
receivers:
otlp:
protocols:
grpc:
endpoint: "0.0.0.0:4317"
storage:
trace:
backend: local
local:
path: /tmp/tempo/blocksdocker compose up -d打开 http://localhost:3000,将Tempo添加为数据源(http://tempo:3200),并探索痕迹。
数据狗
设置API密钥和站点,然后使用Datadog代理作为OTLP收集器:
docker run -d \
-e DD_API_KEY= \
-e DD_SITE=datadoghq.com \
-e DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_GRPC_ENDPOINT=0.0.0.0:4317 \
-p 4317:4317 \
gcr.io/datadoghq/agent:latest跟踪和指标显示在 数据狗APM。 仪表板。
蜂窝
云原生可观察性 慷慨的免费套餐无需Docker,直接发送OTLP:
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io \
OTEL_EXPORTER_OTLP_HEADERS="x-honeycomb-team=" \
go run ./examples/otlp格拉法娜云
免费版 包括跟踪和度量。从Grafana Cloud门户获取OTLP端点和令牌:
OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-.grafana.net/otlp \
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic " \
go run ./examples/otlp依赖项
github.com/modelcontextprotocol/go-sdkv1.3.0版本+go.opentelemetry.io/otelv1.34.0+- 没有出口商依赖关系。你自己带。
许可证
麻省理工学院
