Was this page helpful?
MCP tunnels is a Research Preview feature. Request access to try it.
The Anthropic Helm chart installs the MCP tunnel stack as a single Deployment and attaches it to the tunnel you created in the Console.
You need:
tnl_...).org:manage_tunnels.helm and kubectl. The Without programmatic access tab also uses openssl (1.1.1 or later).api.anthropic.com (443 TCP) and the tunnel edge (7844 TCP and UDP). See the full network requirements.gateway.config.routes. If you don't have one yet, use the sample server below.If you don't have an MCP server available for testing, use this minimal one:
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 }
EOFThe Install steps below note where to add the corresponding route.
Verify end to end from Anthropic's side: use https://<route>.<your-tunnel-domain>/<path> (where <path> is whatever the upstream serves at) in a Managed Agent session or a Messages API request. See Use the tunneled MCP servers for the request shapes.
If that fails, check the pod logs (kubectl -n mcp-tunnel logs deploy/mcp-tunnel -c mcp-proxy and -c cloudflared) and consult Troubleshooting.
Ingress to the proxy pod is denied by default (networkPolicy.ingress.enabled: true). To additionally restrict pod egress, set networkPolicy.egress.enabled: true and populate networkPolicy.egress.mcpServers with pod label selectors or CIDR ranges that cover your upstream MCP servers; cloudflared egress to the tunnel edge is allowed through networkPolicy.egress.cloudflaredEgressCIDRs.
Fields under gateway.config.* pass through to the proxy configuration file. Common adjustments include upstream.allowed_ips, log_level, and upstream.tls. See the proxy configuration reference for the full field list. The chart force-sets listen_addr, tls.cert_file, and tls.key_file; setting them in gateway.config has no effect.
By default the chart projects a Kubernetes ServiceAccount token for the setup Job. To present a token from a different identity provider (SPIFFE, Vault, a cloud-SDK sidecar), mount it with setup.extraVolumes / setup.extraVolumeMounts and point api.wif.tokenFile at the mount path. The setup binary reads the token from ANTHROPIC_IDENTITY_TOKEN_FILE, which the chart sets to that path.
Always pass --version to helm upgrade so you don't pull a newer chart unexpectedly.
For routine changes such as routes, replica count, or 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.yamlMaintain a complete values.yaml rather than relying on --reuse-values. Helm's deep-merge behavior can silently fail to remove deleted routes.
With programmatic access, increment tunnel.tokenVersion in values.yaml and upgrade with --set setup.force=true. The setup hook only re-runs on upgrades when forced:
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=trueThe setup Job authenticates with Workload Identity Federation; there is no API token to revoke.
Without programmatic access, click Rotate token on the tunnel detail page in the Console, then update the 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-tunnelClicking Rotate token invalidates the current token immediately. Until the Secret is updated and the rollout completes, any pod that restarts with the old token (eviction, node drain, OOM) cannot reconnect. Update the Secret promptly after rotating; for stricter availability requirements, use programmatic access so the chart handles the rotation atomically.
The chart provides automation, but you remain responsible for monitoring expiry and confirming renewal completes.
With programmatic access, certificate renewal is automatic. The chart deploys a CronJob (<release>-cert-renew, daily at serverCert.cronSchedule, default 0 0 * * * UTC) that runs setup renew-cert and only renews when the certificate is within serverCert.renewBefore (default 30 days) of expiry. Renewal is local: it signs a fresh certificate with the CA stored in the Secret, makes no API calls, and only needs the Kubernetes RBAC the chart grants. The proxy hot-reloads the certificate from the Secret mount, so no Deployment restart is needed.
Without programmatic access there is no CronJob. From inside the mcp-tunnel/ directory you kept after install, sign a fresh server certificate with the existing CA (do not regenerate the 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 -The proxy hot-reloads the certificate from the Secret mount.
Diagnose connectivity, TLS, and routing issues.