# 使用 Helm 部署 MCP 隧道

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

---

<Note>
  MCP 隧道是一项研究预览功能。[申请访问权限](https://claude.com/form/claude-managed-agents) 即可试用。
</Note>

Anthropic Helm chart 将 MCP 隧道技术栈安装为单个 Deployment，并将其附加到您在 [控制台](/docs/en/agents-and-tools/mcp-tunnels/console#create-a-tunnel) 中创建的隧道。

## 准备工作

您需要：

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

## 可选：使用示例 MCP 服务器

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

```bash
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
```

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

## 安装

<Tabs>
<Tab title="使用编程访问">

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

<Steps>
  <Step title="为集群设置 Workload Identity Federation">
    按照 [在 Kubernetes 中使用 WIF](/docs/en/manage-claude/wif-providers/kubernetes) 注册集群的 OIDC 颁发者并创建联邦规则。Chart 的 setup Job 以发布命名空间中的 ServiceAccount `<release>-setup` 运行，因此在创建规则时使用以下值（假设发布名称为 `mcp-tunnel`，命名空间为 `mcp-tunnel`，本指南其余部分也使用此配置；请替换为您的值）：

    | 字段 | 值 |
    |---|---|
    | Subject | `system:serviceaccount:mcp-tunnel:mcp-tunnel-setup` |
    | Audience | `api.anthropic.com`（无协议前缀）。这是 chart 的默认值，必须与规则的受众逐字节匹配。如果您的规则使用 `https://api.anthropic.com`（控制台的建议），请将 `api.wif.audience` 设置为该值。 |
    | Scope | `org:manage_tunnels` |

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

    记下规则的 ID（`fdrl_...`）；您将把它设置为 `api.wif.federationRuleId`。

    <Note>
      每日证书续期 CronJob 使用单独的 ServiceAccount（`<release>-cert-renew`），但不调用 Tunnels API；它在本地续期证书，只需要 chart 授予的 Kubernetes RBAC。联邦规则无需覆盖它。
    </Note>
  </Step>

  <Step title="获取默认值">
    ```bash
    helm show values \
      oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
      --version 1.0.0 > values.yaml
    ```
  </Step>

  <Step title="配置隧道附加和路由">
    编辑 `values.yaml`，使用隧道 ID、联邦规则 ID 和组织 ID 设置 `api.wif.*` 键，并为每个上游 MCP 服务器添加 `routes` 条目：

    ```yaml values.yaml
    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 验证](/docs/en/agents-and-tools/mcp-tunnels/troubleshooting#upstream-ip-validation) 在此处添加 `gateway.config.upstream.allowed_ips`。

    <Note>
      如果您使用的是 [示例 MCP 服务器](#optional-use-a-sample-mcp-server)，请将 `routes` 设置为 `echo: http://hello-mcp:9000`。
    </Note>
  </Step>

  <Step title="查看渲染的清单">
    渲染 chart 并根据组织的审查流程查看输出：

    ```bash
    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
    ```
  </Step>

  <Step title="安装">
    ```bash
    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 身份验证失败](/docs/en/agents-and-tools/mcp-tunnels/troubleshooting#setup-job-authentication-failures)。

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

</Tab>
<Tab title="不使用编程访问">

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

<Steps>
  <Step title="获取隧道令牌和域名">
    [创建隧道](/docs/en/agents-and-tools/mcp-tunnels/console#create-a-tunnel) 并 [从控制台获取隧道令牌](/docs/en/agents-and-tools/mcp-tunnels/console#get-the-connection-details)。

    <Note>
      从详情页记录隧道域名。您将把它设置为 `gateway.config.tunnel_domain`。
    </Note>
  </Step>

  <Step title="生成 CA 和服务器证书">
    代理在纯 WebSocket 上监听；内部 TLS 握手使用您在此处生成的证书在该流内部进行。服务器证书的 SAN 必须包含 `*.<tunnel-domain>`，参见 [证书要求](/docs/en/agents-and-tools/mcp-tunnels/reference#certificate-requirements)。

    ```bash
    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`](/docs/en/agents-and-tools/mcp-tunnels/console#add-a-ca-certificate)。将 `data/ca.key` 保存在持久且安全的位置；续期时需要用它签发新的服务器证书。
  </Step>

  <Step title="创建两个 Secret">
    Chart 读取特定的键；Secret 名称可配置但键不可配置。第一行是幂等的（[示例 MCP 服务器](#optional-use-a-sample-mcp-server) 部分已创建命名空间）。

    ```bash
    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
    ```
  </Step>

  <Step title="获取默认值">
    ```bash
    helm show values \
      oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
      --version 1.0.0 > values.yaml
    ```
  </Step>

  <Step title="设置外部模式的值">
    编辑 `values.yaml` 并设置以下键：

    ```yaml 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 验证](/docs/en/agents-and-tools/mcp-tunnels/troubleshooting#upstream-ip-validation) 在此处添加 `gateway.config.upstream.allowed_ips`。

    <Note>
      如果您使用的是 [示例 MCP 服务器](#optional-use-a-sample-mcp-server)，请将 `routes` 设置为 `echo: http://hello-mcp:9000`。
    </Note>
  </Step>

  <Step title="查看渲染的清单">
    ```bash
    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
    ```
  </Step>

  <Step title="安装">
    ```bash
    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
    ```
  </Step>
</Steps>

</Tab>
</Tabs>

## 验证部署

从 Anthropic 端进行端到端验证：在 Managed Agent 会话或 Messages API 请求中使用 `https://<route>.<your-tunnel-domain>/`（其中 `` 是上游提供的路径）。有关请求格式，请参阅 [使用隧道 MCP 服务器](/docs/en/agents-and-tools/mcp-tunnels/overview#use-the-tunneled-mcp-servers)。

如果失败，请检查 Pod 日志（`kubectl -n mcp-tunnel logs deploy/mcp-tunnel -c mcp-proxy` 和 `-c cloudflared`）并查阅 [故障排除](/docs/en/agents-and-tools/mcp-tunnels/troubleshooting)。

## 可选配置

### 使用 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_ips`、`log_level` 和 `upstream.tls`。有关完整字段列表，请参阅 [代理配置](/docs/en/agents-and-tools/mcp-tunnels/reference#proxy-configuration) 参考。Chart 强制设置 `listen_addr`、`tls.cert_file` 和 `tls.key_file`；在 `gateway.config` 中设置它们无效。

### 提供您自己的 OIDC 令牌

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

## 升级

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

### 更改配置

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

```bash
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 的深度合并行为可能会静默地无法删除已删除的路由。
</Warning>

### 轮换隧道令牌

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

```bash
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：

```bash
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 原子性地处理轮换。
</Warning>

### 证书续期

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）：

```bash
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 挂载中热重载证书。

## 后续步骤

<CardGroup cols={2}>
  <Card title="使用隧道 MCP 服务器" icon="link" href="/docs/en/agents-and-tools/mcp-tunnels/overview#use-the-tunneled-mcp-servers">
    将路由的 MCP 服务器附加到 Managed Agent 或 Messages API。
  </Card>
  <Card title="安全" icon="lock" href="/docs/en/agents-and-tools/mcp-tunnels/security">
    加固指南、凭据轮换和泄露响应。
  </Card>
  <Card title="故障排除" icon="wrench" href="/docs/en/agents-and-tools/mcp-tunnels/troubleshooting">
    诊断连接、TLS 和路由问题。
  </Card>
</CardGroup>
