MCP Gateway

Um gateway de proxy reverso para gerenciar e acessar múltiplos servidores MCP através de um único ponto de entrada, implantável via Docker.

Documentação

MCP Gateway

Descrição

O gateway MCP é um servidor proxy reverso que encaminha solicitações de clientes para o servidor MCP ou usa todos os servidores MCP sob o gateway através de um portal unificado.

Suporta dois protocolos de transporte (alternáveis na inicialização):

  • SSE (padrão, transporte MCP legado)
  • Streamable HTTP (especificação MCP 2025-03-26)

Recursos

  • Implantar vários servidores MCP
  • Conectar ao servidor MCP
  • Usar o gateway para chamar servidores MCP
  • Obter todos os fluxos SSE dos servidores MCP
  • Obter todas as ferramentas dos servidores MCP
  • Endpoint agregado Streamable HTTP com gerenciamento de sessão via cabeçalho Mcp-Session-Id
  • Agregação dinâmica de capacidades (o gateway anuncia apenas capacidades que pelo menos um MCP downstream suporta)
  • Autenticação de servidor de recursos MCP OAuth 2.1 com descoberta de metadados de recursos protegidos

Instalação

  1. baixar pacote do github
docker pull ghcr.io/lucky-aeon/mcp-gateway:latest
  1. construir imagem docker manualmente
docker build -t mcp-gateway .

Uso

executar contêiner docker do github

docker run -d --name mcp-gateway -p 8080:8080 ghcr.io/lucky-aeon/mcp-gateway

executar contêiner docker de construção manual

docker run -d --name mcp-gateway -p 8080:8080 mcp-gateway

Configuração

O gateway lê config.json do diretório de configuração (padrão é ./vm quando presente, caso contrário .). Um exemplo mínimo:

{
    "LogLevel": 0,
    "Bind": "[::]:8080",
    "Auth": {
        "Enabled": true,
        "AuthorizationServers": ["https://auth.example.com"],
        "TokenIssuer": "https://auth.example.com",
        "TokenJWKSURI": "https://auth.example.com/.well-known/jwks.json",
        "TokenAudience": "http://localhost:8080/stream",
        "RequiredScopes": ["mcp:read"],
        "ScopesSupported": ["mcp:read"]
    },
    "GatewayProtocol": "all",
    "McpServiceMgrConfig": {
        "McpServiceRetryCount": 3
    }
}

Campos principais:

CampoPadrãoDescrição
Bind[::]:8080Endereço de escuta do servidor.
GatewayProtocolallProtocolos expostos do gateway: all, sse ou streamhttp. Também pode ser substituído via flag --protocol.
Auth.EnabledtrueSe deve impor autenticação. Defina false para uso MCP local sem autenticação.
Auth.ApiKey123456Token de login legado de chave única da API de gerenciamento; não usado como autenticação MCP.
Auth.Modesingle-keysingle-key ou saas. O modo SaaS usa contas MCP Gateway e login por senha.
Auth.AuthorizationServersvazioURLs opcionais de emissor OAuth externo. Vazio no modo SaaS significa que o gateway se anuncia como servidor de autorização.
Auth.TokenIssuerprimeiro servidor de autorizaçãoReivindicação iss esperada para tokens de acesso OAuth MCP.
Auth.TokenJWKSURIdescoberto do emissorURL JWKS usada para verificar tokens de acesso JWT.
Auth.TokenIntrospectionURLvazioEndpoint de introspecção RFC 7662 para tokens de acesso opacos. Se definido, a introspecção é usada em vez de JWKS.
Auth.TokenAudienceURL do recurso solicitadoReivindicação aud esperada. Configure para o indicador de recurso MCP usado pelo seu servidor de autorização.
Auth.RequiredScopesvazioEscopos necessários para solicitações MCP e anunciados em WWW-Authenticate.
Auth.ScopesSupportedvazioEscopos anunciados nos Metadados de Recursos Protegidos.
SessionGCInterval10sIntervalo para coleta de lixo de sessões de proxy ociosas.
ProxySessionTimeout1mTempo limite para sessões de proxy ociosas antes da coleta de lixo.
McpServiceMgrConfig.McpServiceRetryCount3Máximo de tentativas para um serviço MCP com falha antes de marcá-lo como failed.

Selecionando o protocolo do gateway

Defina GatewayProtocol em config.json:

{ "GatewayProtocol": "all" }

Ou passe a flag CLI (tem precedência):

./mcp-gateway --protocol=streamhttp

Valores válidos: all (padrão), sse ou streamhttp.

Autenticação

Quando Auth.Enabled é true, toda solicitação de protocolo MCP deve apresentar um token Bearer:

Authorization: Bearer <access-token>

O gateway não trata mais api_key, sessionId, Mcp-Session-Id ou X-Session-Id como credenciais de autenticação. Mcp-Session-Id permanece um identificador de sessão de transporte e deve ser enviado junto com o token Bearer em solicitações Streamable HTTP autenticadas.

Para descoberta OAuth MCP, o gateway expõe Metadados de Recursos Protegidos OAuth:

GET /.well-known/oauth-protected-resource
GET /.well-known/oauth-protected-resource/stream

Solicitações MCP não autorizadas retornam 401 com um desafio WWW-Authenticate: Bearer ... resource_metadata="..." quando a descoberta OAuth está disponível. Para desenvolvimento local sem autenticação, defina Auth.Enabled como false.

Em Auth.Mode = "saas" sem Auth.AuthorizationServers externo, o gateway usa seu próprio sistema de contas para login MCP. A descoberta anuncia a origem do gateway como servidor de autorização e expõe:

GET /.well-known/oauth-authorization-server
POST /oauth/token
POST /oauth/register

/oauth/token aceita grant_type=password codificado em formulário com username/password e retorna o mesmo JWT do gateway usado por /api/v1/auth/login. Configure Auth.AuthorizationServers apenas quando quiser Keycloak, Auth0 ou outro provedor OAuth externo.

Clientes MCP baseados em navegador também podem usar o endpoint de autorização anunciado:

GET /oauth/authorize

O endpoint de autorização integrado renderiza um formulário de login de conta do Gateway e completa o fluxo de código de autorização OAuth, incluindo PKCE. /oauth/register implementa registro dinâmico mínimo de cliente para clientes como MCP Inspector.

API

Implantar

suporte: uvx, npx. ou url sse

POST /deploy HTTP/1.1
Host: localhost:8080
Content-Type: application/json

{
    "mcpServers": {
        "time": {
            "url": "http://mcp-server:8080",  // url 和 command 二选一
            "command": "uvx",  // url 和 command 二选一
            "args": ["mcp-server-time", "--local-timezone=America/New_York"],  // 可选,command 的参数
            "env": {  // 可选,环境变量
                "KEY1": "VALUE1",
                "KEY2": "VALUE2"
            }
        }
    }
}

Usar MCP (Modo SSE)

Disponível quando GatewayProtocol é all (padrão) ou sse.

GET SSE

GET /{mcp-server-name}/sse HTTP/1.1
Host: localhost:8080

POST Message

POST /{mcp-server-name}/message HTTP/1.1
Host: localhost:8080
Content-Type: application/json

{
    "method": "tools/call",
    "params": {
        "name": "get_current_time",
        "arguments": {
            "timezone": "Asia/Seoul"
        }
    },
    "jsonrpc": "2.0",
    "id": 2
}

Usar Gateway (Modo SSE)

Disponível quando GatewayProtocol é all (padrão) ou sse.

A diferença entre o gateway e a conexão direta com o MCP é que você só precisa interagir com o gateway, que encaminhará automaticamente a solicitação para o servidor MCP correspondente. Ao chamar, você precisa adicionar o conteúdo mcpServerName antes do método, identificando de qual servidor MCP a solicitação vem.

GET SSE

GET /sse HTTP/1.1
Host: localhost:8080

Aqui, o SSE é o fluxo SSE de todos os servidores MCP sob o gateway.

Quando o cliente assina o SSE, o gateway cria uma conexão SSE para cada servidor MCP e combina todos os fluxos SSE dos servidores MCP em um só.

Em todos os resultados de tools/call na resposta, o conteúdo mcpServerName é adicionado antes do método, identificando de qual servidor MCP o resultado vem.

POST Message

POST /message HTTP/1.1
Host: localhost:8080
Content-Type: application/json

{
    "method": "tools/call",
    "params": {
        "name": "{mcp-server-name}-get_current_time",
        "arguments": {
            "timezone": "Asia/Seoul"
        }
    },
    "jsonrpc": "2.0",
    "id": 2
}

Obter todas as ferramentas sob o gateway

POST /message HTTP/1.1
Host: localhost:8080
Content-Type: application/json

{
    "method": "tools/list",
    "jsonrpc": "2.0",
    "id": 1
}

# SSE 响应 message event

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "{mcpServerName}-get_current_time",
        "description": "Get current time in a specific timezones",
        "inputSchema": {
          "type": "object",
          "properties": {
            "timezone": {
              "type": "string",
              "description": "IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'America/New_York' as local timezone if no timezone provided by the user."
            }
          },
          "required": [
            "timezone"
          ]
        }
      },
      {
        "name": "{mcpServerName}-convert_time",
        "description": "Convert time between timezones",
        "inputSchema": {
          "type": "object",
          "properties": {
            "source_timezone": {
              "type": "string",
              "description": "Source IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'America/New_York' as local timezone if no source timezone provided by the user."
            },
            "time": {
              "type": "string",
              "description": "Time to convert in 24-hour format (HH:MM)"
            },
            "target_timezone": {
              "type": "string",
              "description": "Target IANA timezone name (e.g., 'Asia/Tokyo', 'America/San_Francisco'). Use 'America/New_York' as local timezone if no target timezone provided by the user."
            }
          },
          "required": [
            "source_timezone",
            "time",
            "target_timezone"
          ]
        }
      }
    ]
  }
}

Usar Gateway (Modo Streamable HTTP)

Disponível quando GatewayProtocol é all (padrão) ou streamhttp.

Implementa o transporte Streamable HTTP MCP definido na especificação 2025-03-26. O gateway expõe um único endpoint agregado /stream que aceita POST, GET e DELETE. Os identificadores de sessão são transportados no cabeçalho HTTP Mcp-Session-Id.

1. Estabelecer uma sessão (inicializar)

POST /stream HTTP/1.1
Host: localhost:8080
Authorization: Bearer <access-token>
Accept: application/json, text/event-stream
Content-Type: application/json

{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
        "protocolVersion": "2025-03-26",
        "capabilities": {},
        "clientInfo": {"name": "my-client", "version": "1.0.0"}
    }
}

Resposta:

HTTP/1.1 200 OK
Content-Type: application/json
Mcp-Session-Id: 7782f2f9-563c-4379-b961-df06e49e54c0

{
    "jsonrpc": "2.0",
    "id": 1,
    "result": {
        "protocolVersion": "2025-03-26",
        "serverInfo": {"name": "mcp-gateway", "version": "1.0.0"},
        "capabilities": { /* OR-merged from all downstream MCP servers */ },
        "instructions": "MCP Gateway aggregates multiple MCP servers. Tools are namespaced as <serverName>_<toolName>."
    }
}

Mantenha o Mcp-Session-Id retornado e envie-o em cada solicitação subsequente.

2. Completar o handshake (notificação)

POST /stream HTTP/1.1
Host: localhost:8080
Authorization: Bearer <access-token>
Content-Type: application/json
Mcp-Session-Id: 7782f2f9-563c-4379-b961-df06e49e54c0

{"jsonrpc": "2.0", "method": "notifications/initialized"}

Resposta: 202 Accepted (corpo vazio).

3. Chamar ferramentas ou listar recursos

POST /stream HTTP/1.1
Host: localhost:8080
Authorization: Bearer <access-token>
Accept: application/json, text/event-stream
Content-Type: application/json
Mcp-Session-Id: 7782f2f9-563c-4379-b961-df06e49e54c0

{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
        "name": "{mcp-server-name}_get_current_time",
        "arguments": {"timezone": "Asia/Seoul"}
    }
}

Os nomes de ferramentas agregados seguem o padrão <serverName>_<toolName>, mesma regra do modo gateway SSE.

A resposta chega de forma síncrona no corpo da resposta HTTP:

{"jsonrpc": "2.0", "id": 2, "result": { /* ... */ }}

Notificações (mensagens JSON-RPC sem id) são respondidas com 202 Accepted e encaminhadas de forma assíncrona.

4. Assinar eventos iniciados pelo servidor (opcional)

GET /stream HTTP/1.1
Host: localhost:8080
Authorization: Bearer <access-token>
Accept: text/event-stream
Mcp-Session-Id: 7782f2f9-563c-4379-b961-df06e49e54c0

O gateway mantém a conexão aberta e emite quadros event: message para solicitações e notificações JSON-RPC do servidor para o cliente (por exemplo, atualizações de progresso, mensagens de log). Respostas JSON-RPC nunca são enviadas aqui — elas são retornadas na resposta HTTP da solicitação POST /stream original.

Linhas que começam com : são comentários de keepalive SSE e podem ser ignoradas.

5. Fechar a sessão

DELETE /stream HTTP/1.1
Host: localhost:8080
Authorization: Bearer <access-token>
Mcp-Session-Id: 7782f2f9-563c-4379-b961-df06e49e54c0

Resposta: 200 OK.

Passagem direta de servidor único

No modo Streamable HTTP, você também pode acessar um servidor MCP individual diretamente:

POST /{mcp-server-name} HTTP/1.1
GET  /{mcp-server-name} HTTP/1.1

O gateway encaminha a solicitação para o endpoint message do MCP de destino. O gerenciamento de sessão neste modo é responsabilidade do servidor downstream.

Conectando com o MCP Inspector

  1. No Inspector, selecione Tipo de Transporte: Streamable HTTP.
  2. URL: http://localhost:8080/stream.
  3. Complete o fluxo OAuth no seu servidor de autorização e forneça Authorization: Bearer <access-token>.
  4. Clique em Conectar. O Inspector lida com a troca Mcp-Session-Id automaticamente.