Create a Docker MCP tunnel
/create-docker-mcp-tunnel[**MCP tunnels quickstart**](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/quickstart)
--- description: Stand up an Anthropic MCP tunnel locally with Docker Compose so Claude can call a private MCP server (manual-credentials quickstart). argument-hint: "[deployment-dir] (default: ./mcp-tunnel)" allowed-tools: [Bash, Read, Write, Edit, AskUserQuestion] --- # Create a Docker MCP tunnel Drive the [MCP tunnels quickstart](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/quickstart) end to end: from zero to Claude calling a private MCP server through an Anthropic-operated tunnel, using Docker Compose with manually supplied credentials (the shortest path for local testing). > MCP tunnels is in research preview. It is provided "as-is" with no uptime > or support commitment and depends on a third-party transport (Cloudflare). > Do not put production traffic through this without reading the > [security model](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/security). You are guiding the user through a mix of local commands you run and Console actions only they can do (creating the tunnel, uploading the CA). Be a careful operator: explain each step briefly, run the commands, check the output, and stop with a clear diagnosis if something fails. Deployment directory: use $ARGUMENTS if the user passed a path, otherwise default to ./mcp-tunnel. Refer to it below as $DIR. ## What you'll build A container stack on the user's machine: - mcp-proxy : Anthropic's proxy. Terminates the inner TLS handshake using a certificate the user controls, validates upstream IPs, routes by hostname. - cloudflared : the tunnel agent. Outbound-only connection to the Anthropic tunnel edge; shares the proxy's network namespace. - hello-mcp (optional) : a sample FastMCP server, only if the user has no MCP server of their own to expose yet. When it's up, the routed server is reachable from Claude at https://<subdomain>.<tunnel-domain>/<path> with nothing listening on a public port. ## Step 0 : Preflight Run these and report what's missing before going further: ``bash docker --version && docker compose version && openssl version ` - Docker + Docker Compose are required. openssl 1.1.1+ is required (the commands below use -addext, available in 1.1.1+). - Confirm the host has **outbound** access to api.anthropic.com:443 and the tunnel edge (198.41.192.0/19, 2606:4700:a0::/44) on **7844 TCP and UDP**. No inbound ports are opened. If docker compose (v2) is unavailable but docker-compose (v1) exists, use that and tell the user; the compose file is v2-compatible. ## Step 1 : Create the tunnel (Console : user action) Tell the user to do this in the [Claude Console](https://console.anthropic.com) (see [Create a tunnel](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#create-a-tunnel)): 1. Sidebar → **Manage → MCP tunnels** → **New tunnel**. Give it a name. 2. Leave **Set up programmatic access** **off** : this quickstart uses manual credentials. 3. Open the tunnel. From the **Connection** section copy two values: - **Domain** : looks like abcd1234.tunnel.anthropic.com - **Token** : click the eye icon, then copy Then ask the user, via AskUserQuestion or a direct prompt, for the **Domain**. **Do not ask them to paste the Token into the chat.** The token is a secret that authenticates the outbound tunnel connection; keep it out of the transcript. Instead, tell them you will create a $DIR/.env file and they should paste the token into it themselves (Step 3), or have them export it: export TUNNEL_TOKEN='eyJ...' in the shell you'll run compose from. Record the domain as TUNNEL_DOMAIN for the steps below. ## Step 2 : Deployment directory `bash mkdir -p "$DIR"/{config,data} cd "$DIR" ` ## Step 3 : Credentials file Create $DIR/.env (compose auto-loads it; this survives reboots, unlike a shell export). Write TUNNEL_DOMAIN yourself; leave a placeholder for the secret and have the **user** fill it in: ` TUNNEL_DOMAIN=<the domain from step 1> TUNNEL_TOKEN=PASTE_TUNNEL_TOKEN_HERE ` Then lock it down and make sure it never gets committed: `bash chmod 600 "$DIR/.env" printf '.env\ndata/\n' > "$DIR/.gitignore" ` Pause and have the user replace PASTETUNNELTOKEN_HERE with the real token (tell them the exact file path). Verify it's set without printing it: `bash cd "$DIR" && grep -q '^TUNNEL_TOKEN=eyJ' .env && echo "token looks set" || echo "token NOT set : edit .env" ` Load it for the openssl/config steps in this shell: `bash cd "$DIR" && set -a && . ./.env && set +a && echo "domain: $TUNNEL_DOMAIN" ` ## Step 4 : Generate the CA and server certificate The proxy terminates an inner TLS handshake using a certificate signed by a CA the user controls. Generate both (Linux/macOS shown; the [quickstart](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/quickstart) also has a Windows PowerShell variant : offer it if the user is on Windows): ``bash cd