使用 Helm 部署 MCP 隧道

使用 Anthropic Helm chart 在 Kubernetes 集群上安装 MCP 隧道技术栈。


Note

MCP 隧道是一项研究预览功能。申请访问权限 即可试用。

Anthropic Helm chart 将 MCP 隧道技术栈安装为单个 Deployment,并将其附加到您在 控制台 中创建的隧道。

准备工作

您需要:

  • **在控制台中创建的隧道。**请按照 创建隧道 的步骤操作,并记录隧道 ID(tnl_...)。
  • 一种让 chart 向 Tunnels API 进行身份验证的方式。
    • **编程访问(推荐)。**Chart 的 setup Job 通过 Workload Identity Federation 进行身份验证,获取隧道令牌,生成 CA,向 Anthropic 注册,并将所有内容存储在 Secret 中。您需要一条范围为 org:manage_tunnels 的联邦规则。
    • **手动方式。**跳过编程访问。您将 从控制台获取隧道令牌,自行生成 CA 和服务器证书,在控制台中注册 CA,并将凭据作为 Secrets 提供给集群。
  • 可以使用 helmkubectl 部署的 Kubernetes 集群。Without programmatic access 选项卡还需要 openssl(1.1.1 或更新版本)。
  • **从集群到 api.anthropic.com(443 TCP)和隧道边缘(7844 TCP 和 UDP)的出站网络连接。**参见完整的 网络要求
  • 一个或多个 MCP 服务器正在运行,并且可以从集群上通过您将在 gateway.config.routes 下配置的地址访问。如果您还没有,使用下方的示例服务器

可选:使用示例 MCP 服务器

如果您没有可用于测试的 MCP 服务器,请使用这个最小示例:

kubectl create namespace mcp-tunnel --dry-run=client -o yaml | kubectl apply -f -
kubectl -n mcp-tunnel apply -f - <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: hello-mcp-src
data:
  hello_server.py: |
    from mcp.server.fastmcp import FastMCP

    mcp = FastMCP("hello-server", host="0.0.0.0", port=9000)


    @mcp.tool()
    def hello(name: str = "world") -> str:
        """Say hello to someone."""
        return f"Hello, {name}!"


    if __name__ == "__main__":
        mcp.run(transport="streamable-http")
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-mcp
spec:
  replicas: 1
  selector:
    matchLabels: { app: hello-mcp }
  template:
    metadata:
      labels: { app: hello-mcp }
    spec:
      containers:
        - name: hello-mcp
          image: python:3.13-slim
          command: ["sh", "-c", "pip install --quiet mcp && python /app/hello_server.py"]
          volumeMounts:
            - { name: src, mountPath: /app }
          ports:
            - { containerPort: 9000 }
      volumes:
        - name: src
          configMap: { name: hello-mcp-src }
---
apiVersion: v1
kind: Service
metadata:
  name: hello-mcp
spec:
  selector: { app: hello-mcp }
  ports:
    - { port: 9000, targetPort: 9000 }
EOF

以下安装步骤会注明在何处添加相应的路由。

安装

Chart 的 setup Job 通过您的联邦规则交换集群的投射 ServiceAccount 令牌,获取隧道令牌,生成 CA 和服务器证书,并向 Anthropic 注册 CA。每日 CronJob 会根据需要续期服务器证书。无需手动处理密钥。

  1. 为集群设置 Workload Identity Federation

    按照 在 Kubernetes 中使用 WIF 注册集群的 OIDC 颁发者并创建联邦规则。Chart 的 setup Job 以发布命名空间中的 ServiceAccount <release>-setup 运行,因此在创建规则时使用以下值(假设发布名称为 mcp-tunnel,命名空间为 mcp-tunnel,本指南其余部分也使用此配置;请替换为您的值):

    字段
    Subjectsystem:serviceaccount:mcp-tunnel:mcp-tunnel-setup
    Audienceapi.anthropic.com(无协议前缀)。这是 chart 的默认值,必须与规则的受众逐字节匹配。如果您的规则使用 https://api.anthropic.com(控制台的建议),请将 api.wif.audience 设置为该值。
    Scopeorg:manage_tunnels

    如果隧道位于组织默认工作区以外的工作区,还需在 Settings > Workspaces 下将规则的服务账户添加为该工作区的成员(Tunnels API 根据服务账户的工作区成员资格进行授权)。

    记下规则的 ID(fdrl_...);您将把它设置为 api.wif.federationRuleId

    Note

    每日证书续期 CronJob 使用单独的 ServiceAccount(<release>-cert-renew),但不调用 Tunnels API;它在本地续期证书,只需要 chart 授予的 Kubernetes RBAC。联邦规则无需覆盖它。

  2. 获取默认值

    helm show values \
      oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
      --version 1.0.0 > values.yaml
    
  3. 配置隧道附加和路由

    编辑 values.yaml,使用隧道 ID、联邦规则 ID 和组织 ID 设置 api.wif.* 键,并为每个上游 MCP 服务器添加 routes 条目:

    api:
      wif:
        tunnelId: "tnl_..."
        federationRuleId: "fdrl_..."
        organizationId: "00000000-0000-0000-0000-000000000000"
        # Set when the tunnel is in a non-default workspace and the
        # rule's service account is a member of that workspace.
        # workspaceId: "wrkspc_..."
    
    gateway:
      config:
        routes:
          docs: http://docs-mcp.internal:8080
          search: http://search-mcp.internal:8080
    

    使用这些路由,Claude 可以通过 docs.<your-tunnel-domain>search.<your-tunnel-domain> 访问服务器。某些托管 Kubernetes 发行版将 Service CIDR 分配在标准私有范围之外;如果您的路由指向集群内 Service,请按照 上游 IP 验证 在此处添加 gateway.config.upstream.allowed_ips

    Note

    如果您使用的是 示例 MCP 服务器,请将 routes 设置为 echo: http://hello-mcp:9000

  4. 查看渲染的清单

    渲染 chart 并根据组织的审查流程查看输出:

    helm template mcp-tunnel \
      oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
      --version 1.0.0 \
      -n mcp-tunnel \
      -f values.yaml > rendered.yaml
    
  5. 安装

    helm install mcp-tunnel \
      oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
      --version 1.0.0 \
      --namespace mcp-tunnel --create-namespace \
      -f values.yaml
    

    setup Job 作为 Helm pre-install hook 运行,因此 helm install 会阻塞直到完成;成功后 Helm 会自动删除 Job。如果 helm install 因 hook 错误而失败,请参阅 Setup Job 身份验证失败

    Warning

    api.wif.* 值是标识符而非密钥,因此将它们存储在 Helm 发布历史 Secret 中不会带来风险。静态存储的敏感数据是 setup Job 创建的 mcp-tunnel Secret,其中包含隧道令牌和 TLS 私钥。请将组织的标准 Kubernetes Secret 保护实践应用到此命名空间。

在此模式下(setup.enabled: false),chart 不进行任何 API 调用;没有 setup Job 或 cert-renew CronJob。如果您不想设置 Workload Identity Federation,请使用此路径。

  1. 获取隧道令牌和域名

    创建隧道从控制台获取隧道令牌

    Note

    从详情页记录隧道域名。您将把它设置为 gateway.config.tunnel_domain

  2. 生成 CA 和服务器证书

    代理在纯 WebSocket 上监听;内部 TLS 握手使用您在此处生成的证书在该流内部进行。服务器证书的 SAN 必须包含 *.<tunnel-domain>,参见 证书要求

    export TUNNEL_DOMAIN=YOUR_TUNNEL_DOMAIN_HERE
    mkdir -p mcp-tunnel/data
    cd mcp-tunnel
    
    # Self-signed CA. Explicit extensions so it satisfies the certificate
    # requirements regardless of distro openssl.cnf defaults.
    openssl req -x509 -newkey rsa:2048 -nodes \
      -keyout data/ca.key -out data/ca.crt \
      -days 3650 -subj "/CN=mcp-tunnel-ca" \
      -addext "basicConstraints=critical,CA:TRUE" \
      -addext "keyUsage=critical,keyCertSign,cRLSign" \
      -addext "subjectKeyIdentifier=hash"
    
    # Extension file for the server certificate. Using -extfile (instead of
    # -copy_extensions, which is OpenSSL 3.0+ only) keeps this working on
    # OpenSSL 1.1.x.
    cat > data/tls.ext <<EOF
    subjectAltName = DNS:${TUNNEL_DOMAIN},DNS:*.${TUNNEL_DOMAIN}
    authorityKeyIdentifier = keyid,issuer
    extendedKeyUsage = serverAuth
    EOF
    
    # Server certificate signed by the CA
    openssl req -newkey rsa:2048 -nodes \
      -keyout data/tls.key -out /tmp/server.csr \
      -subj "/CN=${TUNNEL_DOMAIN}"
    openssl x509 -req -in /tmp/server.csr \
      -CA data/ca.crt -CAkey data/ca.key -CAcreateserial \
      -out data/tls.crt -days 90 \
      -extfile data/tls.ext
    

    在控制台中注册 data/ca.crt。将 data/ca.key 保存在持久且安全的位置;续期时需要用它签发新的服务器证书。

  3. 创建两个 Secret

    Chart 读取特定的键;Secret 名称可配置但键不可配置。第一行是幂等的(示例 MCP 服务器 部分已创建命名空间)。

    kubectl create namespace mcp-tunnel --dry-run=client -o yaml | kubectl apply -f -
    kubectl -n mcp-tunnel create secret generic mcp-tunnel-token \
      --from-literal=tunnel-token='eyJ...'
    kubectl -n mcp-tunnel create secret generic mcp-tunnel-cert \
      --from-file=tls.crt=data/tls.crt \
      --from-file=tls.key=data/tls.key
    
  4. 获取默认值

    helm show values \
      oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
      --version 1.0.0 > values.yaml
    
  5. 设置外部模式的值

    编辑 values.yaml 并设置以下键:

    setup:
      enabled: false
    
    external:
      tunnelTokenSecretName: mcp-tunnel-token   # must contain key: tunnel-token
      serverCertSecretName: mcp-tunnel-cert     # must contain keys: tls.crt, tls.key
    
    gateway:
      config:
        # Required in external mode. In managed mode the chart injects this from
        # the Secret as a -tunnel-domain flag; in external mode you set it here.
        tunnel_domain: YOUR_TUNNEL_DOMAIN_HERE
        routes:
          docs: http://docs-mcp.internal:8080
          search: http://search-mcp.internal:8080
    

    某些托管 Kubernetes 发行版将 Service CIDR 分配在标准私有范围之外;如果您的路由指向集群内 Service,请按照 上游 IP 验证 在此处添加 gateway.config.upstream.allowed_ips

    Note

    如果您使用的是 示例 MCP 服务器,请将 routes 设置为 echo: http://hello-mcp:9000

  6. 查看渲染的清单

    helm template mcp-tunnel \
      oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
      --version 1.0.0 \
      -n mcp-tunnel \
      -f values.yaml > rendered.yaml
    
  7. 安装

    helm install mcp-tunnel \
      oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
      --version 1.0.0 \
      --namespace mcp-tunnel \
      -f values.yaml
    

验证部署

从 Anthropic 端进行端到端验证:在 Managed Agent 会话或 Messages API 请求中使用 https://<route>.<your-tunnel-domain>/(其中 `` 是上游提供的路径)。有关请求格式,请参阅 使用隧道 MCP 服务器

如果失败,请检查 Pod 日志(kubectl -n mcp-tunnel logs deploy/mcp-tunnel -c mcp-proxy-c cloudflared)并查阅 故障排除

可选配置

使用 NetworkPolicy 限制出站流量

默认情况下,代理 Pod 的入站流量被拒绝(networkPolicy.ingress.enabled: true)。要额外限制 Pod 出站流量,请设置 networkPolicy.egress.enabled: true 并将 networkPolicy.egress.mcpServers 填充为覆盖上游 MCP 服务器的 Pod 标签选择器或 CIDR 范围;cloudflared 到隧道边缘的出站流量通过 networkPolicy.egress.cloudflaredEgressCIDRs 允许。

调优代理

gateway.config.* 下的字段会透传到代理配置文件。常见调整包括 upstream.allowed_ipslog_levelupstream.tls。有关完整字段列表,请参阅 代理配置 参考。Chart 强制设置 listen_addrtls.cert_filetls.key_file;在 gateway.config 中设置它们无效。

提供您自己的 OIDC 令牌

默认情况下,chart 为 setup Job 投射一个 Kubernetes ServiceAccount 令牌。要使用来自不同身份提供商的令牌(SPIFFE、Vault、云 SDK sidecar),请使用 setup.extraVolumes / setup.extraVolumeMounts 挂载它,并将 api.wif.tokenFile 指向挂载路径。Setup 程序从 ANTHROPIC_IDENTITY_TOKEN_FILE 读取令牌,chart 会将其设置为该路径。

升级

始终向 helm upgrade 传递 --version,以避免意外拉取更新版本的 chart。

更改配置

对于路由、副本数或 NetworkPolicy 等常规更改:

helm upgrade mcp-tunnel \
  oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
  --version 1.0.0 \
  -n mcp-tunnel \
  -f values.yaml
Warning

维护完整的 values.yaml 而不是依赖 --reuse-values。Helm 的深度合并行为可能会静默地无法删除已删除的路由。

轮换隧道令牌

使用编程访问时,在 values.yaml 中递增 tunnel.tokenVersion 并使用 --set setup.force=true 升级。Setup hook 仅在强制时才会在升级时重新运行:

helm upgrade mcp-tunnel \
  oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
  --version 1.0.0 \
  -n mcp-tunnel \
  -f values.yaml \
  --set setup.force=true

Setup Job 通过 Workload Identity Federation 进行身份验证;无需撤销 API 令牌。

不使用编程访问时,在控制台的隧道详情页上点击 Rotate token,然后更新 mcp-tunnel-token Secret:

kubectl -n mcp-tunnel create secret generic mcp-tunnel-token \
  --from-literal=tunnel-token='eyJ...' --dry-run=client -o yaml | kubectl apply -f -
kubectl -n mcp-tunnel rollout restart deploy/mcp-tunnel
Warning

点击 Rotate token 会立即撤销当前令牌。在 Secret 更新和滚动完成之前,任何使用旧令牌重启的 Pod(驱逐、节点排空、OOM)都无法重新连接。轮换后请尽快更新 Secret;对于更严格的可用性要求,请使用编程访问,让 chart 原子性地处理轮换。

证书续期

Chart 提供自动化功能,但您仍有责任监控过期时间并确认续期完成。

使用编程访问时,证书续期是自动的。Chart 部署了一个 CronJob(<release>-cert-renew,每日在 serverCert.cronSchedule 时运行,默认为 0 0 * * * UTC),运行 setup renew-cert 并仅在证书在 serverCert.renewBefore(默认 30 天)内到期时续期。续期是本地的:使用 Secret 中存储的 CA 签发新证书,不进行 API 调用,只需要 chart 授予的 Kubernetes RBAC。代理从 Secret 挂载中热重载证书,因此无需重启 Deployment。

不使用编程访问时没有 CronJob。从安装后保留的 mcp-tunnel/ 目录中,使用现有 CA 签发新的服务器证书(不要重新生成 CA):

export TUNNEL_DOMAIN=YOUR_TUNNEL_DOMAIN_HERE
openssl req -new -key data/tls.key -out /tmp/server.csr \
  -subj "/CN=$\{TUNNEL_DOMAIN\}"
openssl x509 -req -in /tmp/server.csr \
  -CA data/ca.crt -CAkey data/ca.key -CAcreateserial \
  -out data/tls.crt -days 90 -extfile data/tls.ext

kubectl -n mcp-tunnel create secret generic mcp-tunnel-cert \
  --from-file=tls.crt=data/tls.crt --from-file=tls.key=data/tls.key \
  --dry-run=client -o yaml | kubectl apply -f -

代理从 Secret 挂载中热重载证书。

后续步骤