troubleshooting changedagents-and-tools/mcp-tunnels/troubleshooting
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+13added
Lines−13removed
From line
14
where the diff opens
First seen
14 Aug 2026
this site's first read of the page
Recorded edits4to this page, all time
## Tunnel won’t connect ## Tunnel won't connect
The whole hunk
from line 14, old and new numbered
/
from line 14
1414
1515| Symptom | Cause | Fix |
1616| -------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
17| Tunnel doesn't appear in the agent **+ MCP Server** picker | The picker only lists tunnels in the session's workspace that have at least one active certificate. | Register a CA certificate, or open the session in the workspace the tunnel was created in. |
17| Tunnel doesn’t appear in the agent **+ MCP Server** picker | The picker only lists tunnels in the session’s workspace that have at least one active certificate. | Register a CA certificate, or open the session in the workspace the tunnel was created in. |
1818| Caller sees HTTP 500; [cloudflared](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) logs `No ingress rules were defined` | cloudflared has no local target. | Add `--url http://localhost:8080` and `network_mode: "service:mcp-proxy"` to the cloudflared service. |
19| Proxy logs `no route for host` | `tunnel_domain` doesn't match the assigned domain, or `config.yaml` was edited without restarting. | Set `tunnel_domain` to the exact domain shown on the tunnel detail page, then restart the proxy (`docker compose restart mcp-proxy`). |
19| Proxy logs `no route for host` | `tunnel_domain` doesn’t match the assigned domain, or `config.yaml` was edited without restarting. | Set `tunnel_domain` to the exact domain shown on the tunnel detail page, then restart the proxy (`docker compose restart mcp-proxy`). |
2020| Proxy logs `IP validation failed: <ip> is not a private address` | Upstream MCP server resolves outside RFC1918. | See [Upstream IP validation](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/troubleshooting#upstream-ip-validation). |
2121| Proxy exits with `cannot unmarshal !!seq into map[string]string` | `routes` is a YAML list. | Use `routes: { name: http://host:port }`. |
2222| Proxy exits with `open /data/tls.key: permission denied` | The key is `0600`; the proxy container runs non-root. | `chmod 644 data/tls.key`. |
from line 26
2626
2727## OAuth fails behind a source-IP allowlist
2828
29OAuth flows fail when your authorization server's source-IP allowlist blocks Anthropic's back end from reaching `/token`, `/register`, and the discovery endpoints. If you'd rather not allowlist Anthropic's egress ranges, you can route the backend-to-backend OAuth calls through the tunnel while keeping the browser-facing `/authorize` endpoint on your existing public hostname.
29OAuth flows fail when your authorization server’s source-IP allowlist blocks Anthropic’s back end from reaching `/token`, `/register`, and the discovery endpoints. If you’d rather not allowlist Anthropic’s egress ranges, you can route the backend-to-backend OAuth calls through the tunnel while keeping the browser-facing `/authorize` endpoint on your existing public hostname.
3030
3131<Steps>
3232 <Step title="Add a proxy route for the authorization server">
from line 40
4040 </Step>
4141
4242 <Step title="Serve split-endpoint discovery metadata">
43 Your authorization server's `/.well-known/oauth-authorization-server` response should point `authorization_endpoint` at your existing allowlisted hostname and everything else at the tunnel:
43 Your authorization server’s `/.well-known/oauth-authorization-server` response should point `authorization_endpoint` at your existing allowlisted hostname and everything else at the tunnel:
4444
4545 ```json
4646 {
from line 54
5454 </Step>
5555
5656 <Step title="Point the MCP server at the tunnel issuer">
57 Your MCP server's `/.well-known/oauth-protected-resource` response should reference the tunnel hostname as its authorization server:
57 Your MCP server’s `/.well-known/oauth-protected-resource` response should reference the tunnel hostname as its authorization server:
5858
5959 ```json
6060 {
from line 65
6565 </Step>
6666</Steps>
6767
68With this configuration, the user's browser requests `/authorize` on your existing hostname (which your allowlist already includes), whereas Anthropic's back end reaches `/token`, `/register`, and the discovery documents through the tunnel.
68With this configuration, the user’s browser requests `/authorize` on your existing hostname (which your allowlist already includes), whereas Anthropic’s back end reaches `/token`, `/register`, and the discovery documents through the tunnel.
6969
7070## Setup component authentication failures
7171
from line 73
7373
7474Tunnels-specific causes:
7575
76* The chart's default audience is `api.anthropic.com` (no scheme). If your rule's audience is `https://api.anthropic.com`, set `api.wif.audience` to match.
77* A `403` from the Tunnels API after a successful exchange means the rule's scope doesn't include `workspace:manage_tunnels`, or the rule's service account isn't a member of the tunnel's workspace. Set the scope and add the service account to the workspace.
76* The chart’s default audience is `api.anthropic.com` (no scheme). If your rule’s audience is `https://api.anthropic.com`, set `api.wif.audience` to match.
77* A `403` from the Tunnels API after a successful exchange means the rule’s scope doesn’t include `workspace:manage_tunnels`, or the rule’s service account isn’t a member of the tunnel’s workspace. Set the scope and add the service account to the workspace.
7878
79On Helm, the setup component runs as a pre-install hook Job. On failure, the Job is left behind for inspection (`kubectl logs job/mcp-tunnel-setup -n mcp-tunnel`). Helm doesn't manage hook resources, so delete it before retrying:
79On Helm, the setup component runs as a pre-install hook Job. On failure, the Job is left behind for inspection (`kubectl logs job/mcp-tunnel-setup -n mcp-tunnel`). Helm doesn’t manage hook resources, so delete it before retrying:
8080
8181```bash
8282helm uninstall mcp-tunnel -n mcp-tunnel
from line 83
8383kubectl -n mcp-tunnel delete job mcp-tunnel-setup
8484```
8585
86## Tunnel won't connect
86## Tunnel won’t connect
8787
8888Check the cloudflared logs first. Common causes:
8989
from line 94
9494
9595## Certificate errors
9696
97When Anthropic rejects the proxy's certificate during inner TLS, the proxy logs `tls handshake failed`. Verify that:
97When Anthropic rejects the proxy’s certificate during inner TLS, the proxy logs `tls handshake failed`. Verify that:
9898
9999* The server certificate has not expired.
100* The certificate's Subject Alternative Name matches `*.<tunnel-domain>`.
100* The certificate’s Subject Alternative Name matches `*.<tunnel-domain>`.
101101* The signing CA is registered with Anthropic for this tunnel.
102102
103103See the [certificate requirements](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/reference#certificate-requirements) for the full validation rules.
from line 106
106106
107107For SSRF protection, the proxy only dials addresses in the RFC1918 private ranges (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`) by default. Only IPv4 is supported for the proxy-to-upstream connection. (The cloudflared-to-edge egress range in [Network requirements](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/overview#network-requirements) is a different hop.)
108108
109If the proxy logs `IP validation failed: <ip> is not a private address`, the upstream hostname resolved outside that set. On Kubernetes, some managed distributions allocate the Service CIDR outside RFC1918; if `kubectl get svc kubernetes -n default -o jsonpath='{.spec.clusterIP}'` returns an address outside the private ranges, look up your cluster's Service CIDR and add it.
109If the proxy logs `IP validation failed: <ip> is not a private address`, the upstream hostname resolved outside that set. On Kubernetes, some managed distributions allocate the Service CIDR outside RFC1918; if `kubectl get svc kubernetes -n default -o jsonpath='{.spec.clusterIP}'` returns an address outside the private ranges, look up your cluster’s Service CIDR and add it.
110110
111111If the address is legitimate, add the narrowest covering CIDR to `upstream.allowed_ips`. Setting `allowed_ips` **replaces** the RFC1918 default rather than extending it, so include the private ranges your other upstream MCP servers use:
112112
No line in this hunk matches that.