Claude Platform Docs
MessagesTunnel MCP

Distribuire i tunnel MCP con Docker Compose

Installa lo stack del tunnel MCP su una VM usando Docker Compose.

Questa guida distribuisce lo stack del tunnel come container rafforzati (hardened) su un singolo host. La stessa configurazione può essere replicata su più host per garantire la disponibilità.

Prima di iniziare

Ti servono:

  • Un tunnel. Con l'accesso programmatico, il componente di setup ne crea uno per te quando non fornisci un ID tunnel; per collegarti invece a un tunnel esistente, crealo nella Console e annota l'ID del tunnel (tnl_...). Il provisioning manuale parte sempre da un tunnel creato nella Console.
  • Un modo per l'host di autenticarsi alla Tunnels API.
    • Accesso programmatico (consigliato). Attiva Set up programmatic access quando crei il tunnel (oppure crea la regola di federazione direttamente in Settings > Workload identity se lasci che il componente di setup crei il tunnel) in modo che il componente di setup possa autenticarsi tramite Workload Identity Federation. Annota l'ID della regola di federazione (fdrl_...) e l'ID della tua organizzazione.
    • Manuale. Salta l'accesso programmatico. Dovrai ottenere il token del tunnel dalla Console, generare tu stesso una CA e un certificato server, e registrare la CA nella Console.
  • Un host con Docker e Docker Compose installati. Il flusso manuale richiede anche openssl (1.1.1 o successivo).
  • Connettività di rete in uscita dall'host verso api.anthropic.com (443 TCP) e verso il tunnel edge (7844 TCP e UDP). Consulta i requisiti di rete completi.
  • Uno o più server MCP in esecuzione e raggiungibili dall'host agli indirizzi che configurerai in routes. Se non ne hai ancora uno, usa il server di esempio.

Opzionale: usa un server MCP di esempio

Se non hai un server MCP disponibile per i test, usa questo server minimale:

mkdir -p mcp-tunnel
cat > mcp-tunnel/hello_server.py <<'EOF'
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")
EOF

I passaggi di installazione seguenti eseguono cd in mcp-tunnel/ e indicano dove aggiungere il servizio e la route corrispondenti.

Installazione

Questa guida fornisce un approccio di riferimento basato su Docker Compose. Sei responsabile di adattarlo per soddisfare i requisiti di sicurezza della tua organizzazione.

Questo percorso richiede che l'host disponga di un identity provider OIDC (come il metadata server di una VM cloud o SPIFFE). In caso contrario, usa invece la scheda Senza accesso programmatico.

Il componente di setup usa Workload Identity Federation per recuperare il token del tunnel, generare una CA e un certificato server, e registrare la CA presso Anthropic.

  1. Prepara la directory di distribuzione

    mkdir -p mcp-tunnel/{config,data}
    cd mcp-tunnel
    sudo chown 65532:65532 data

    I container vengono eseguiti con l'UID non-root 65532 e necessitano dell'accesso in scrittura a data/.

  2. Scrivi docker-compose.yaml

    Il file compose fissa le immagini tramite digest SHA-256, esegue ogni container come non-root con un filesystem in sola lettura, rimuove tutte le capability Linux e disabilita l'escalation dei privilegi.

    cat > docker-compose.yaml <<'EOF'
    services:
      setup:
        image: us-docker.pkg.dev/anthropic-public-registry/images/mcp-proxy@sha256:efb27b299d627e4134815663cb8896641eeaee025d734c0f695582b4df38f013
        entrypoint: ["/setup"]
        command:
          - init
          - --api-url=https://api.anthropic.com
          - --output=dir:/data
          - --token-version=1
        environment:
          - TUNNEL_ID
          - ANTHROPIC_FEDERATION_RULE_ID
          - ANTHROPIC_ORGANIZATION_ID
          - ANTHROPIC_WORKSPACE_ID
          - ANTHROPIC_IDENTITY_TOKEN
        volumes:
          - ./data:/data
        user: "65532:65532"
        read_only: true
        security_opt:
          - no-new-privileges:true
        cap_drop:
          - ALL
        profiles: ["setup"]
    
      cloudflared:
        image: cloudflare/cloudflared@sha256:6b599ca3e974349ead3286d178da61d291961182ec3fe9c505e1dd02c8ac31b0
        command: tunnel --no-autoupdate run --url http://localhost:8080
        environment:
          - TUNNEL_TOKEN
        # Condividi il netns del proxy così che localhost:8080 lo raggiunga.
        network_mode: "service:mcp-proxy"
        restart: unless-stopped
        user: "65532:65532"
        read_only: true
        security_opt:
          - no-new-privileges:true
        cap_drop:
          - ALL
        stop_grace_period: 30s
        logging:
          options:
            max-size: "10m"
            max-file: "3"
    
      mcp-proxy:
        image: us-docker.pkg.dev/anthropic-public-registry/images/mcp-proxy@sha256:efb27b299d627e4134815663cb8896641eeaee025d734c0f695582b4df38f013
        volumes:
          - ./config/mcp-proxy.yaml:/etc/mcp-gateway/config.yaml:ro
          - ./data:/data:ro
        restart: unless-stopped
        user: "65532:65532"
        read_only: true
        security_opt:
          - no-new-privileges:true
        cap_drop:
          - ALL
        stop_grace_period: 30s
        logging:
          options:
            max-size: "10m"
            max-file: "3"
    EOF

    Se stai usando il server MCP di esempio, aggiungilo come servizio:

    cat >> docker-compose.yaml <<'EOF'
    
      hello-mcp:
        image: python:3.13-slim
        working_dir: /app
        volumes:
          - ./hello_server.py:/app/hello_server.py:ro
        command: sh -c "pip install --quiet mcp && python hello_server.py"
        restart: unless-stopped
    EOF
  3. Esegui il provisioning del tunnel

    Imposta gli identificatori. Lascia TUNNEL_ID non impostato per far creare un tunnel al componente di setup; impostalo per collegarti a un tunnel esistente dalla Console:

    # export TUNNEL_ID=tnl_...   # imposta per collegarti a un tunnel esistente
    export ANTHROPIC_FEDERATION_RULE_ID=fdrl_...
    export ANTHROPIC_ORGANIZATION_ID=00000000-0000-0000-0000-000000000000

    Se la tua regola di federazione è limitata a un workspace diverso da quello predefinito della tua organizzazione, imposta anche ANTHROPIC_WORKSPACE_ID=wrkspc_...; altrimenti il componente di setup usa il workspace predefinito. Un tunnel creato automaticamente viene creato in quel workspace.

    Imposta ANTHROPIC_IDENTITY_TOKEN su un JWT OIDC emesso dall'identity provider di questo host. Segui la guida WIF per il tuo provider per registrare l'issuer, impostare il subject della regola ed emettere il token; l'audience della regola deve corrispondere all'audience che richiedi al momento dell'emissione.

    Esegui il componente di setup:

    docker compose run --rm setup

    setup init è idempotente rispetto a data/: rieseguirlo riutilizza l'ID del tunnel e la CA già memorizzati lì e non crea mai un secondo tunnel. Una nuova CA viene generata e registrata solo quando data/ è vuota o TUNNEL_ID è cambiato; in quel caso si applica il limite di due certificati attivi, quindi revocane prima uno nella Console se entrambi gli slot sono occupati.

    Consulta Errori di autenticazione del componente di setup in caso di errore.

    Recupera il dominio del tuo tunnel ed esportalo per i passaggi successivi:

    export TUNNEL_DOMAIN=$(sudo cat data/tunnel-domain)
    echo "$TUNNEL_DOMAIN"
  4. Scrivi la configurazione del proxy

    tunnel_domain è obbligatorio: il proxy lo usa per rimuovere il suffisso di dominio dagli hostname in ingresso prima di cercare il sottodominio in routes. routes è una mappa piatta da sottodominio a URL upstream, non una lista.

    cat > config/mcp-proxy.yaml <<EOF
    listen_addr: ":8080"
    log_level: info
    shutdown_timeout: 30s
    tunnel_domain: ${TUNNEL_DOMAIN}
    tls:
      cert_file: /data/tls.crt
      key_file: /data/tls.key
    routes:
      echo: http://hello-mcp:9000
    EOF

    La route echo: punta al server MCP di esempio; sostituiscila con (o aggiungi) le tue route. Consulta il riferimento alla configurazione del proxy per tutti i campi disponibili.

  5. Avvia la distribuzione

    export TUNNEL_TOKEN=$(sudo cat data/tunnel-token)
    docker compose up -d

Il file compose legge TUNNEL_TOKEN dall'ambiente dell'host senza alcun valore predefinito, quindi l'export deve essere ripetuto in ogni nuova shell e dopo un riavvio.

Per una distribuzione multi-VM, copia la directory mcp-tunnel/ su ciascun host, imposta TUNNEL_TOKEN ed esegui docker compose up -d. Nel flusso programmatico TUNNEL_TOKEN è $(sudo cat data/tunnel-token); nel flusso manuale è il valore che hai copiato dalla Console. Lo stesso token del tunnel e gli stessi certificati funzionano su tutte le repliche.

Verifica la distribuzione

Verifica end-to-end chiamando un server MCP upstream dal lato di Anthropic: consulta Usare i server MCP in tunnel. Con il server MCP di esempio, l'URL instradato è https://echo.<your-tunnel-domain>/mcp. Se la verifica fallisce, consulta Risoluzione dei problemi.

Aggiornamenti

Esegui i comandi di questa sezione dall'interno della directory di distribuzione mcp-tunnel/.

Ruota il token del tunnel

Con l'accesso programmatico, incrementa --token-version nel comando del servizio setup, imposta gli identificatori di Workload Identity Federation, emetti un nuovo JWT OIDC e riesegui il componente di setup:

# Modifica docker-compose.yaml: incrementa l'intero nell'argomento
# --token-version del servizio setup (ad esempio, da --token-version=1 a
# --token-version=2). Il binario di setup rifiuta la rotazione se il valore
# non è cambiato.

# export TUNNEL_ID=tnl_...   # imposta solo se l'hai impostato durante l'installazione
export ANTHROPIC_FEDERATION_RULE_ID=fdrl_...
export ANTHROPIC_ORGANIZATION_ID=00000000-0000-0000-0000-000000000000
# export ANTHROPIC_WORKSPACE_ID=wrkspc_...   # se la regola ha ambito workspace
# Rigenera ANTHROPIC_IDENTITY_TOKEN secondo la guida del provider WIF per il tuo
# ambiente (sarà scaduto dall'installazione).
export ANTHROPIC_IDENTITY_TOKEN=...

docker compose run --rm setup

export TUNNEL_TOKEN=$(sudo cat data/tunnel-token)
docker compose up -d cloudflared

L'argomento --token-version viene modificato in docker-compose.yaml anziché passato sulla riga di comando, in modo che il nuovo valore persista per le esecuzioni future del componente di setup. Il componente di setup si autentica con Workload Identity Federation; non c'è alcun token API da revocare.

Senza accesso programmatico, fai clic su Rotate token nella pagina di dettaglio del tunnel nella Console, quindi aggiorna la variabile d'ambiente TUNNEL_TOKEN su ciascun host e riavvia cloudflared (docker compose up -d cloudflared).

Rinnovo dei certificati

Sei responsabile di monitorare la scadenza e di rinnovare il certificato server prima che scada.

Con l'accesso programmatico:

docker compose run --rm setup renew-cert --output=dir:/data

Gli argomenti CLI sostituiscono il command del servizio setup (gli argomenti init) ma ne mantengono l'entrypoint, quindi questo esegue /setup renew-cert --output=dir:/data.

Senza accesso programmatico, firma un nuovo certificato server con la tua CA esistente (la CA registrata nella Console non cambia) e sostituisci data/tls.crt. Imposta prima TUNNEL_DOMAIN se stai eseguendo questi comandi da una nuova shell.

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

In entrambi i flussi il proxy controlla periodicamente tls.cert_file e lo ricarica automaticamente, quindi non è necessario alcun riavvio.

Passaggi successivi

Collega un server MCP upstream a un Managed Agent o alla Messages API.

Indicazioni per l'hardening, rotazione delle credenziali e risposta alle violazioni.

Diagnostica problemi di connettività, TLS e instradamento.

Was this page helpful?