• Messages
  • Managed Agents
  • Admin

Search...
⌘K
First steps
Intro to ClaudeQuickstart
Building with Claude
Features overviewUsing the Messages APIStop reasons and fallbackRefusals and fallbackFallback credit
Model capabilities
Extended thinkingAdaptive thinkingEffortTask budgets (beta)Fast mode (research preview)Structured outputsCitationsStreaming MessagesBatch processingSearch resultsStreaming refusalsMultilingual supportEmbeddings
Tools
OverviewHow tool use worksTutorial: Build a tool-using agentDefine toolsHandle tool callsParallel tool useTool Runner (SDK)Strict tool useTool use with prompt cachingServer toolsTroubleshootingWeb search toolWeb fetch toolCode execution toolAdvisor toolMemory toolBash toolComputer use toolText editor tool
Tool infrastructure
Tool referenceManage tool contextTool combinationsTool searchProgrammatic tool callingFine-grained tool streaming
Context management
Context windowsCompactionContext editingPrompt cachingMid-conversation system messagesBuild an orchestration modeCache diagnostics (beta)Token counting
Working with files
Files APIPDF supportImages and vision
Skills
OverviewQuickstartBest practicesSkills for enterpriseSkills in the API
MCP
Remote MCP serversMCP connector
OverviewArchitecture and componentsQuickstartManage in the ConsoleDeploy with HelmDeploy with Docker ComposeSecurityTroubleshootingReference
Claude on cloud platforms
Amazon BedrockAmazon Bedrock (legacy)Claude Platform on AWSMicrosoft FoundryVertex AI

Log in
Deploy with Helm
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...

Solutions

  • AI agents
  • Code modernization
  • Coding
  • Customer support
  • Education
  • Financial services
  • Government
  • Life sciences

Partners

  • Claude on AWS
  • Google Cloud's Vertex AI

Learn

  • Blog
  • Courses
  • Use cases
  • Connectors
  • Customer stories
  • Engineering at Anthropic
  • Events
  • Powered by Claude
  • Service partners
  • Startups program

Company

  • Anthropic
  • Careers
  • Economic Futures
  • Research
  • News
  • Responsible Scaling Policy
  • Security and compliance
  • Transparency

Learn

  • Blog
  • Courses
  • Use cases
  • Connectors
  • Customer stories
  • Engineering at Anthropic
  • Events
  • Powered by Claude
  • Service partners
  • Startups program

Help and security

  • Availability
  • Status
  • Support
  • Discord

Terms and policies

  • Privacy policy
  • Responsible disclosure policy
  • Terms of service: Commercial
  • Terms of service: Consumer
  • Usage policy
Messages/MCP tunnels

Deploy MCP tunnels with Helm

Install the tunnel stack on a Kubernetes cluster using the Anthropic Helm chart.

Was this page helpful?

  • Before you begin
  • Optional: Use a sample MCP server
  • Install
  • Verify the deployment
  • Optional configuration
  • Restrict egress with NetworkPolicy
  • Tune the proxy
  • Supply your own OIDC token
  • Upgrades
  • Change configuration
  • Rotate the tunnel token
  • Certificate renewal
  • Next steps


MCP tunnels are in research preview. Request access to try them.

The Anthropic Helm chart installs the tunnel stack as a single Deployment and attaches it to the tunnel you created in the Console.

Before you begin

You need:

  • A tunnel created in the Console. Complete Create a tunnel first and record the tunnel ID (tnl_...). For manual provisioning you also need the tunnel token and tunnel domain from that step.
  • A way for the chart to authenticate to the Tunnels API.
    • Programmatic access (recommended). The setup component authenticates through Workload Identity Federation, fetches the tunnel token, generates a CA, registers it with Anthropic, and stores everything in a Secret. You'll need a federation rule scoped to org:manage_tunnels.
    • Manual. Skip programmatic access. You'll get the tunnel token from the Console, generate a CA and server certificate yourself, register the CA in the Console, and supply the credentials to the cluster as Secrets.
  • A Kubernetes cluster you can deploy to with helm and kubectl. The Without programmatic access tab also uses openssl (1.1.1 or later).
  • Outbound network connectivity from the cluster to api.anthropic.com (443 TCP) and the tunnel edge (7844 TCP and UDP). See the full network requirements.
  • 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.

Optional: Use a sample MCP server

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 }
EOF

The Install steps that follow note where to add the corresponding route.

Install

Verify the deployment

Verify 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, that's https://echo.<your-tunnel-domain>/mcp. 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.

Optional configuration

Restrict egress with NetworkPolicy

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. Egress from cloudflared to the tunnel edge is allowed separately through networkPolicy.egress.cloudflaredEgressCIDRs.

Tune the proxy

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 always sets listen_addr, tls.cert_file, and tls.key_file; setting them in gateway.config has no effect.

Supply your own OIDC token

By default the chart projects a Kubernetes ServiceAccount token for the setup component. To use a token from a different identity provider (such as SPIFFE, Vault, or a cloud-SDK sidecar), mount it with setup.extraVolumes and setup.extraVolumeMounts. Then point api.wif.tokenFile at the mount path. The chart sets ANTHROPIC_IDENTITY_TOKEN_FILE to that path, and the setup component reads the token from there.

Upgrades

Always pass --version to helm upgrade so you don't pull a newer chart unexpectedly.

Change configuration

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.yaml


Maintain a complete values.yaml rather than relying on --reuse-values. Helm's deep-merge behavior can silently fail to remove deleted routes.

Rotate the tunnel token

With programmatic access, increment tunnel.tokenVersion in values.yaml and upgrade with --set setup.force=true. The setup component 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=true

The setup component 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-tunnel


Clicking 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.

Certificate renewal

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 (named after the Helm fullname, suffixed -cert-renew) that runs setup renew-cert daily (at serverCert.cronSchedule, default 0 0 * * * UTC). The job is a no-op unless the certificate is within serverCert.renewBefore of expiry (default 30 days). Renewal is local: the job signs a fresh certificate with the CA already 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.

Next steps


Use the tunneled MCP servers

Attach an upstream MCP server to a Managed Agent or the Messages API.


Security

Hardening guidance, credential rotation, and breach response.


Troubleshooting

Diagnose connectivity, TLS, and routing issues.