Set up an MCP tunnel changedconnectors/mcp-tunnels/setup
Nearest release: v2.1.305, published 2 hours after upstream edited the page. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.
Upstream edited this page at 20 Oct 2026 17:01 UTC, give or take a minute or two: the time comes from Anthropic’s own sitemap rather than from a commit. This site recorded the change at 20 Oct 2026 18:07 UTC.
Upstream edited
Recorded here
Lines+397added
Lines−429removed
From line
2
where the diff opens
First seen
31 Aug 2026
this site's first read of the page
Recorded edits5to this page, all time
The whole hunk
from line 2, old and new numbered
/
The two sides of this change are more than 400 edits apart, too far apart to line up, so this is the differ's own diff of it and the words inside a line are not marked.
from line 2
22
33> Create a Tunnels API key, deploy the MCP tunnel stack with Helm or Docker Compose, verify the connection, and add tunneled MCP servers as custom connectors.
44
5<Note>
6 MCP tunnels are in research preview and are available to organizations on the Claude Enterprise plan by request. To request access, [submit the MCP tunnels interest form](https://claude.com/form/mcp-tunnels) or contact your Anthropic account team.
7</Note>
5> [!NOTE]
6>
7> MCP tunnels are in research preview and are available to organizations on the Claude Enterprise plan by request. To request access, [submit the MCP tunnels interest form](https://claude.com/form/mcp-tunnels) or contact your Anthropic account team.
88
99This page covers the full setup of an MCP tunnel for a claude.ai Enterprise organization, from creating the API key that provisioning uses to members calling a tunneled MCP server from Claude. You need the Owner or Primary Owner role in claude.ai, and someone who can deploy containers to a Kubernetes cluster or a Docker host inside your network. Read [MCP tunnels](/docs/connectors/mcp-tunnels/overview) first if the tunnel stack, the tunnel domain, and routes are unfamiliar.
1010
from line 12
1212
1313The setup component that runs alongside the tunnel stack needs a short-lived credential to create the tunnel, register its certificate authority (CA) certificate with Anthropic, and fetch the tunnel token. In claude.ai that credential is a Tunnels API key.
1414
15<Steps>
16 <Step title="Open Organization settings > Tunnels">
17 In claude.ai, go to **Organization settings > Tunnels**. This page appears once Anthropic has enabled MCP tunnels for your organization.
18 </Step>
19
20 <Step title="Create a key">
21 Open **Tunnels API** and create a key.
22 </Step>
23
24 <Step title="Copy the key">
25 Copy the key somewhere safe for the next section. You pass it to the setup component once.
26 </Step>
27</Steps>
15**1. Open Organization settings > Tunnels**
16
17In claude.ai, go to **Organization settings > Tunnels**. This page appears once Anthropic has enabled MCP tunnels for your organization.
18
19**2. Create a key**
20
21Open **Tunnels API** and create a key.
22
23**3. Copy the key**
24
25Copy the key somewhere safe for the next section. You pass it to the setup component once.
2826
2927The tunnel stack does not use the key at runtime. Revoke the key as soon as setup completes, and create a fresh one later when you rotate the tunnel token.
3028
from line 34
3634
3735Both the Helm and Docker Compose paths need at least one route. A route maps a subdomain of your tunnel domain to the internal URL of an MCP server, in the form `scheme://host:port` with no path. The examples use `docs` pointing at `http://docs-mcp.example.corp:8080`. Replace them with your own servers.
3836
39<Tabs>
40 <Tab title="Helm">
41 <Steps>
42 <Step title="Fetch the default values">
43 ```bash theme={null}
44 helm show values \
45 oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
46 --version 2.1.0 > values.yaml
47 ```
48
49 The file includes comments explaining each field.
50 </Step>
51
52 <Step title="Configure routes">
53 Edit `values.yaml` and add a `routes` entry under `gateway.config` for each MCP server. Leave `tunnel.id` empty so the setup component creates the tunnel during install.
54
55 ```yaml values.yaml theme={null}
56 tunnel:
57 id: ""
58 # Increment to rotate the tunnel token on a later upgrade.
59 tokenVersion: "1"
60
61 gateway:
62 config:
63 routes:
64 docs: http://docs-mcp.example.corp:8080
65 search: http://10.0.12.7:9000
66 ```
67
68 With these routes, Claude reaches the servers at `docs.<your-tunnel-domain>` and `search.<your-tunnel-domain>`. If a route targets an address outside the RFC 1918 private ranges (some managed Kubernetes distributions allocate Service IPs elsewhere), add the range under `gateway.config.upstream.allowed_ips` as described in [Troubleshooting](/docs/connectors/mcp-tunnels/troubleshooting#proxy-logs-ip-validation-failed).
69 </Step>
70
71 <Step title="Review the rendered manifests">
72 Render the chart with a placeholder key and review the output according to your organization's practices for third-party manifests. Rendering makes no API calls.
73
74 ```bash theme={null}
75 helm template mcp-tunnel \
76 oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
77 --version 2.1.0 \
78 -n mcp-tunnel \
79 -f values.yaml \
80 --set api.token=placeholder > rendered.yaml
81 ```
82 </Step>
83
84 <Step title="Install">
85 Read the Tunnels API key into an environment variable so it stays out of your shell history and values file, then install into a dedicated namespace.
86
87 ```bash theme={null}
88 # Paste the Tunnels API key (input is hidden)
89 read -rs API_TOKEN && export API_TOKEN
90
91 helm install mcp-tunnel \
92 oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
93 --version 2.1.0 \
94 --namespace mcp-tunnel --create-namespace \
95 -f values.yaml \
96 --set api.token="$API_TOKEN"
97 ```
98
99 The setup component runs as a pre-install hook, so `helm install` blocks until the tunnel is created, the CA is registered, and the credentials are stored in the `mcp-tunnel` Secret. If the install fails with a hook error, see [Troubleshooting](/docs/connectors/mcp-tunnels/troubleshooting#helm-install-fails-with-a-hook-error).
100
101 <Warning>
102 Revoke the Tunnels API key in **Organization settings > Tunnels > Tunnels API** as soon as the install completes. Helm records `--set` values in its release history Secrets, and Kubernetes Secrets are not encrypted at rest by default, so the key remains recoverable from the cluster until you revoke it.
103 </Warning>
104 </Step>
105
106 <Step title="Read the tunnel domain">
107 You need the tunnel domain to add connectors later.
108
109 ```bash theme={null}
110 kubectl -n mcp-tunnel get secret mcp-tunnel \
111 -o jsonpath='{.data.tunnel-domain}' | base64 -d
112 ```
113
114 The value looks like `abc123.tunnel.anthropic.com`.
115 </Step>
116 </Steps>
117
118 To restrict the pod's egress at the network level, set `networkPolicy.enabled: true` in `values.yaml` and list your MCP servers under `networkPolicy.mcpServers`. The policy already allows cloudflared to reach the tunnel edge. Your cluster's network plugin must support NetworkPolicy.
119
120 For later configuration changes such as routes or replica count, edit `values.yaml` and run `helm upgrade` with the same `--version` and `-f values.yaml`, without the API key. Keep a complete `values.yaml` rather than relying on `--reuse-values`, because Helm's deep merge can silently keep a route you deleted.
121 </Tab>
122
123 <Tab title="Docker Compose">
124 <Steps>
125 <Step title="Prepare the deployment directory">
126 ```bash theme={null}
127 mkdir -p mcp-tunnel/{config,data}
128 cd mcp-tunnel
129 sudo chown 65532:65532 data
130 ```
131
132 The containers run as the non-root user ID `65532` and need write access to `data/`.
133 </Step>
134
135 <Step title="Write docker-compose.yaml">
136 The compose file pins images by digest, runs every container as non-root with a read-only filesystem, drops all Linux capabilities, and disables privilege escalation.
137
138 ```bash theme={null}
139 cat > docker-compose.yaml <<'EOF'
140 services:
141 # One-time provisioning. Run with: docker compose run --rm setup
142 setup:
143 image: us-docker.pkg.dev/anthropic-public-registry/images/mcp-proxy@sha256:d243d30642d39857a0579e465cb3b9bf3ae7c1141e83143bb6856777a31467a5
144 entrypoint: ["/setup"]
145 command:
146 - init
147 - --api-url=https://api.anthropic.com
148 - --output=dir:/data
149 - --token-version=1
150 environment:
151 - API_TOKEN
152 volumes:
153 - ./data:/data
154 user: "65532:65532"
155 read_only: true
156 security_opt:
157 - no-new-privileges:true
158 cap_drop:
159 - ALL
160 profiles: ["setup"]
161
162 cloudflared:
163 image: cloudflare/cloudflared@sha256:0e56c13c7058fcef9c4f0e7d83372f82330548dbf8f8b366f3d8b506526d93b2
164 command: tunnel --no-autoupdate run --url http://localhost:8080
165 environment:
166 - TUNNEL_TOKEN
167 # Share the proxy's network namespace so localhost:8080 reaches it.
168 network_mode: "service:mcp-proxy"
169 restart: unless-stopped
170 user: "65532:65532"
171 read_only: true
172 security_opt:
173 - no-new-privileges:true
174 cap_drop:
175 - ALL
176 stop_grace_period: 30s
177 logging:
178 options:
179 max-size: "10m"
180 max-file: "3"
181
182 mcp-proxy:
183 image: us-docker.pkg.dev/anthropic-public-registry/images/mcp-proxy@sha256:d243d30642d39857a0579e465cb3b9bf3ae7c1141e83143bb6856777a31467a5
184 volumes:
185 - ./config/mcp-proxy.yaml:/etc/mcp-gateway/config.yaml:ro
186 - ./data:/data:ro
187 restart: unless-stopped
188 user: "65532:65532"
189 read_only: true
190 security_opt:
191 - no-new-privileges:true
192 cap_drop:
193 - ALL
194 # Match shutdown_timeout in the proxy config
195 stop_grace_period: 30s
196 logging:
197 options:
198 max-size: "10m"
199 max-file: "3"
200 EOF
201 ```
202 </Step>
203
204 <Step title="Provision the tunnel">
205 Read the Tunnels API key into an environment variable, then run the setup component. It creates the tunnel, generates the CA and server certificate, registers the CA with Anthropic, fetches the tunnel token, and writes everything to `data/`.
206
207 ```bash theme={null}
208 # Paste the Tunnels API key (input is hidden)
209 read -rs API_TOKEN && export API_TOKEN
210
211 docker compose run --rm setup
212 ```
213
214 Read the tunnel domain and keep it for later steps.
215
216 ```bash theme={null}
217 export TUNNEL_DOMAIN=$(sudo cat data/tunnel-domain)
218 echo "$TUNNEL_DOMAIN"
219 ```
220
221 <Warning>
222 Revoke the Tunnels API key in **Organization settings > Tunnels > Tunnels API** before continuing, and run `unset API_TOKEN`. The stack does not need the key at runtime.
223 </Warning>
224 </Step>
225
226 <Step title="Write the proxy config">
227 `tunnel_domain` is required so the proxy can strip the domain from incoming hostnames and look up the remaining subdomain in `routes`. `routes` is a map, not a list.
228
229 ```bash theme={null}
230 cat > config/mcp-proxy.yaml <<EOF
231 listen_addr: ":8080"
232 log_level: info
233 shutdown_timeout: 30s
234 tunnel_domain: ${TUNNEL_DOMAIN}
235 tls:
236 cert_file: /data/tls.crt
237 key_file: /data/tls.key
238 routes:
239 docs: http://docs-mcp.example.corp:8080
240 search: http://10.0.12.7:9000
241 upstream:
242 allowed_ips:
243 - 10.0.0.0/8
244 EOF
245 ```
246
247 `upstream.allowed_ips` is the proxy's protection against server-side request forgery. Use the narrowest ranges that cover your MCP servers. Setting it replaces the RFC 1918 default rather than extending it.
248 </Step>
249
250 <Step title="Start the stack">
251 ```bash theme={null}
252 export TUNNEL_TOKEN=$(sudo cat data/tunnel-token)
253 docker compose up -d
254 ```
255
256 The compose file reads `TUNNEL_TOKEN` from the host environment with no default, so repeat the export in every fresh shell and after a reboot. For a multi-host deployment, copy the `mcp-tunnel/` directory to each host and start it the same way. The same tunnel token and certificates work across all replicas.
257 </Step>
258 </Steps>
259
260 The `data/` directory now holds the tunnel ID, tunnel domain, tunnel token, CA key pair, and server key pair. Protect it with your organization's file-permission, encryption-at-rest, and secrets-management controls, and consider moving `ca.key` and `tunnel-token` to secure storage.
261 </Tab>
262</Tabs>
37**Helm**
38
39**1. Fetch the default values**
40
41```bash theme={null}
42helm show values \
43 oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
44 --version 2.1.0 > values.yaml
45```
46
47The file includes comments explaining each field.
48
49**2. Configure routes**
50
51Edit `values.yaml` and add a `routes` entry under `gateway.config` for each MCP server. Leave `tunnel.id` empty so the setup component creates the tunnel during install.
52
53```yaml values.yaml theme={null}
54tunnel:
55 id: ""
56 # Increment to rotate the tunnel token on a later upgrade.
57 tokenVersion: "1"
58
59gateway:
60 config:
61 routes:
62 docs: http://docs-mcp.example.corp:8080
63 search: http://10.0.12.7:9000
64```
65
66With these routes, Claude reaches the servers at `docs.<your-tunnel-domain>` and `search.<your-tunnel-domain>`. If a route targets an address outside the RFC 1918 private ranges (some managed Kubernetes distributions allocate Service IPs elsewhere), add the range under `gateway.config.upstream.allowed_ips` as described in [Troubleshooting](/docs/connectors/mcp-tunnels/troubleshooting#proxy-logs-ip-validation-failed).
67
68**3. Review the rendered manifests**
69
70Render the chart with a placeholder key and review the output according to your organization's practices for third-party manifests. Rendering makes no API calls.
71
72```bash theme={null}
73helm template mcp-tunnel \
74 oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
75 --version 2.1.0 \
76 -n mcp-tunnel \
77 -f values.yaml \
78 --set api.token=placeholder > rendered.yaml
79```
80
81**4. Install**
82
83Read the Tunnels API key into an environment variable so it stays out of your shell history and values file, then install into a dedicated namespace.
84
85```bash theme={null}
86# Paste the Tunnels API key (input is hidden)
87read -rs API_TOKEN && export API_TOKEN
88
89helm install mcp-tunnel \
90 oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
91 --version 2.1.0 \
92 --namespace mcp-tunnel --create-namespace \
93 -f values.yaml \
94 --set api.token="$API_TOKEN"
95```
96
97The setup component runs as a pre-install hook, so `helm install` blocks until the tunnel is created, the CA is registered, and the credentials are stored in the `mcp-tunnel` Secret. If the install fails with a hook error, see [Troubleshooting](/docs/connectors/mcp-tunnels/troubleshooting#helm-install-fails-with-a-hook-error).
98
99> [!WARNING]
100>
101> Revoke the Tunnels API key in **Organization settings > Tunnels > Tunnels API** as soon as the install completes. Helm records `--set` values in its release history Secrets, and Kubernetes Secrets are not encrypted at rest by default, so the key remains recoverable from the cluster until you revoke it.
102
103**5. Read the tunnel domain**
104
105You need the tunnel domain to add connectors later.
106
107```bash theme={null}
108kubectl -n mcp-tunnel get secret mcp-tunnel \
109 -o jsonpath='{.data.tunnel-domain}' | base64 -d
110```
111
112The value looks like `abc123.tunnel.anthropic.com`.
113
114To restrict the pod's egress at the network level, set `networkPolicy.enabled: true` in `values.yaml` and list your MCP servers under `networkPolicy.mcpServers`. The policy already allows cloudflared to reach the tunnel edge. Your cluster's network plugin must support NetworkPolicy.
115
116For later configuration changes such as routes or replica count, edit `values.yaml` and run `helm upgrade` with the same `--version` and `-f values.yaml`, without the API key. Keep a complete `values.yaml` rather than relying on `--reuse-values`, because Helm's deep merge can silently keep a route you deleted.
117
118**Docker Compose**
119
120**1. Prepare the deployment directory**
121
122```bash theme={null}
123mkdir -p mcp-tunnel/{config,data}
124cd mcp-tunnel
125sudo chown 65532:65532 data
126```
127
128The containers run as the non-root user ID `65532` and need write access to `data/`.
129
130**2. Write docker-compose.yaml**
131
132The compose file pins images by digest, runs every container as non-root with a read-only filesystem, drops all Linux capabilities, and disables privilege escalation.
133
134```bash theme={null}
135cat > docker-compose.yaml <<'EOF'
136services:
137 # One-time provisioning. Run with: docker compose run --rm setup
138 setup:
139 image: us-docker.pkg.dev/anthropic-public-registry/images/mcp-proxy@sha256:d243d30642d39857a0579e465cb3b9bf3ae7c1141e83143bb6856777a31467a5
140 entrypoint: ["/setup"]
141 command:
142 - init
143 - --api-url=https://api.anthropic.com
144 - --output=dir:/data
145 - --token-version=1
146 environment:
147 - API_TOKEN
148 volumes:
149 - ./data:/data
150 user: "65532:65532"
151 read_only: true
152 security_opt:
153 - no-new-privileges:true
154 cap_drop:
155 - ALL
156 profiles: ["setup"]
157
158 cloudflared:
159 image: cloudflare/cloudflared@sha256:0e56c13c7058fcef9c4f0e7d83372f82330548dbf8f8b366f3d8b506526d93b2
160 command: tunnel --no-autoupdate run --url http://localhost:8080
161 environment:
162 - TUNNEL_TOKEN
163 # Share the proxy's network namespace so localhost:8080 reaches it.
164 network_mode: "service:mcp-proxy"
165 restart: unless-stopped
166 user: "65532:65532"
167 read_only: true
168 security_opt:
169 - no-new-privileges:true
170 cap_drop:
171 - ALL
172 stop_grace_period: 30s
173 logging:
174 options:
175 max-size: "10m"
176 max-file: "3"
177
178 mcp-proxy:
179 image: us-docker.pkg.dev/anthropic-public-registry/images/mcp-proxy@sha256:d243d30642d39857a0579e465cb3b9bf3ae7c1141e83143bb6856777a31467a5
180 volumes:
181 - ./config/mcp-proxy.yaml:/etc/mcp-gateway/config.yaml:ro
182 - ./data:/data:ro
183 restart: unless-stopped
184 user: "65532:65532"
185 read_only: true
186 security_opt:
187 - no-new-privileges:true
188 cap_drop:
189 - ALL
190 # Match shutdown_timeout in the proxy config
191 stop_grace_period: 30s
192 logging:
193 options:
194 max-size: "10m"
195 max-file: "3"
196EOF
197```
198
199**3. Provision the tunnel**
200
201Read the Tunnels API key into an environment variable, then run the setup component. It creates the tunnel, generates the CA and server certificate, registers the CA with Anthropic, fetches the tunnel token, and writes everything to `data/`.
202
203```bash theme={null}
204# Paste the Tunnels API key (input is hidden)
205read -rs API_TOKEN && export API_TOKEN
206
207docker compose run --rm setup
208```
209
210Read the tunnel domain and keep it for later steps.
211
212```bash theme={null}
213export TUNNEL_DOMAIN=$(sudo cat data/tunnel-domain)
214echo "$TUNNEL_DOMAIN"
215```
216
217> [!WARNING]
218>
219> Revoke the Tunnels API key in **Organization settings > Tunnels > Tunnels API** before continuing, and run `unset API_TOKEN`. The stack does not need the key at runtime.
220
221**4. Write the proxy config**
222
223`tunnel_domain` is required so the proxy can strip the domain from incoming hostnames and look up the remaining subdomain in `routes`. `routes` is a map, not a list.
224
225```bash theme={null}
226cat > config/mcp-proxy.yaml <<EOF
227listen_addr: ":8080"
228log_level: info
229shutdown_timeout: 30s
230tunnel_domain: ${TUNNEL_DOMAIN}
231tls:
232 cert_file: /data/tls.crt
233 key_file: /data/tls.key
234routes:
235 docs: http://docs-mcp.example.corp:8080
236 search: http://10.0.12.7:9000
237upstream:
238 allowed_ips:
239 - 10.0.0.0/8
240EOF
241```
242
243`upstream.allowed_ips` is the proxy's protection against server-side request forgery. Use the narrowest ranges that cover your MCP servers. Setting it replaces the RFC 1918 default rather than extending it.
244
245**5. Start the stack**
246
247```bash theme={null}
248export TUNNEL_TOKEN=$(sudo cat data/tunnel-token)
249docker compose up -d
250```
251
252The compose file reads `TUNNEL_TOKEN` from the host environment with no default, so repeat the export in every fresh shell and after a reboot. For a multi-host deployment, copy the `mcp-tunnel/` directory to each host and start it the same way. The same tunnel token and certificates work across all replicas.
253
254The `data/` directory now holds the tunnel ID, tunnel domain, tunnel token, CA key pair, and server key pair. Protect it with your organization's file-permission, encryption-at-rest, and secrets-management controls, and consider moving `ca.key` and `tunnel-token` to secure storage.
263255
264256## Verify the connection
265257
266258Check the logs on your side first. cloudflared logs four `Registered tunnel connection` lines when it has reached the tunnel edge, and the proxy logs one `route configured` line per route.
267259
268<CodeGroup>
269 ```bash Helm theme={null}
270 kubectl -n mcp-tunnel logs deploy/mcp-tunnel -c cloudflared | grep "Registered tunnel connection"
271 kubectl -n mcp-tunnel logs deploy/mcp-tunnel -c mcp-proxy | grep "route configured"
272 ```
273
274 ```bash Docker Compose theme={null}
275 docker compose logs cloudflared | grep "Registered tunnel connection"
276 docker compose logs mcp-proxy | grep "route configured"
277 ```
278</CodeGroup>
260```bash Helm theme={null}
261kubectl -n mcp-tunnel logs deploy/mcp-tunnel -c cloudflared | grep "Registered tunnel connection"
262kubectl -n mcp-tunnel logs deploy/mcp-tunnel -c mcp-proxy | grep "route configured"
263```
264
265```bash Docker Compose theme={null}
266docker compose logs cloudflared | grep "Registered tunnel connection"
267docker compose logs mcp-proxy | grep "route configured"
268```
279269
280270The containers take a few seconds to start, so rerun the commands if they come back empty. If cloudflared never registers, see [Troubleshooting](/docs/connectors/mcp-tunnels/troubleshooting#the-tunnel-stack-starts-but-cloudflared-never-connects). The end-to-end check happens from Claude, in the next section.
281271
from line 273
283273
284274Each route becomes a custom connector for your organization. The connector URL is the route's tunnel hostname plus the path your MCP server serves. Many servers serve at `/mcp`, and the proxy forwards the path unchanged.
285275
286<Steps>
287 <Step title="Open organization connectors">
288 In claude.ai, go to **Organization settings > Connectors**.
289 </Step>
290
291 <Step title="Add a custom connector">
292 Select **Add**, then **Custom**. If Claude asks for the connector type, choose **Web**.
293 </Step>
294
295 <Step title="Enter the tunnel URL">
296 Enter the server URL, for example `https://docs.abc123.tunnel.anthropic.com/mcp`.
297 </Step>
298
299 <Step title="Configure authentication">
300 Configure authentication for the server. If its OAuth authorization server is also inside your network, turn on **Tunnel OAuth configuration** and follow [Authenticate to MCP servers behind a tunnel](/docs/connectors/mcp-tunnels/oauth).
301 </Step>
302
303 <Step title="Add the connector">
304 Select **Add**.
305 </Step>
306</Steps>
276**1. Open organization connectors**
277
278In claude.ai, go to **Organization settings > Connectors**.
279
280**2. Add a custom connector**
281
282Select **Add**, then **Custom**. If Claude asks for the connector type, choose **Web**.
283
284**3. Enter the tunnel URL**
285
286Enter the server URL, for example `https://docs.abc123.tunnel.anthropic.com/mcp`.
287
288**4. Configure authentication**
289
290Configure authentication for the server. If its OAuth authorization server is also inside your network, turn on **Tunnel OAuth configuration** and follow [Authenticate to MCP servers behind a tunnel](/docs/connectors/mcp-tunnels/oauth).
291
292**5. Add the connector**
293
294Select **Add**.
307295
308296Members then find the connector in their own connector settings and select **Connect** to sign in, as described in [Add a connector by URL](/docs/connectors/custom/add-unlisted#add-a-connector-by-url). To confirm the tunnel end to end, connect the server yourself and ask Claude to use one of its tools while you watch the proxy logs for the request.
309297
from line 299
311299
312300Adding another MCP server later takes a new route and a new connector. No certificate or cloudflared changes are needed, because the server certificate covers every subdomain of your tunnel domain.
313301
314<Steps>
315 <Step title="Add a route">
316 Add a route for the new server.
317 </Step>
318
319 <Step title="Apply the change">
320 Apply the change:
321
322 <CodeGroup>
323 ```bash Helm theme={null}
324 # After adding the route under gateway.config.routes in values.yaml
325 helm upgrade mcp-tunnel \
326 oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
327 --version 2.1.0 \
328 -n mcp-tunnel \
329 -f values.yaml
330 ```
331
332 ```bash Docker Compose theme={null}
333 # After adding the route in config/mcp-proxy.yaml
334 docker compose restart mcp-proxy
335 ```
336 </CodeGroup>
337 </Step>
338
339 <Step title="Register the connector">
340 Register the new hostname as another custom connector.
341 </Step>
342</Steps>
302**1. Add a route**
303
304Add a route for the new server.
305
306**2. Apply the change**
307
308Apply the change:
309
310```bash Helm theme={null}
311# After adding the route under gateway.config.routes in values.yaml
312helm upgrade mcp-tunnel \
313 oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
314 --version 2.1.0 \
315 -n mcp-tunnel \
316 -f values.yaml
317```
318
319```bash Docker Compose theme={null}
320# After adding the route in config/mcp-proxy.yaml
321docker compose restart mcp-proxy
322```
323
324**3. Register the connector**
325
326Register the new hostname as another custom connector.
343327
344328## Rotate credentials
345329
from line 337
353337
354338The tunnel token authenticates cloudflared's outbound connection. Rotate it on your regular schedule and immediately if you suspect exposure. Rotation does not sever connections that are already established, so you can rotate, restart cloudflared with the new value, and let the old connections drain.
355339
356<Tabs>
357 <Tab title="Helm">
358 <Steps>
359 <Step title="Increment the token version">
360 Increment `tunnel.tokenVersion` in `values.yaml`.
361 </Step>
362
363 <Step title="Create a Tunnels API key">
364 Create a fresh Tunnels API key.
365 </Step>
366
367 <Step title="Upgrade">
368 Upgrade. The setup component re-runs, rotates the token, and updates the Secret.
369
370 ```bash theme={null}
371 read -rs API_TOKEN && export API_TOKEN
372
373 helm upgrade mcp-tunnel \
374 oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
375 --version 2.1.0 \
376 -n mcp-tunnel \
377 -f values.yaml \
378 --set api.token="$API_TOKEN" \
379 --set setup.force=true
380 ```
381
382 Revoke the API key once the upgrade completes.
383 </Step>
384 </Steps>
385 </Tab>
386
387 <Tab title="Docker Compose">
388 <Steps>
389 <Step title="Increment the token version">
390 Edit `docker-compose.yaml` and increment the `--token-version` value in the `setup` service (for example from `1` to `2`), so the new value persists for future runs.
391 </Step>
392
393 <Step title="Create a Tunnels API key">
394 Create a fresh Tunnels API key.
395 </Step>
396
397 <Step title="Re-run setup">
398 Re-run setup, then restart cloudflared with the new token:
399
400 ```bash theme={null}
401 read -rs API_TOKEN && export API_TOKEN
402 docker compose run --rm setup
403
404 export TUNNEL_TOKEN=$(sudo cat data/tunnel-token)
405 docker compose up -d cloudflared
406 ```
407 </Step>
408 </Steps>
409
410 Revoke the API key and run `unset API_TOKEN` once rotation completes. For a multi-host deployment, setup writes the new token only to the `data/` directory on the host where it ran, so copy the updated `data/` directory (at minimum `data/tunnel-token`) to every other host that runs a replica. Then repeat the last two commands on each of those hosts so every replica restarts with the new token.
411 </Tab>
412</Tabs>
340**Helm**
341
342**1. Increment the token version**
343
344Increment `tunnel.tokenVersion` in `values.yaml`.
345
346**2. Create a Tunnels API key**
347
348Create a fresh Tunnels API key.
349
350**3. Upgrade**
351
352Upgrade. The setup component re-runs, rotates the token, and updates the Secret.
353
354```bash theme={null}
355read -rs API_TOKEN && export API_TOKEN
356
357helm upgrade mcp-tunnel \
358 oci://us-docker.pkg.dev/anthropic-public-registry/charts/mcp-tunnel \
359 --version 2.1.0 \
360 -n mcp-tunnel \
361 -f values.yaml \
362 --set api.token="$API_TOKEN" \
363 --set setup.force=true
364```
365
366Revoke the API key once the upgrade completes.
367
368**Docker Compose**
369
370**1. Increment the token version**
371
372Edit `docker-compose.yaml` and increment the `--token-version` value in the `setup` service (for example from `1` to `2`), so the new value persists for future runs.
373
374**2. Create a Tunnels API key**
375
376Create a fresh Tunnels API key.
377
378**3. Re-run setup**
379
380Re-run setup, then restart cloudflared with the new token:
381
382```bash theme={null}
383read -rs API_TOKEN && export API_TOKEN
384docker compose run --rm setup
385
386export TUNNEL_TOKEN=$(sudo cat data/tunnel-token)
387docker compose up -d cloudflared
388```
389
390Revoke the API key and run `unset API_TOKEN` once rotation completes. For a multi-host deployment, setup writes the new token only to the `data/` directory on the host where it ran, so copy the updated `data/` directory (at minimum `data/tunnel-token`) to every other host that runs a replica. Then repeat the last two commands on each of those hosts so every replica restarts with the new token.
413391
414392### Renew the server certificate
415393
416394The server certificate the proxy presents is valid for 90 days, and you are responsible for renewing it before it expires. Renewal is local. It signs a new certificate with the CA already stored in your deployment, makes no API calls, and needs no API key. The proxy reloads the certificate file automatically, so no restart is required.
417395
418<Tabs>
419 <Tab title="Helm">
420 The chart deploys a CronJob that runs daily and renews the certificate once it is within 30 days of expiry. Monitor the CronJob and the certificate's expiry date to confirm renewal completes.
421 </Tab>
422
423 <Tab title="Docker Compose">
424 Run the renewal from the deployment directory. With `--renew-before=720h` the command does nothing while more than 30 days of validity remain, so it is safe to run on a schedule such as a daily cron entry.
425
426 ```bash theme={null}
427 docker compose run --rm setup renew-cert --output=dir:/data --renew-before=720h
428 ```
429 </Tab>
430</Tabs>
396**Helm**
397
398The chart deploys a CronJob that runs daily and renews the certificate once it is within 30 days of expiry. Monitor the CronJob and the certificate's expiry date to confirm renewal completes.
399
400**Docker Compose**
401
402Run the renewal from the deployment directory. With `--renew-before=720h` the command does nothing while more than 30 days of validity remain, so it is safe to run on a schedule such as a daily cron entry.
403
404```bash theme={null}
405docker compose run --rm setup renew-cert --output=dir:/data --renew-before=720h
406```
431407
432408## Remove a tunnel
433409
434410Decommission a tunnel when you no longer need it, or as the first steps of responding to a suspected compromise. Archiving a tunnel invalidates its token, detaches its domain, and is permanent.
435411
436<Steps>
437 <Step title="Record the tunnel ID">
438 <CodeGroup>
439 ```bash Helm theme={null}
440 TUNNEL_ID=$(kubectl -n mcp-tunnel get secret mcp-tunnel \
441 -o jsonpath='{.data.tunnel-id}' | base64 -d)
442 ```
443
444 ```bash Docker Compose theme={null}
445 TUNNEL_ID=$(sudo cat data/tunnel-id)
446 ```
447 </CodeGroup>
448 </Step>
449
450 <Step title="Stop the tunnel stack">
451 <CodeGroup>
452 ```bash Helm theme={null}
453 helm uninstall mcp-tunnel -n mcp-tunnel
454 ```
455
456 ```bash Docker Compose theme={null}
457 docker compose down
458 ```
459 </CodeGroup>
460
461 If you are responding to a suspected compromise, use `docker compose down --timeout 0` to sever the connection immediately.
462 </Step>
463
464 <Step title="Remove the connectors">
465 In **Organization settings > Connectors**, remove each custom connector that points at the tunnel's hostnames.
466 </Step>
467
468 <Step title="Archive the tunnel">
469 Create a fresh Tunnels API key and call the [archive endpoint](https://platform.claude.com/docs/en/api/beta/tunnels/archive) of the Tunnels API. Revoke the key when you are done.
470
471 ```bash theme={null}
472 read -rs API_TOKEN && export API_TOKEN
473
474 curl -X POST "https://api.anthropic.com/v1/tunnels/${TUNNEL_ID}/archive" \
475 -H "Authorization: Bearer $API_TOKEN" \
476 -H "anthropic-version: 2023-06-01" \
477 -H "anthropic-beta: mcp-tunnels-2026-06-22"
478 ```
479 </Step>
480
481 <Step title="Delete the stored credentials">
482 <CodeGroup>
483 ```bash Helm theme={null}
484 # The setup component created this Secret, so helm uninstall leaves it behind
485 kubectl -n mcp-tunnel delete secret mcp-tunnel
486 ```
487
488 ```bash Docker Compose theme={null}
489 sudo rm -rf data
490 ```
491 </CodeGroup>
492 </Step>
493</Steps>
412**1. Record the tunnel ID**
413
414```bash Helm theme={null}
415TUNNEL_ID=$(kubectl -n mcp-tunnel get secret mcp-tunnel \
416 -o jsonpath='{.data.tunnel-id}' | base64 -d)
417```
418
419```bash Docker Compose theme={null}
420TUNNEL_ID=$(sudo cat data/tunnel-id)
421```
422
423**2. Stop the tunnel stack**
424
425```bash Helm theme={null}
426helm uninstall mcp-tunnel -n mcp-tunnel
427```
428
429```bash Docker Compose theme={null}
430docker compose down
431```
432
433If you are responding to a suspected compromise, use `docker compose down --timeout 0` to sever the connection immediately.
434
435**3. Remove the connectors**
436
437In **Organization settings > Connectors**, remove each custom connector that points at the tunnel's hostnames.
438
439**4. Archive the tunnel**
440
441Create a fresh Tunnels API key and call the [archive endpoint](https://platform.claude.com/docs/en/api/beta/tunnels/archive) of the Tunnels API. Revoke the key when you are done.
442
443```bash theme={null}
444read -rs API_TOKEN && export API_TOKEN
445
446curl -X POST "https://api.anthropic.com/v1/tunnels/${TUNNEL_ID}/archive" \
447 -H "Authorization: Bearer $API_TOKEN" \
448 -H "anthropic-version: 2023-06-01" \
449 -H "anthropic-beta: mcp-tunnels-2026-06-22"
450```
451
452**5. Delete the stored credentials**
453
454```bash Helm theme={null}
455# The setup component created this Secret, so helm uninstall leaves it behind
456kubectl -n mcp-tunnel delete secret mcp-tunnel
457```
458
459```bash Docker Compose theme={null}
460sudo rm -rf data
461```
494462
495463If you archived the tunnel because of a suspected compromise, also notify your Anthropic account team, rotate any OAuth tokens or secrets your MCP servers issued, and review the proxy, cloudflared, and MCP server logs for the affected period before you provision a replacement tunnel.
496464
497465
No line in this hunk matches that.