关于 MCP Helm Server
MCP Helm Server 让 AI 助手无需本地安装 Helm,即可查询 Helm 仓库和 Chart。它专注于 Chart 信息而非其他 Kubernetes 资源,帮助减少对 values 和模板内容的猜测。
主要功能
- 01列出 Chart 及其版本
- 02查找最新稳定版本
- 03获取 values 和 Chart 文件
- 04查看 Chart 依赖
- 05提取容器镜像
适用场景
- 01查询 HTTP 或 OCI 仓库中的 Chart
- 02部署前检查 Chart 的 values
- 03按文件模式查看模板和元数据
- 04确认 Chart 使用的镜像和依赖
MCP 相关
向 MCP 客户端提供 Helm 仓库和 Chart 查询工具,并遵循标准 MCP 协议。支持 HTTP 仓库和 OCI Registry。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
docker run -i --rm ghcr.io/zekker6/mcp-helm:v1.3.0本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
ghcr.io/zekker6/mcp-helm:v1.3.0stdio工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
MCP Helm 服务
一个 MCP(模型上下文协议)服务,提供用于与 Helm 仓库和 chart 交互的工具。此 服务可让 AI 助手查询 Helm 仓库、获取 chart 信息并访问 chart 的 values,无需 在本地安装 Helm。
使用 MCP 操作 Helm 的目的是避免在使用大模型时臆造 values.yaml 的格式和 chart 的内容。
服务会提供标准化的方式来访问这些信息,让 AI 助手能够更轻松地与 Helm chart 和仓库交互。
此 MCP 服务目前及未来都只会提供用于操作 Helm 仓库的工具。如果需要操作其他 Kubernetes 资源,请考虑使用提供 Kubernetes 资源工具的独立 MCP 服务。
功能
MCP Helm 服务提供以下工具:
- list_repository_charts - 列出 Helm 仓库中所有可用的 chart(对于 OCI 注册表,则列出 chart 名称)
- list_chart_versions - 列出 chart 的所有可用版本/标签
- get_latest_version_of_chart - 获取指定 chart 的最新稳定版(非预发布版)
- get_chart_values - 获取 chart 的 values 文件(最新版本或指定版本)
- get_chart_contents - 获取 chart 的内容(包括模板、values 和元数据),可选择
使用文件路径 glob 模式筛选(例如
templates/**) - get_chart_dependencies - 获取 chart 的
Chart.yaml文件中定义的依赖项 - get_chart_images - 通过渲染模板并解析 Kubernetes 清单,提取 Helm chart 使用的容器镜像
仓库类型
所有工具都支持传统的 HTTP Helm 仓库和 OCI 注册表:
| 仓库类型 | 示例 URL |
|---|---|
| HTTP 仓库 | https://charts.example.com |
| OCI 注册表 | oci://ghcr.io/org/charts/mychart |
| OCI(Docker Hub) | oci://docker.io/library/mysql |
OCI 注册表支持
OCI(开放容器倡议)注册表将 Helm chart 存储为 OCI 工件。与多个 chart 共用一个索引的 HTTP 仓库不同, OCI 注册表通常每个仓库包含一个 chart,并带有多个版本标签。
OCI 使用示例:
repository_url: oci://ghcr.io/nginxinc/charts/nginx-ingress
chart_name: (empty - chart name is in the URL)
无需安装即可试用
你可以使用一个公开可用的 MCP Helm 服务实例来测试功能,无需安装: https://mcp-helm.zekker.dev/mcp
安装
使用 Docker 运行
你可以使用 Docker 运行 MCP Helm 服务。这是最简单的入门方式,无需安装 Go 或 从源代码构建。
docker run -d --name mcp-helm -p 8012:8012 ghcr.io/zekker6/mcp-helm:v1.3.0 -mode=sse
请注意,--mode=sse 标志用于启用服务器发送事件模式,MCP 客户端会通过该模式进行连接。
或者,你也可以使用 -mode=http 启用 Streamable HTTP 模式。
使用预构建二进制文件
从 发布页面 下载二进制文件。
Linux x86_64 示例(其他架构和平台也可用):
latest=$(curl -s https://api.github.com/repos/zekker6/mcp-helm/releases/latest | grep 'tag_name' | cut -d\" -f4)
wget https://github.com/zekker6/mcp-helm/releases/download/$latest/mcp-helm_Linux_x86_64.tar.gz
tar axvf mcp-helm_Linux_x86_64.tar.gz
使用 Mise
Mise(mise-en-place)是一个开发环境配置工具。
mise i ubi:zekker6/mcp-helm@latest
使用 Go 安装
注意:需要 Go 1.26.0。
go install github.com/zekker6/mcp-helm/cmd/mcp-helm@latest
从源代码构建
注意:需要 Go 1.26.0。
使用 Kubernetes 模块 v0.37.1 时,请将 k8s.io/kube-openapi 固定为 v0.0.0-20260721132016-d427ff9ee9ad,并将 Kustomize api/kyaml 固定为
v0.21.2。较新的修订版会引入 structured-merge-diff/v7,
其类型与 Kubernetes 模块的 structured-merge-diff/v6 不兼容。
-
克隆仓库:
git clone https://github.com/zekker6/mcp-helm.git cd mcp-helm -
构建二进制文件:
go build -o mcp-helm ./cmd/mcp-helm -
运行服务:
./mcp-helm
配置
配置 MCP 客户端以连接此服务。该服务实现了标准 MCP 协议,用于发现和执行工具。
在 http 和 sse 模式下,POST 请求正文的大小上限为 8 MiB。较大的请求会收到 HTTP 413 响应,包括不带 Content-Length 的分块上传。HTTP 服务为请求标头预留 5 秒,为标头和正文合计预留 30 秒,为空闲连接预留 60 秒。SSE 响应没有写入截止时间,因此可以在这些请求读取时限之后继续保持打开状态。这些限制不会限制已接受的工具调用所执行的工作。
仓库索引缓存
HTTP Helm 仓库的索引会在首次使用时下载,并在 -repo-index-max-age 指定的时长内重复使用(默认值为 1h)。超过该时长后,下一个请求会重新下载索引,因此无需重启即可显示新发布的图表版本。设置 -repo-index-max-age=0 可在每次请求时都下载索引。OCI 注册表始终会实时查询。
默认情况下,每个客户端最多保留 16 个仓库索引,来源字节预算根据启动时分配给该客户端的内存计算。达到任一限制时,会逐出最近最少使用的索引。MCP 服务会在请求之间共享一个 Helm 客户端及其缓存。解析后的 Go 对象会增加内存开销;这并非进程堆内存上限。逐出不会更改活动请求已持有的快照。索引文件使用私有临时目录,解析后会删除,包括下载或解析失败的情况。HTTP 图表存档会直接加载到内存中。不会读取或删除共享 Helm 缓存目录中的现有文件。缓存的快照不会保留请求上下文或下载客户端。
缓存和下载限制
默认值是 lib/helm_client/limits.go 和 lib/helm_client/memory.go 中命名的常量。创建 Helm 客户端时,可以通过读取环境变量覆盖这些默认值。值必须是正十进制整数,字节限制必须以字节为单位,而不是使用 MiB 或其他带后缀的单位。未设置变量时使用默认值;值为空、为零、为负数、格式错误或溢出都会导致启动失败。现有客户端不会重新加载环境变量的更改。-repo-index-max-age 标志仍控制 TTL。
| 环境变量 | 默认值 | 控制项 |
|---|---|---|
MCP_HELM_REPO_CACHE_MAX_ENTRIES | 16 | 保留的仓库索引数量 |
MCP_HELM_REPO_CACHE_MAX_BYTES | 自动计算,见下文 | 保留的索引源总字节数 |
MCP_HELM_INDEX_MAX_BYTES | 33554432(32 MiB) | 每次 HTTP 索引下载的字节数 |
MCP_HELM_CHART_MAX_BYTES | 104857600(100 MiB) | 每个压缩 HTTP Chart 归档的字节数 |
MCP_HELM_OCI_MAX_BYTES | 134217728(128 MiB) | 每次 OCI 拉取的响应正文总字节数 |
未设置 MCP_HELM_REPO_CACHE_MAX_BYTES 时,客户端使用检测到的最小内存分配量的 1/16,且上限为 256 MiB。它会考虑 Linux cgroup v1/v2 限制(包括可见的父级限制)、由 GOMEMLIMIT 初始化的当前 Go 运行时内存限制,以及主机 RAM 总量。它不会使用波动的可用内存或 Kubernetes 内存请求。此计算不会更改 Go 的内存限制。
| 内存分配量 | 自动缓存源字节预算 |
|---|---|
| 512 MiB | 32 MiB |
| 1 GiB | 64 MiB |
| 2 GiB | 128 MiB |
| 4 GiB 或更多 | 256 MiB |
如果无法检测到任何限制,客户端会回退到 64 MiB。检测出错(例如无法读取容器控制器)时,结果上限为 64 MiB,同时保留任何较小的已检测预算。启动日志会报告大小调整模式、选定的预算、检测到的分配量及其来源(如有),以及任何检测错误。不会在后台重新调整大小;分配量的更改会在新客户端启动时生效。显式设置字节覆盖值会跳过检测和自动上限。在较小的内存分配量下,可以下载大小超过缓存预算但有效的索引,不过不会保留该索引。这些源字节预算不是按堆内存限制测量的,也无法防止并发工作导致 OOM。
OCI 预算包括清单、配置、Chart 层、身份验证响应、重定向和重试。并发拉取各自使用独立预算。对于没有 Content-Length 的响应,会在读取过程中进行检查;超大下载会失败,而不会被截断后解析。这些限制不会限制并发请求或 Chart 渲染所需的内存。Helm 解压后的 Chart 内容限制仍然适用。OCI 下载会遵循调用方取消和注册表请求取消,包括读取响应正文期间。
例如,允许更大的工作集和更大的单个索引:
env MCP_HELM_REPO_CACHE_MAX_ENTRIES=32 \
MCP_HELM_REPO_CACHE_MAX_BYTES=134217728 \
MCP_HELM_INDEX_MAX_BYTES=67108864 \
./mcp-helm -mode=http
验证共享服务
运行 task test:repos,从 Prometheus Community、Grafana、Bitnami、Jetstack、Argo 和
ingress-nginx 获取最新索引。该测试会检查工作集是否符合配置的限制,使用 8 个并发调用者在本地重放这些确切索引,
验证预热缓存是否复用,并从每个公共仓库下载一个代表性 Chart。重复负载阶段不会访问公共服务。测试会记录来源大小、延迟、失败情况
以及静止状态下的 Go 堆内存使用量;其中包含捕获的测试夹具,不代表进程 RSS。公共端点或 Chart 格式失败会导致测试失败,而不会跳过。
task test 会继续禁用此网络检查;task test:all 会构建二进制文件,并同时运行单元测试、端到端测试和公共仓库测试。
CI 会在 lint 后运行 task test:all,因此公共仓库检查也会阻止有问题的更改通过。
对于引用 OCI Chart 的索引,测试还会单独测试直接 OCI 引用。目前,HTTP 仓库加载器不会跟随 oci:// 存档 URL,
包括 Bitnami 的 nginx Chart;对于这些 Chart,请使用直接 OCI 仓库 URL。此兼容性失败仍会显示在公共测试中。
常规测试还会针对本地仓库测试 8 个独立的 MCP HTTP 客户端,其中包括强制缓存逐出和重复下载 Chart。测试通过表示兼容性和并发回归检查通过, 不代表生产环境的容量保证。缓存限制约束的是保留量,而不是活跃用户数。冷索引获取仍共享一个全局锁,每次请求都会下载 Chart 存档。 索引限制应按最大仓库设置,缓存字节限制应能容纳完整的常用工作集,并预留增长空间。
身份验证
该服务支持 OCI 注册表和 HTTP Helm 仓库的身份验证。
嵌入 Go 客户端时,如果 http.DefaultTransport 是 *http.Transport,HTTP 仓库下载会克隆该传输配置。
如果它被包装或替换为其他 http.RoundTripper,下载则会改用支持环境代理的私有传输。不会使用该包装器;客户端的仓库 TLS 选项仍会生效。
命令行标志
| 标志 | 说明 |
|---|---|
-username | 基本身份验证的用户名(HTTP 仓库,以及未被 -registry-credentials 覆盖的 OCI 注册表) |
-password-file | 包含密码的文件路径 |
-registry-credentials | Docker 样式凭据文件的路径(例如 ~/.docker/config.json);对其中列出的 OCI 注册表具有权威性 |
-registry-plain-http | 对 OCI 注册表使用纯 HTTP(不安全,仅用于开发) |
-tls-cert | HTTP 仓库的 TLS 客户端证书文件路径 |
-tls-key | HTTP 仓库的 TLS 客户端密钥文件路径 |
-tls-ca | 用于验证服务器证书的 CA 证书文件路径 |
-tls-insecure-skip-verify | 跳过 TLS 证书验证(不安全) |
-pass-credentials-all | 将 HTTP 仓库凭据发送到所有 Chart URL 和重定向来源(不安全) |
默认情况下,HTTP 仓库凭据只会发送到仓库自身的协议、主机和端口。仍可下载来自其他来源的 Chart 存档,但不会收到仓库凭据,包括重定向后或从 HTTPS 降级到 HTTP 后的情况。-pass-credentials-all 会选择将这些凭据发送到每个 Chart URL 和重定向来源。只有在信任仓库的 Chart URL 以及所有重定向目标时,才使用此选项。
基本身份验证
对于要求使用用户名和密码进行身份验证的仓库:
# Create a password file (recommended for security)
echo "your-password" > /path/to/password.txt
chmod 600 /path/to/password.txt
# Run with basic auth
./mcp-helm -username myuser -password-file /path/to/password.txt
OCI 仓库身份验证
对于私有 OCI 仓库,可以通过以下方式配置身份验证:
- Docker 凭据 - 服务会自动使用
~/.docker/config.json中的凭据 - 显式凭据文件 - 使用
-registry-credentials标志
# Using Docker login (credentials stored in ~/.docker/config.json)
docker login ghcr.io
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
# Using explicit credentials file
./mcp-helm -registry-credentials /path/to/docker/config.json
# Using basic auth for OCI registry
./mcp-helm -username myuser -password-file /path/to/password.txt
将基本身份验证与仓库凭据文件结合使用
同一个实例可以同时为私有 HTTP 仓库和私有 OCI 仓库提供服务。
同时设置 -username/-password-file 和 -registry-credentials 时,OCI 请求会按仓库主机路由:
- 如果凭据文件能解析出图表仓库主机对应的凭据,则使用该主机的凭据(会同时查询
auths、credHelpers和credsStore,使用与 Helm CLI 相同的 Docker 凭据解析方式,因此能够正确匹配 Docker Hub 的标准https://index.docker.io/v1/键)。 - 否则,使用静态的
-username/-password-file基本身份验证。
这样,-registry-credentials 对其涵盖的 OCI 仓库仍具有权威性,同时基本身份验证也适用于 HTTP 仓库(以及凭据文件无法解析的任何 OCI 仓库)。
# HTTP repos use basic auth; OCI hosts in config.json use their per-host creds
./mcp-helm \
-username myuser -password-file /path/to/password.txt \
-registry-credentials /path/to/docker/config.json
对于由外部凭据存储(credsStore)或按仓库配置的辅助程序(credHelpers)管理的凭据,服务会在运行时调用相应的辅助程序二进制文件进行解析。
如果运行环境中没有该辅助程序,受影响的仓库会回退到基本身份验证;服务启动时会记录警告,以便发现此情况。路由只考虑传给 -registry-credentials 的文件(不会隐式回退到 ~/.docker/config.json),因此请在该文件中列出所需的每个私有 OCI 仓库。
TLS/mTLS 配置
对于有自定义 TLS 要求的仓库:
# Custom CA certificate (for self-signed or internal CAs)
./mcp-helm -tls-ca /path/to/ca.crt
# Client certificate authentication (mTLS)
./mcp-helm -tls-cert /path/to/client.crt -tls-key /path/to/client.key
# Combined: mTLS with custom CA
./mcp-helm -tls-cert client.crt -tls-key client.key -tls-ca ca.crt
# Skip TLS verification (development only, not recommended for production)
./mcp-helm -tls-insecure-skip-verify
Docker 配置
使用 Docker 并传入身份验证的示例:
# With basic auth
docker run -d --name mcp-helm -p 8012:8012 \
-v /path/to/password.txt:/secrets/password.txt:ro \
ghcr.io/zekker6/mcp-helm:v1.3.0 \
-mode=sse -username myuser -password-file /secrets/password.txt
# With Docker credentials
docker run -d --name mcp-helm -p 8012:8012 \
-v ~/.docker/config.json:/root/.docker/config.json:ro \
ghcr.io/zekker6/mcp-helm:v1.3.0 \
-mode=sse
可观测性
服务可以通过 OTLP 导出 OpenTelemetry 跟踪、指标和日志。默认情况下处于禁用状态:未设置
OTEL_ENABLED 时,不会导出任何内容,不会创建导出器,不会全局注册提供程序,也不会安装任何
检测机制,因此二进制文件的行为与当前完全相同。
OTEL_ENABLED 是唯一的开关。不会检查 OTEL_SDK_DISABLED,也不会检查
OTEL_EXPORTER_OTLP_PROTOCOL:传输协议由端点的方案决定,具体如下。
环境变量
| 变量 | 默认值 | 描述 |
|---|---|---|
OTEL_ENABLED | false | 总开关。仅当此变量为类似 true 的值(true、1、t)时,才会设置遥测 |
OTEL_EXPORTER_OTLP_ENDPOINT | - | 所有三种信号的基础端点。启用时必填,除非已为每种信号设置各自的端点 |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT | 基础端点 | 仅用于追踪的覆盖端点,按原样使用(HTTP 需包含完整路径) |
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT | 基础端点 | 仅用于指标的覆盖端点,按原样使用 |
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT | 基础端点 | 仅用于日志的覆盖端点,按原样使用 |
OTEL_SERVICE_NAME | mcp-helm | 上报给收集器的服务名称 |
OTEL_RESOURCE_ATTRIBUTES | - | 额外的资源属性,例如 deployment.environment.name=production |
OTEL_EXPORTER_OTLP_HEADERS | - | 随每次导出发送的请求头,例如用于身份验证。由 SDK 导出器读取 |
OTEL_EXPORTER_OTLP_TIMEOUT | 10000 | 导出超时时间,单位为毫秒。由 SDK 导出器读取 |
OTEL_EXPORTER_OTLP_COMPRESSION | - | 设为 gzip 可压缩导出数据。由 SDK 导出器读取 |
OTEL_METRIC_EXPORT_INTERVAL | 60000 | 指标导出间隔,单位为毫秒 |
OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION | base2_exponential_bucket_histogram | 两种 OTLP 传输方式的直方图聚合方式。设为 explicit_bucket_histogram 可使用经典分桶 |
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE | cumulative | 计数器和直方图从启动或重置时开始累积。SDK 还接受 delta 和 lowmemory |
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT | 4096 | Span 属性值的最大长度;超出部分会被截断。SDK 默认不限制长度,但有些值来自客户端。OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT 优先级更高,设为 -1 可取消限制 |
OTEL_EXPORTER_OTLP_CERTIFICATE | 系统根证书 | 用于验证 https:// 或 grpcs:// 收集器的 PEM CA 证书包。由 SDK 导出器读取 |
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE | - | 用于与收集器进行 mTLS 的 PEM 客户端证书。由 SDK 导出器读取 |
OTEL_EXPORTER_OTLP_CLIENT_KEY | - | 用于与收集器进行 mTLS 的 PEM 客户端密钥。由 SDK 导出器读取 |
启用遥测后,如果端点缺失或格式错误,服务将以非零状态码退出,并报告出错变量的名称;拼写错误绝不会被悄然降级为“无遥测”。关闭 OTEL_ENABLED 时,不会解析端点变量,因此环境中的过期值不会导致启动失败。
资源属性
服务会自行设置 service.name(取自 OTEL_SERVICE_NAME,否则为 mcp-helm)和 service.version,并从
OTEL_RESOURCE_ATTRIBUTES 读取其他所有属性。语义约定要求提供更多属性,其余属性必须来自部署环境:
| 属性 | 应从何处获取 |
|---|---|
deployment.environment.name | OTEL_RESOURCE_ATTRIBUTES。注意 .name 后缀——不带后缀的 deployment.environment 已弃用 |
service.instance.id | 通过 downward API 从 OTEL_RESOURCE_ATTRIBUTES 获取。运行多个副本后必须设置,否则各实例的序列会发生冲突 |
service.namespace | 当其他服务共用后端时,从 OTEL_RESOURCE_ATTRIBUTES 获取 |
k8s.pod.uid 和其他 k8s.* | 由 Collector 的 k8sattributes 处理器添加,这是受支持的添加方式 |
在 Kubernetes 上,前两项通过 downward API 获取:
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: OTEL_RESOURCE_ATTRIBUTES
value: deployment.environment.name=production,service.instance.id=$(POD_NAME)
端点方案
传输协议和 TLS 均由端点的 URL 方案决定:
| 端点 | 协议 | TLS | 导出目标 |
|---|---|---|---|
grpc://collector:4317 | OTLP/gRPC | 否 | collector:4317 |
grpcs://otel.example.com:4317 | OTLP/gRPC | 是 | otel.example.com:4317 |
http://collector:4318 | OTLP/HTTP | 否 | http://collector:4318/v1/traces、/v1/metrics、/v1/logs |
https://otel.example.com | OTLP/HTTP | 是 | https://otel.example.com/v1/traces、/v1/metrics、/v1/logs |
其他任何方案都会导致启动错误,并指出接受的四种方案。
- 基础端点是一个基础 URL:对于 HTTP,会在去掉末尾斜杠后追加信号路径(
/v1/traces、/v1/metrics、/v1/logs)。注入时末尾带斜杠的 Collector 端点也能正常处理。 - 按信号配置的端点会原样使用,符合 OTLP 规范,因此 HTTP 端点必须包含完整路径,例如
/v1/traces。 - gRPC 端点仅保留
host:port;gRPC 没有信号路径,因此任何路径都会被丢弃。 - 协议按信号解析,因此支持混合配置:traces 通过 gRPC 传输,而 metrics 和 logs 通过 HTTP 传输。对于 logs,即使没有自定义 CA,
grpcs://也要求使用 TLS;私有 CA 请使用OTEL_EXPORTER_OTLP_CERTIFICATE或OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE。
配置示例
三个信号共用一个端点:
OTEL_ENABLED=true \
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318 \
./mcp-helm -mode=http
Traces、metrics 和 logs 分别会发送到 http://otel-collector:4318/v1/traces、/v1/metrics 和 /v1/logs。将该端点切换为
grpc://otel-collector:4317,即可在不做任何其他更改的情况下,将这三个信号都改为通过 OTLP/gRPC 传输。
按信号配置端点,混用协议和 TLS:
OTEL_ENABLED=true \
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=grpc://otel-collector:4317 \
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://metrics.example.com/v1/metrics \
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://logs.example.com/v1/logs \
OTEL_SERVICE_NAME=mcp-helm \
OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=production \
./mcp-helm -mode=http
使用 Docker:
docker run -d --name mcp-helm -p 8012:8012 \
-e OTEL_ENABLED=true \
-e OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318 \
ghcr.io/zekker6/mcp-helm:latest -mode=http
在 OpenTelemetry 支持加入之前构建的镜像会忽略 OTEL_* 变量,因此请固定使用包含该支持的标签,或使用
latest。
输出内容
Traces
通过 HTTP 发起的工具调用会产生一条 trace,从入站请求一直追踪到由该请求触发的 Helm 操作:
POST /mcp (HTTP server span)
tools/call get_chart_images (MCP server span)
tool.get_chart_images (MCP tool span)
helm.get_chart_images (Helm operation span)
helm.load_chart
helm.oci.pull
helm.parse.images
自定义遥测名称使用应用专属的 mcp_helm.* 命名空间,不包含个人域名。为便于阅读,下方的 Helm 专属属性省略了 mcp_helm.helm. 前缀;实际发出的名称会包含该前缀。
OpenTelemetry 命名指南
建议避免与标准命名空间冲突,但并未强制要求使用反向 DNS 前缀。注册表属性
(url.full、server.address、server.port、error.type、gen_ai.*、mcp.*、jsonrpc.*、rpc.*)均完整显示。
使用旧自定义命名空间的现有查询必须更新;标准 OTel 名称保持不变。
| Span | 类型 | 属性 |
|---|---|---|
<METHOD> <route> | server | otelhttp HTTP 服务端语义约定,以及 POST 携带的 JSON-RPC 消息(见下文)。仅在 sse 和 http 模式下适用 |
<mcp method> [<tool>] | server | mcp.method.name、jsonrpc.request.id、gen_ai.tool.name、gen_ai.operation.name、mcp.session.id、mcp.protocol.version、network.transport、network.protocol.name、rpc.response.status_code、error.type |
tool.<name> | internal | - |
helm.list_charts | internal | repository.url、repository.type、chart.count |
helm.list_chart_versions | internal | + chart.name、version.count |
helm.get_latest_version | internal | + chart.name、chart.version(已解析) |
helm.get_latest_values | internal | + chart.name、chart.version(已解析)。仅供库使用;没有 MCP 工具会调用它 |
helm.get_chart_values | internal | + chart.name、chart.version |
helm.get_chart_contents | internal | + chart.name、chart.version、recursive |
helm.get_chart_dependencies | internal | + chart.name、chart.version、dependency.count |
helm.get_chart_images | internal | + chart.name、chart.version、recursive、image.count |
helm.load_chart | internal | repository.type、chart.name、chart.version |
helm.oci.pull | client | oci.ref、server.address、server.port |
helm.oci.tags | client | oci.ref、server.address、server.port |
helm.repo.index | client | repository.url、server.address、server.port |
helm.chart.download | client | url.full、server.address、server.port |
helm.parse.images | internal | recursive |
helm.parse.contents | internal | recursive |
失败的 Span 会标记为 ERROR 状态、记录异常,并携带 error.type。URL 属性会经过清理:仓库、OCI 或 Chart URL 成为 Span 属性之前,会先移除其中的 user:password@ 用户信息。
helm.chart.download span 会报告 url.full,因为 chart URL 就是实际获取的资源。
helm.repo.index 则不会:Helm getter 会自行解析索引路径,因此该服务持有的 URL 并非请求的 URL。
MCP 服务 span 遵循 MCP 语义约定:
其名称为 {mcp.method.name} {target},例如 tools/call get_chart_values、tools/list 或 ping。只有已注册的工具会成为 target,因为名称由客户端选择;未注册的工具仍会进入
gen_ai.tool.name。对于 mcp-go 未实现的方法,会在名为 MCP 的 span 上记录 mcp.method.name=_OTHER,与 HTTP 插桩处理未知方法的方式相同,因此客户端无法为每个请求自定义 span 名称。该服务通过自己的适配器接入 mcp-go 的追踪接口,而不使用 github.com/mark3labs/mcp-go/otel,并且不会记录 mcp-go 设置的 mcp.method 和 mcp.tool.name 键:注册表并未定义这两项。由于 mcp-go 在此路径上不会向追踪器或错误钩子暴露 ID,未知方法的 span 目前会省略
jsonrpc.request.id。
待修复项需要上游 API 支持。
只有当 mcp-go 能识别请求的有效版本时,才会记录 mcp.protocol.version。不受支持的元数据值和原始协议标头不会复制到 span 或指标中。在 sse 和 http 模式下,network.transport 为 tcp,同时 network.protocol.name=http;在 stdio 模式下则为 pipe。
以 JSON-RPC 错误作答的请求会在 rpc.response.status_code 中携带错误代码。约定将 -32700、-32600、-32601、-32602(其中包括未知工具)和 -32002 归因于调用方,因此这些错误不会设置 error.type,span 状态为 UNSET。其他代码会将 error.type 设为该代码,并将状态设为 ERROR,由 JSON-RPC 错误消息说明。若工具处理程序向调用方报告了失败,而非返回错误,则会将 error.type 设为 tool_error,状态同样为 ERROR。
每个录制请求的 POST span 还会记录其请求体携带的 JSON-RPC 消息类型,作为 mcp_helm.jsonrpc.message.kind
(request、notification 或 response)。通知会添加 mcp.method.name;若注册表中未列出该方法,则值为 _OTHER;响应则会添加 jsonrpc.request.id。mcp-go 只会为其分派的请求打开 MCP span,因此对于通知以及客户端对服务端 ping 的回复(空闲会话发送的大多数 POST 都属于这两类),HTTP span 是唯一的记录。请求的方法和 ID 会保留在其 MCP span 上,因此按这些字段查询时,每个请求只会计数一次。注释捕获限制为 64 KiB;更大的请求体不会被解析或添加注释。非录制 span 会完全跳过捕获。这些限制仅影响遥测:传输仍会接收原始请求体和读取错误。
在 sse 和 http 模式下,会遵循传入的 W3C traceparent / tracestate 标头,因此工具调用会加入调用方的跟踪,而不是另开新跟踪。在请求的 params._meta(SEP-414)中由客户端提供的跟踪上下文,会在所有模式下成为 MCP 服务 span 的父 span,包括 stdio,因为约定规定 MCP 客户端 span 是其父 span。当此上下文替换现有传输上下文时,MCP span 会链接到该上下文,从而即使跨越不同跟踪,也能保留与 HTTP span 的关联。元数据缺失或无效时,会保留现有父 span,不添加额外链接。两者都不存在时,stdio 模式下的每个 MCP 请求都是根 span。
指标
| 指标 | 类型 | 单位 | 属性 |
|---|---|---|---|
mcp.server.operation.duration | 直方图 | s | mcp.method.name, gen_ai.tool.name, gen_ai.operation.name, mcp.protocol.version, network.transport, network.protocol.name, rpc.response.status_code, error.type |
mcp_helm.helm.operation.duration | 直方图 | s | …helm.operation, …helm.repository.type, error.type |
http.server.* | 来自 otelhttp | - | http.server.request.duration, http.server.request.body.size, http.server.response.body.size。仅适用于 sse/http 模式 |
go.* | 来自 OTel 运行时检测 | - | Go 运行时内存、GC 和 goroutine 指标 |
mcp.server.operation.duration 是 MCP 语义约定为接收端定义的直方图。它涵盖 mcp-go 会为其创建跨度的每个请求,从收到请求到响应就绪,包括以错误作答的请求。不测量通知,也不测量 mcp-go 在此之前拒绝的消息(格式错误的 JSON、错误的 jsonrpc 版本),因为这两类消息都不会生成跨度。
所有直方图,包括 MCP、Helm、HTTP 和运行时直方图,默认都通过 OTLP/HTTP 和 OTLP/gRPC 使用以 2 为底的指数聚合。桶会根据记录的值进行调整,每个正数或负数范围最多有 160 个桶,最大尺度为 20。指标默认使用累积时间性:重复导出时会保留之前的观测值,而不是只报告最新时间段的数据。进程重启会重置累积值。
上述标准环境变量可以分别覆盖这两项默认设置。显式选择聚合方式时,MCP 和 Helm 时长直方图会使用从 10 ms 到 300 s 的推荐边界。请确保 Collector 和后端支持指数直方图;需要经典 _bucket 序列的查询可能需要更新。
成功时,不会包含 error.type,这符合约定——不要筛选 ok 值,而应筛选该属性未设置的情况。在 mcp.server.operation.duration 上,它与跨度中的值一致:JSON-RPC 错误代码(除非约定将该代码归因于调用方),或者对于向调用方报告失败的工具处理程序,使用 tool_error(mcp-go 会将其作为携带错误结果的成功响应传递)。发生 panic 的工具处理程序会以 -32603 作答。mcp_helm.helm.operation.duration 中的该属性则保存 Go 错误类型。直方图计数值的速率可用于计算调用速率,因此无需单独的计数器。
Helm 操作会嵌套,每一层都会记录自己的数据点:get_latest_values 包装了 get_latest_version 和 get_chart_values;任何省略 chart_version 的工具调用,都会先记录 get_latest_version,再记录所请求的操作。请按 mcp_helm.helm.operation 筛选,而不要汇总不同操作的直方图计数,否则一次逻辑调用会被重复计数。
指标属性刻意保持低基数:仓库 URL、图表名称和图表版本只会出现在跨度中,不会出现在指标中。gen_ai.tool.name 仅针对已注册的工具记录,mcp.method.name 未匹配时会回退到 _OTHER,因此两者都不会携带来自客户端的任意字符串。协议版本仅限于 mcp-go 支持的版本。对于 http.server.* 指标,server.address 和 server.port 报告的是 -httpListenAddr,而不是请求的 Host 标头,因此未经身份验证的探测无法通过变换该标头为每个值创建一个序列。
日志
启用遥测后,日志记录除了输出到标准错误流外,也会转发到 OTLP 日志导出器。两个输出目标都会遵循
-logLevel,因此提高日志级别后,被过滤的记录既不会发送到线上,也不会输出到标准错误流。每次工具调用也会记录一条 INFO 记录,其中包含工具名称、耗时,以及在失败时的 error.type。这条记录描述的是处理程序,而非 JSON-RPC 响应:返回的错误以其 Go 类型命名,报告给调用方的失败标记为 tool_error,而 panic 则标记为 _OTHER。
每条从带追踪的调用点发出的记录都会关联到对应的 span,因此后端中的日志、追踪和指标能够相互对应。两个输出目标携带关联信息的方式不同:标准错误流中的行会包含 trace_id 和 span_id 字段,而导出的记录会使用日志数据模型自身的 trace ID 字段,由 SDK 根据发出记录时的上下文填充。这些 ID 会从导出记录的属性中移除,避免重复发送到线上。
限制
有两项功能刻意未做检测:
- 不创建每请求的出站 HTTP span。 出站工作由手动创建的
helm.repo.index、helm.chart.download、helm.oci.pull和helm.oci.tags客户端 span 覆盖。OCI 请求仍使用 Helm SDK 内部创建的上下文;对这些请求进行检测会产生与工具追踪断开的 span。 - 长时间运行的流式请求不会纳入 HTTP 追踪和指标。
sse模式下的GET /sse请求和http模式下的GET /mcp请求会在整个客户端会话期间保持打开状态。对它们进行追踪会产生持续数小时的 span,并将会话时长计入请求延迟直方图,因此两者都会被过滤掉。这些会话承载的 JSON-RPC 请求仍会正常追踪。
关闭
收到 SIGTERM 或 SIGINT 后,服务会停止接受新任务,在 10 秒内处理完进行中的请求,然后在 5 秒内刷新缓冲的遥测数据,确保客户端一直不主动断开连接或采集器一直不响应时,进程也不会因此无法退出。两个时限刻意分开设置:耗时过长的排空操作不会占用刷新所需的时间。收到第二个信号后会立即终止。
为进程留出完成退出的时间:将 terminationGracePeriodSeconds(或 docker stop -t)设置为至少 20 秒,否则信号发出时缓冲的 span 和日志记录会丢失。
如果传输无法启动——例如端口已被占用或监听地址不可用——系统会记录为 fatal 并以状态码 1 退出。
路线图
- OpenTelemetry 检测(通过 OTLP 导出追踪、指标和日志)
- 添加更多工具
- 列出仓库中的所有 chart
- 列出某个 chart 的所有版本
- 获取 chart 的最新版本
- 获取 chart 的 values
- 获取 chart 最新版本的 values
- 提取完整的 chart 内容
- 从 Charts.yaml 提取依赖的 chart
- 提取 chart 中使用的镜像
- 支持 OCI 注册表
- 从 OCI 注册表拉取 chart
- 列出 OCI 注册表中的标签/版本
- 支持通过 Docker 凭据进行身份验证
- 支持使用私有 HTTP 仓库
- 提供 HTTP 基本身份验证凭据的方式
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。