---
title: Implantar túneis MCP com Docker Compose
url: https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/deploy-compose
description: Instale a pilha de túneis MCP em uma VM usando Docker Compose.
---

<Note>
  Os "MCP tunnels" (túneis MCP) estão em prévia de pesquisa. [Solicite acesso](https://claude.com/form/mcp-tunnels) para experimentá-los.
</Note>

Este guia implanta a [pilha de túneis](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/concepts#components) como contêineres reforçados em um único host. A mesma configuração pode ser replicada em vários hosts para disponibilidade.

## Antes de começar

Você precisa de:

* **Um túnel.** Com acesso programático, o [componente de setup](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/concepts#components) cria um para você quando você não fornece um ID de túnel; para anexar a um túnel existente, [crie-o no Console](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/console#create-a-tunnel) e anote o ID do túnel (`tnl_...`). O provisionamento manual sempre começa a partir de um túnel criado no Console.

* **Uma forma de o host se autenticar na API de Tunnels.**

  * **Acesso programático (recomendado).** Ative **Set up programmatic access** ao criar o túnel para que o componente de setup possa se autenticar por meio de "Workload Identity Federation" (federação de identidade de carga de trabalho). Se você estiver deixando o componente de setup criar o túnel, crie a regra de federação diretamente na página [Workload identity](https://platform.claude.com/settings/workload-identity-federation) (`platform.claude.com/settings/workload-identity-federation`) no Claude Console. Anote o ID da regra de federação (`fdrl_...`) e o ID da sua organização.
  * **Manual.** Pule o acesso programático. Você vai [obter o token do túnel no Console](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/console#get-the-connection-details), gerar uma CA e um certificado de servidor por conta própria e [registrar a CA no Console](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/console#add-a-ca-certificate).

* **Um host com Docker e Docker Compose** instalados. O fluxo manual também requer `openssl` (1.1.1 ou posterior).

* **Conectividade de rede de saída** do host para `api.anthropic.com` (443 TCP) e para a [borda do túnel](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/concepts#components) (7844 TCP e UDP). Consulte os [requisitos de rede](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/overview#network-requirements) completos.

* **Um ou mais servidores MCP** em execução e acessíveis a partir do host nos endereços que você configurará em `routes`. Se você ainda não tiver um, [use o servidor de exemplo](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/deploy-compose#optional-use-a-sample-mcp-server).

## Opcional: Use um servidor MCP de exemplo

Se você não tiver um servidor MCP disponível para testes, use este mínimo:

```bash
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
```

As etapas de instalação a seguir fazem `cd` para `mcp-tunnel/` e indicam onde adicionar o serviço e a rota correspondentes.

## Instalar

Este guia fornece uma abordagem de referência usando Docker Compose. Você é responsável por adaptá-la para atender aos requisitos de segurança da sua organização.

<Tabs>
  <Tab title="Com acesso programático">
    Este caminho requer que o host tenha um provedor de identidade OIDC (como um servidor de metadados de VM em nuvem ou SPIFFE). Se não tiver, use a aba **Sem acesso programático**.

    O componente de setup usa Workload Identity Federation para buscar o token do túnel, gerar uma CA e um certificado de servidor e registrar a CA na Anthropic.

    <Steps>
      <Step title="Prepare o diretório de implantação">
        ```bash
        mkdir -p mcp-tunnel/{config,data}
        cd mcp-tunnel
        sudo chown 65532:65532 data
        ```

        Os contêineres são executados como o UID não-root `65532` e precisam de acesso de escrita a `data/`.
      </Step>

      <Step title="Escreva o docker-compose.yaml">
        O arquivo compose fixa as imagens por digest SHA-256, executa cada contêiner como não-root com um sistema de arquivos somente leitura, remove todas as capabilities do Linux e desativa a escalada de privilégios.

        ```bash
        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
            # Compartilha o netns do proxy para que localhost:8080 o alcance.
            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 você estiver usando o [servidor MCP de exemplo](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/deploy-compose#optional-use-a-sample-mcp-server), adicione-o como um serviço:

        ```bash
        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
        ```
      </Step>

      <Step title="Provisione o túnel">
        Defina os identificadores. Deixe `TUNNEL_ID` sem definir para que o componente de setup crie um túnel; defina-o para anexar a um túnel existente do [Console](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/console#create-a-tunnel):

        ```bash
        # export TUNNEL_ID=tnl_...   # defina para anexar a um túnel existente
        export ANTHROPIC_FEDERATION_RULE_ID=fdrl_...
        export ANTHROPIC_ORGANIZATION_ID=00000000-0000-0000-0000-000000000000
        ```

        Se sua regra de federação tiver escopo em um workspace diferente do padrão da sua organização, defina também `ANTHROPIC_WORKSPACE_ID=wrkspc_...`; caso contrário, o componente de setup usa o workspace padrão. Um túnel criado automaticamente é criado nesse workspace.

        Defina `ANTHROPIC_IDENTITY_TOKEN` como um JWT OIDC do provedor de identidade deste host. Siga o [guia de WIF para o seu provedor](https://platform.claude.com/docs/pt-BR/manage-claude/workload-identity-federation#identity-providers) para registrar o emissor, definir o subject da regra e emitir o token; a audience da regra deve corresponder à audience que você solicita ao emitir.

        Execute o componente de setup:

        ```bash
        docker compose run --rm setup
        ```

        `setup init` é idempotente sobre `data/`: executá-lo novamente reutiliza o ID do túnel e a CA já armazenados ali e nunca cria um segundo túnel. Uma nova CA é gerada e registrada somente quando `data/` está vazio ou `TUNNEL_ID` mudou; nesse caso, aplica-se o limite de dois certificados ativos, então revogue um no Console primeiro se ambos os slots estiverem ocupados.

        Consulte [Falhas de autenticação do componente de setup](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/troubleshooting#setup-component-authentication-failures) se ocorrer erro.

        Recupere o domínio do seu túnel e exporte-o para as etapas posteriores:

        ```bash
        export TUNNEL_DOMAIN=$(sudo cat data/tunnel-domain)
        echo "$TUNNEL_DOMAIN"
        ```

        <Note>
          Os tokens de Workload Identity Federation têm vida curta (1 hora por padrão) e expiram automaticamente; não há nada a revogar após a conclusão do setup.
        </Note>
      </Step>

      <Step title="Escreva a configuração do proxy">
        `tunnel_domain` é **obrigatório**: o [proxy](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/concepts#components) o usa para remover o sufixo de domínio dos hostnames recebidos antes de procurar o subdomínio em `routes`. `routes` é um mapa plano de subdomínio para URL upstream, não uma lista.

        ```bash
        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
        ```

        A rota `echo:` aponta para o [servidor MCP de exemplo](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/deploy-compose#optional-use-a-sample-mcp-server); substitua-a por (ou adicione) suas próprias rotas. Consulte a referência de [configuração do proxy](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/reference#proxy-configuration) para todos os campos disponíveis.
      </Step>

      <Step title="Inicie a implantação">
        ```bash
        export TUNNEL_TOKEN=$(sudo cat data/tunnel-token)
        docker compose up -d
        ```
      </Step>
    </Steps>
  </Tab>

  <Tab title="Sem acesso programático">
    Use este fluxo se você não ativou **Set up programmatic access**, ou para desenvolvimento e testes locais. Não há serviço `setup`.

    <Steps>
      <Step title="Obtenha o token e o domínio do túnel no Console">
        Na página de detalhes do túnel, copie o **Domain** (ele tem o formato `abcd1234.tunnel.anthropic.com`), depois clique no ícone de olho ao lado de **Token** para buscar o token do túnel e use o ícone de copiar para copiá-lo.

        Defina ambos como variáveis de shell para o restante do guia:

        ```bash
        export TUNNEL_DOMAIN=YOUR_TUNNEL_DOMAIN_HERE
        export TUNNEL_TOKEN='eyJ...'
        ```
      </Step>

      <Step title="Crie a estrutura e gere os certificados">
        ```bash
        mkdir -p mcp-tunnel/{data,config}
        cd mcp-tunnel
        ```

        O proxy escuta em `:8080` via WebSocket simples; o handshake de [TLS interno](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/concepts#components) acontece **dentro** desse fluxo WebSocket usando esses certificados. A Anthropic verifica o handshake interno em relação à CA que você registra no Console. O Subject Alternative Name (SAN) do certificado de servidor deve incluir `*.<tunnel-domain>` conforme os [requisitos de certificado](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/reference#certificate-requirements).

        ```bash
        # CA autoassinada. Extensões explícitas para satisfazer os requisitos
        # de certificado independentemente dos padrões do openssl.cnf da distro.
        openssl req -x509 -newkey rsa:2048 -nodes \
          -keyout data/ca.key -out data/ca.crt \
          -days 3650 -subj "/CN=mcp-tunnel-ca" \
          -addext "basicConstraints=critical,CA:TRUE" \
          -addext "keyUsage=critical,keyCertSign,cRLSign" \
          -addext "subjectKeyIdentifier=hash"

        # Arquivo de extensões para o certificado do servidor. Usar -extfile (em vez de
        # -copy_extensions, que é exclusivo do OpenSSL 3.0+) mantém isso funcionando no
        # OpenSSL 1.1.x.
        cat > data/tls.ext <<EOF
        subjectAltName = DNS:${TUNNEL_DOMAIN},DNS:*.${TUNNEL_DOMAIN}
        authorityKeyIdentifier = keyid,issuer
        extendedKeyUsage = serverAuth
        EOF

        # Certificado do servidor assinado pela CA
        openssl req -newkey rsa:2048 -nodes \
          -keyout 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

        # Permite que o contêiner proxy não-root (UID 65532) leia a chave a partir do
        # bind mount. Sem o bit de leitura global, o contêiner não consegue abrir
        # um arquivo pertencente ao host.
        chmod 644 data/tls.key
        ```
      </Step>

      <Step title="Registre o certificado da CA no Console">
        Na página de detalhes do túnel, role até a seção **Certificates** e clique em **Add certificate**. Faça upload de `data/ca.crt` diretamente com **Choose file** (o modal aceita `.pem`, `.crt` e `.cer`), ou cole seu conteúdo:

        ```bash
        cat data/ca.crt
        ```

        O selo **Needs certificate** ao lado do nome do túnel desaparece assim que um certificado é registrado. Consulte [Adicionar um certificado de CA](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/console#add-a-ca-certificate).
      </Step>

      <Step title="Escreva a configuração do proxy">
        `tunnel_domain` é **obrigatório**: o proxy o usa para remover o sufixo de domínio dos hostnames recebidos antes de procurar o subdomínio em `routes`. `routes` é um mapa plano de subdomínio para URL upstream, não uma lista.

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

        A rota `echo:` aponta para o [servidor MCP de exemplo](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/deploy-compose#optional-use-a-sample-mcp-server); substitua-a por (ou adicione) suas próprias rotas. Consulte a referência de [configuração do proxy](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/reference#proxy-configuration) para todos os campos disponíveis.
      </Step>

      <Step title="Escreva o docker-compose.yaml">
        A configuração `network_mode: "service:mcp-proxy"` coloca o [cloudflared](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/concepts#components) no namespace de rede do proxy para que `localhost:8080` dentro do contêiner do cloudflared alcance o proxy. A flag `--url http://localhost:8080` fornece ao cloudflared seu destino de encaminhamento; sem essa flag, o cloudflared não tem rota para as requisições recebidas e retorna um 503.

        ```bash
        cat > docker-compose.yaml <<'EOF'
        services:
          cloudflared:
            image: cloudflare/cloudflared@sha256:6b599ca3e974349ead3286d178da61d291961182ec3fe9c505e1dd02c8ac31b0
            # --url é obrigatório: nenhuma regra de ingress é enviada no fluxo manual,
            # então sem ele o cloudflared retorna 503 para toda requisição.
            command: tunnel --no-autoupdate run --url http://localhost:8080
            environment:
              - TUNNEL_TOKEN
            # Compartilhe o netns do proxy para que localhost:8080 o alcance.
            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 você estiver usando o [servidor MCP de exemplo](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/deploy-compose#optional-use-a-sample-mcp-server), adicione-o como um serviço:

        ```bash
        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
        ```
      </Step>

      <Step title="Inicie a implantação">
        ```bash
        docker compose up -d
        ```
      </Step>
    </Steps>
  </Tab>
</Tabs>

O arquivo compose lê `TUNNEL_TOKEN` do ambiente do host sem valor padrão, portanto o export deve ser repetido em cada novo shell e após uma reinicialização.

Para uma implantação com várias VMs, copie o diretório `mcp-tunnel/` para cada host, defina `TUNNEL_TOKEN` e execute `docker compose up -d`. No fluxo programático, `TUNNEL_TOKEN` é `$(sudo cat data/tunnel-token)`; no fluxo manual, é o valor que você copiou do Console. O mesmo token de túnel e os mesmos certificados funcionam em todas as réplicas.

## Verifique a implantação

Verifique de ponta a ponta chamando um [servidor MCP upstream](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/concepts#components) a partir do lado da Anthropic: consulte [Use os servidores MCP tunelados](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/overview#use-the-tunneled-mcp-servers). Com o [servidor MCP de exemplo](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/deploy-compose#optional-use-a-sample-mcp-server), a URL roteada é `https://echo.<your-tunnel-domain>/mcp`. Se a verificação falhar, consulte [Solução de problemas](https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/troubleshooting).

## Atualizações

Execute os comandos desta seção de dentro do diretório de implantação `mcp-tunnel/`.

### Rotacione o token do túnel

Com acesso programático, incremente `--token-version` no comando do serviço `setup`, defina os identificadores de Workload Identity Federation, emita um novo JWT OIDC e execute novamente o componente de setup:

```bash
# Edite docker-compose.yaml: incremente o inteiro no argumento
# --token-version do serviço setup (por exemplo, de --token-version=1 para
# --token-version=2). O binário de setup se recusa a rotacionar quando o valor
# não mudou.

# export TUNNEL_ID=tnl_...   # defina apenas se você o definiu durante a instalação
export ANTHROPIC_FEDERATION_RULE_ID=fdrl_...
export ANTHROPIC_ORGANIZATION_ID=00000000-0000-0000-0000-000000000000
# export ANTHROPIC_WORKSPACE_ID=wrkspc_...   # se sua regra tiver escopo de workspace
# Gere novamente o ANTHROPIC_IDENTITY_TOKEN conforme o guia do provedor WIF para seu
# ambiente (ele terá expirado desde a instalação).
export ANTHROPIC_IDENTITY_TOKEN=...

docker compose run --rm setup

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

O argumento `--token-version` é editado em `docker-compose.yaml` em vez de ser passado na linha de comando, para que o novo valor persista em execuções futuras do componente de setup. O componente de setup se autentica com Workload Identity Federation; não há token de API a revogar.

Sem acesso programático, clique em **Rotate token** na página de detalhes do túnel no Console, depois atualize a variável de ambiente `TUNNEL_TOKEN` em cada host e reinicie o cloudflared (`docker compose up -d cloudflared`).

<Warning>
  Clicar em **Rotate token** invalida o token atual imediatamente. Entre esse momento e a atualização de `TUNNEL_TOKEN` em cada host e a reinicialização do cloudflared, qualquer host cujo cloudflared reinicie (falha, reinicialização do host) não conseguirá se reconectar. Atualize cada host prontamente após rotacionar.
</Warning>

### Renovação de certificado

Você é responsável por monitorar a expiração e renovar o certificado de servidor antes que ele expire.

Com acesso programático:

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

Os argumentos da CLI substituem o `command` do serviço `setup` (os argumentos de `init`), mas mantêm seu `entrypoint`, então isso executa `/setup renew-cert --output=dir:/data`.

<Tip>
  Passe `--renew-before=720h` para tornar o comando uma operação nula quando restarem mais de 30 dias de validade. Isso torna seguro executá-lo em um agendamento fixo.
</Tip>

Sem acesso programático, assine um novo certificado de servidor com sua CA existente (a CA registrada no Console não muda) e substitua `data/tls.crt`. Defina `TUNNEL_DOMAIN` primeiro se você estiver executando isso a partir de um novo shell.

```bash
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
```

Em qualquer um dos fluxos, o proxy consulta periodicamente `tls.cert_file` e o recarrega automaticamente, portanto nenhuma reinicialização é necessária.

## Próximos passos

<CardGroup cols={2}>
  <Card title="Use os servidores MCP tunelados" icon="link" href="https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/overview#use-the-tunneled-mcp-servers">
    Anexe um servidor MCP upstream a um Managed Agent ou à Messages API.
  </Card>

  <Card title="Segurança" icon="lock" href="https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/security">
    Orientações de reforço, rotação de credenciais e resposta a violações.
  </Card>

  <Card title="Solução de problemas" icon="wrench" href="https://platform.claude.com/docs/pt-BR/agents-and-tools/mcp-tunnels/troubleshooting">
    Diagnostique problemas de conectividade, TLS e roteamento.
  </Card>
</CardGroup>
