deploy-helm changedagents-and-tools/mcp-tunnels/deploy-helm
Nearest release: v2.1.312, published under an hour before this site recorded the change. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.
Recorded here
Lines+25added
Lines−25removed
From line
8
where the diff opens
First seen
14 Aug 2026
this site's first read of the page
Recorded edits7to this page, all time
The whole hunk
from line 8, old and new numbered
/
from line 8
88 MCP tunnels are in research preview. [Request access](https://claude.com/form/mcp-tunnels) to try them.
99</Note>
1010
11The Anthropic Helm chart installs the [tunnel stack](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) as a single Deployment and attaches it to your tunnel: one the chart's setup hook creates for you, or an existing tunnel you created in the [Claude Console](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#create-a-tunnel).
11The Anthropic Helm chart installs the [tunnel stack](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) as a single Deployment and attaches it to your tunnel: one the chart’s setup hook creates for you, or an existing tunnel you created in the [Claude Console](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#create-a-tunnel).
1212
1313## Before you start
1414
1515You need:
1616
17* **A tunnel.** With programmatic access, the chart's setup hook creates one for you when you don't supply a tunnel ID; to attach to an existing tunnel instead, [create it in the Console](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#create-a-tunnel) and record the tunnel ID (`tnl_...`). Manual provisioning always starts from a Console-created tunnel; you also need its tunnel token and tunnel domain.
17* **A tunnel.** With programmatic access, the chart’s setup hook creates one for you when you don’t supply a tunnel ID; to attach to an existing tunnel instead, [create it in the Console](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#create-a-tunnel) and record the tunnel ID (`tnl_...`). Manual provisioning always starts from a Console-created tunnel; you also need its tunnel token and tunnel domain.
1818
1919* **A way for the chart to authenticate to the Tunnels API.**
2020
2121 * **[Programmatic access](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#credential-provisioning) (recommended).** The [setup component](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) authenticates through Workload Identity Federation, fetches the tunnel token, generates a CA, registers it with Anthropic, and stores everything in a Secret. You need a federation rule scoped to `workspace:manage_tunnels`.
22 * **[Manual](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#credential-provisioning).** Skip programmatic access. You'll [get the tunnel token from the Console](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#get-the-connection-details), generate a CA and server certificate yourself, [register the CA in the Console](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#add-a-ca-certificate), and supply the credentials to the cluster as Secrets.
22 * **[Manual](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#credential-provisioning).** Skip programmatic access. You’ll [get the tunnel token from the Console](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#get-the-connection-details), generate a CA and server certificate yourself, [register the CA in the Console](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#add-a-ca-certificate), and supply the credentials to the cluster as Secrets.
2323
2424* **A Kubernetes cluster** you can deploy to with `helm` and `kubectl`. The **Without programmatic access** tab also uses `openssl` (1.1.1 or later).
2525
2626* **Outbound network connectivity** from the cluster to `api.anthropic.com` (443 TCP) and the [tunnel edge](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) (7844 TCP and UDP). See the full [network requirements](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/overview#network-requirements).
2727
28* **One or more MCP servers** running and reachable from the cluster on the addresses you'll configure under `gateway.config.routes`. If you don't have one yet, [use the sample server](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-helm#optional-use-a-sample-mcp-server).
28* **One or more MCP servers** running and reachable from the cluster on the addresses you’ll configure under `gateway.config.routes`. If you don’t have one yet, [use the sample server](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-helm#optional-use-a-sample-mcp-server).
2929
3030## Optional: Use a sample MCP server
3131
32If you don't have an MCP server available for testing, use this minimal one:
32If you don’t have an MCP server available for testing, use this minimal one:
3333
3434```bash
3535kubectl create namespace mcp-tunnel --dry-run=client -o yaml | kubectl apply -f -
from line 95
9595
9696<Tabs>
9797 <Tab title="With programmatic access">
98 The setup component exchanges the cluster's projected ServiceAccount token through your federation rule, fetches the tunnel token, generates a CA and server certificate, and registers the CA with Anthropic. A daily CronJob renews the server certificate as needed, so you don't handle any secrets by hand.
98 The setup component exchanges the cluster’s projected ServiceAccount token through your federation rule, fetches the tunnel token, generates a CA and server certificate, and registers the CA with Anthropic. A daily CronJob renews the server certificate as needed, so you don’t handle any secrets by hand.
9999
100100 <Steps>
101101 <Step title="Set up Workload Identity Federation for the cluster">
102 Follow [Use WIF with Kubernetes](https://platform.claude.com/docs/en/manage-claude/wif-providers/kubernetes) to register your cluster's OIDC issuer and create a federation rule. The setup component runs under its own ServiceAccount in the release namespace; the exact name follows Helm's `fullname` convention, so for any release name other than `mcp-tunnel`, run `helm template <release> ... | grep -A2 'kind: ServiceAccount'` to confirm it before creating the rule. The rest of this guide assumes release name `mcp-tunnel` in namespace `mcp-tunnel`, where the ServiceAccount is `mcp-tunnel-setup`.
102 Follow [Use WIF with Kubernetes](https://platform.claude.com/docs/en/manage-claude/wif-providers/kubernetes) to register your cluster’s OIDC issuer and create a federation rule. The setup component runs under its own ServiceAccount in the release namespace; the exact name follows Helm’s `fullname` convention, so for any release name other than `mcp-tunnel`, run `helm template <release> ... | grep -A2 'kind: ServiceAccount'` to confirm it before creating the rule. The rest of this guide assumes release name `mcp-tunnel` in namespace `mcp-tunnel`, where the ServiceAccount is `mcp-tunnel-setup`.
103103
104104 | Field | Value |
105105 | -------- | ---------------------------------------------------- |
106106 | Subject | `system:serviceaccount:mcp-tunnel:mcp-tunnel-setup` |
107 | Audience | `api.anthropic.com` (the chart's default; no scheme) |
107 | Audience | `api.anthropic.com` (the chart’s default; no scheme) |
108108 | Scope | `workspace:manage_tunnels` |
109109
110110 <Note>
111 The chart's default audience is `api.anthropic.com` with no scheme, but the Console's federation-rule form suggests `https://api.anthropic.com`. The two must match byte-for-byte or authentication fails. Either set the rule's audience to `api.anthropic.com`, or set `api.wif.audience` in `values.yaml` to `https://api.anthropic.com`.
111 The chart’s default audience is `api.anthropic.com` with no scheme, but the Console’s federation-rule form suggests `https://api.anthropic.com`. The two must match byte-for-byte or authentication fails. Either set the rule’s audience to `api.anthropic.com`, or set `api.wif.audience` in `values.yaml` to `https://api.anthropic.com`.
112112 </Note>
113113
114 If the tunnel is in a workspace other than the organization's default, also [add the rule's service account to that workspace](https://platform.claude.com/docs/en/manage-claude/workspaces#role-inheritance) (the Tunnels API authorizes against the service account's workspace memberships).
114 If the tunnel is in a workspace other than the organization’s default, also [add the rule’s service account to that workspace](https://platform.claude.com/docs/en/manage-claude/workspaces#role-inheritance) (the Tunnels API authorizes against the service account’s workspace memberships).
115115
116 Note the rule's ID (`fdrl_...`); you'll set it as `api.wif.federationRuleId`.
116 Note the rule’s ID (`fdrl_...`); you’ll set it as `api.wif.federationRuleId`.
117117
118118 <Note>
119119 The daily certificate-renewal CronJob uses a separate ServiceAccount (also derived from the Helm `fullname`) but does not call the Tunnels API; it renews the certificate locally and only needs Kubernetes RBAC, which the chart grants. The federation rule does not need to cover it.
from line 158
158158 With these routes, Claude reaches the servers at `docs.<your-tunnel-domain>` and `search.<your-tunnel-domain>`. Some managed Kubernetes distributions allocate the Service CIDR outside the standard private ranges; if your routes target in-cluster Services, add `gateway.config.upstream.allowed_ips` here as described in [Upstream IP validation](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/troubleshooting#upstream-ip-validation).
159159
160160 <Note>
161 If you're using the [sample MCP server](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-helm#optional-use-a-sample-mcp-server), set `routes` to `echo: http://hello-mcp:9000` instead.
161 If you’re using the [sample MCP server](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-helm#optional-use-a-sample-mcp-server), set `routes` to `echo: http://hello-mcp:9000` instead.
162162 </Note>
163163 </Step>
164164
165165 <Step title="Review the rendered manifests">
166 Render the chart and review the output according to your organization's vetting practices:
166 Render the chart and review the output according to your organization’s vetting practices:
167167
168168 ```bash
169169 helm template mcp-tunnel \
from line 185
185185
186186 The setup component runs as a Helm pre-install hook Job, so `helm install` blocks until it completes. On success Helm deletes the Job automatically. If `helm install` fails with a hook error, see [Setup component authentication failures](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/troubleshooting#setup-component-authentication-failures).
187187
188 When `tunnel.id` is empty, the setup component creates the tunnel in the workspace your federation rule targets (the organization's default workspace unless you set `api.wif.workspaceId`) and stores its ID and domain in the `mcp-tunnel` Secret. Find the domain you'll need for [verification](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-helm#verify-the-deployment) on the tunnel's detail page, which you open from the [MCP tunnels](https://platform.claude.com/settings/workspaces/default/mcp-tunnels) page (`platform.claude.com/settings/workspaces/default/mcp-tunnels`) in the Console. In the workspace picker in the sidebar, select the workspace that holds the tunnel. You can also read the domain from the Secret:
188 When `tunnel.id` is empty, the setup component creates the tunnel in the workspace your federation rule targets (the organization’s default workspace unless you set `api.wif.workspaceId`) and stores its ID and domain in the `mcp-tunnel` Secret. Find the domain you’ll need for [verification](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-helm#verify-the-deployment) on the tunnel’s detail page, which you open from the [MCP tunnels](https://platform.claude.com/settings/workspaces/default/mcp-tunnels) page (`platform.claude.com/settings/workspaces/default/mcp-tunnels`) in the Console. In the workspace picker in the sidebar, select the workspace that holds the tunnel. You can also read the domain from the Secret:
189189
190190 ```bash
191191 kubectl -n mcp-tunnel get secret mcp-tunnel \
from line 195
195195 Re-running the setup component (during [upgrades](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-helm#upgrades) or [token rotation](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-helm#rotate-the-tunnel-token)) reuses the tunnel ID stored in this Secret; it never creates a second tunnel.
196196
197197 <Warning>
198 The `api.wif.*` values are identifiers, not secrets, so storing them in Helm release-history Secrets is not a risk. The sensitive data at rest is the `mcp-tunnel` Secret the setup component creates, which holds the tunnel token and TLS private keys. Apply your organization's standard practices for protecting Kubernetes Secrets to this namespace.
198 The `api.wif.*` values are identifiers, not secrets, so storing them in Helm release-history Secrets is not a risk. The sensitive data at rest is the `mcp-tunnel` Secret the setup component creates, which holds the tunnel token and TLS private keys. Apply your organization’s standard practices for protecting Kubernetes Secrets to this namespace.
199199 </Warning>
200200 </Step>
201201 </Steps>
from line 202
202202 </Tab>
203203
204204 <Tab title="Without programmatic access">
205 In this mode (`setup.enabled: false`) the chart makes no API calls; the setup component does not run and there is no cert-renew CronJob. Use this path if you'd rather not set up Workload Identity Federation.
205 In this mode (`setup.enabled: false`) the chart makes no API calls; the setup component does not run and there is no cert-renew CronJob. Use this path if you’d rather not set up Workload Identity Federation.
206206
207207 <Steps>
208208 <Step title="Get the tunnel token and domain">
from line 209
209209 [Create the tunnel](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#create-a-tunnel) and [get the tunnel token from the Console](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#get-the-connection-details).
210210
211211 <Note>
212 Record the tunnel domain from the detail page. You'll set it as `gateway.config.tunnel_domain`.
212 Record the tunnel domain from the detail page. You’ll set it as `gateway.config.tunnel_domain`.
213213 </Note>
214214 </Step>
215215
216216 <Step title="Generate a CA and server certificate">
217 The proxy listens on plain WebSocket, with [inner TLS](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) carried inside that stream using the certificate you generate here. The server certificate's SAN must include `*.<tunnel-domain>` as described in the [certificate requirements](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/reference#certificate-requirements).
217 The proxy listens on plain WebSocket, with [inner TLS](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) carried inside that stream using the certificate you generate here. The server certificate’s SAN must include `*.<tunnel-domain>` as described in the [certificate requirements](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/reference#certificate-requirements).
218218
219219 ```bash
220220 export TUNNEL_DOMAIN=YOUR_TUNNEL_DOMAIN_HERE
from line 249
249249 -extfile data/tls.ext
250250 ```
251251
252 [Register `data/ca.crt` in the Console](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#add-a-ca-certificate). Keep `data/ca.key` somewhere durable and secure; you'll need it to sign a fresh server certificate at renewal time.
252 [Register `data/ca.crt` in the Console](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#add-a-ca-certificate). Keep `data/ca.key` somewhere durable and secure; you’ll need it to sign a fresh server certificate at renewal time.
253253 </Step>
254254
255255 <Step title="Create the two Secrets">
from line 299
299299 Some managed Kubernetes distributions allocate the Service CIDR outside the standard private ranges; if your routes target in-cluster Services, add `gateway.config.upstream.allowed_ips` here as described in [Upstream IP validation](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/troubleshooting#upstream-ip-validation).
300300
301301 <Note>
302 If you're using the [sample MCP server](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-helm#optional-use-a-sample-mcp-server), set `routes` to `echo: http://hello-mcp:9000` instead.
302 If you’re using the [sample MCP server](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-helm#optional-use-a-sample-mcp-server), set `routes` to `echo: http://hello-mcp:9000` instead.
303303 </Note>
304304 </Step>
305305
from line 328
328328
329329## Verify the deployment
330330
331Verify end to end from Anthropic's side: use `https://<route>.<your-tunnel-domain>/<path>` in a Managed Agent session or a Messages API request, where `<route>` is a key from `gateway.config.routes` and `<path>` is whatever the upstream MCP server serves at. With the [sample MCP server](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-helm#optional-use-a-sample-mcp-server), that's `https://echo.<your-tunnel-domain>/mcp`. See [Use the tunneled MCP servers](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/overview#use-the-tunneled-mcp-servers) for the request shapes.
331Verify end to end from Anthropic’s side: use `https://<route>.<your-tunnel-domain>/<path>` in a Managed Agent session or a Messages API request, where `<route>` is a key from `gateway.config.routes` and `<path>` is whatever the upstream MCP server serves at. With the [sample MCP server](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-helm#optional-use-a-sample-mcp-server), that’s `https://echo.<your-tunnel-domain>/mcp`. See [Use the tunneled MCP servers](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/overview#use-the-tunneled-mcp-servers) for the request shapes.
332332
333333If that fails, check the pod logs (`kubectl -n mcp-tunnel logs deploy/mcp-tunnel -c mcp-proxy` and `-c cloudflared`) and consult [Troubleshooting](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/troubleshooting).
334334
from line 348
348348
349349## Upgrades
350350
351Always pass `--version` to `helm upgrade` so you don't pull a newer chart unexpectedly.
351Always pass `--version` to `helm upgrade` so you don’t pull a newer chart unexpectedly.
352352
353353### Upgrade from chart 1.x
354354
355Chart 2.0.0 moves the tunnel ID from `api.wif.tunnelId` to `tunnel.id`. Before upgrading, edit your `values.yaml`: move the `tnl_...` value to `tunnel.id` and remove `api.wif.tunnelId`. Leaving `tunnel.id` unset is safe (the setup component reuses the tunnel ID already stored in the `mcp-tunnel` Secret on re-run), but the explicit move keeps your `values.yaml` accurate. Also update your federation rule's scope from `org:manage_tunnels` to `workspace:manage_tunnels` in the Console.
355Chart 2.0.0 moves the tunnel ID from `api.wif.tunnelId` to `tunnel.id`. Before upgrading, edit your `values.yaml`: move the `tnl_...` value to `tunnel.id` and remove `api.wif.tunnelId`. Leaving `tunnel.id` unset is safe (the setup component reuses the tunnel ID already stored in the `mcp-tunnel` Secret on re-run), but the explicit move keeps your `values.yaml` accurate. Also update your federation rule’s scope from `org:manage_tunnels` to `workspace:manage_tunnels` in the Console.
356356
357357### Change configuration
358358
from line 367
367367```
368368
369369<Warning>
370 Maintain a complete `values.yaml` rather than relying on `--reuse-values`. Helm's deep-merge behavior can silently fail to remove deleted routes.
370 Maintain a complete `values.yaml` rather than relying on `--reuse-values`. Helm’s deep-merge behavior can silently fail to remove deleted routes.
371371</Warning>
372372
373373### Rotate the tunnel token
No line in this hunk matches that.