MCP Gateway

Un gateway de proxy inverso para gestionar y acceder a múltiples servidores MCP a través de un único punto de entrada, desplegable mediante Docker.

Documentación

MCP Gateway

Descripción

El gateway MCP es un servidor proxy inverso que reenvía solicitudes de clientes al servidor MCP o utiliza todos los servidores MCP bajo el gateway a través de un portal unificado.

Soporta dos protocolos de transporte (conmutables al inicio):

  • SSE (predeterminado, transporte MCP heredado)
  • HTTP Streamable (especificación MCP 2025-03-26)

Características

  • Desplegar múltiples servidores MCP
  • Conectarse al servidor MCP
  • Usar el gateway para llamar a servidores MCP
  • Obtener los flujos SSE de todos los servidores MCP
  • Obtener las herramientas de todos los servidores MCP
  • Endpoint agregado HTTP Streamable con gestión de sesiones mediante el encabezado Mcp-Session-Id
  • Agregación dinámica de capacidades (el gateway solo anuncia capacidades que al menos un MCP descendente soporta)
  • Autenticación de servidor de recursos MCP OAuth 2.1 con descubrimiento de metadatos de recursos protegidos

Instalación

  1. extraer el paquete de github
docker pull ghcr.io/lucky-aeon/mcp-gateway:latest
  1. construir la imagen docker usted mismo
docker build -t mcp-gateway .

Uso

ejecutar el contenedor docker de github

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

ejecutar el contenedor docker construido por usted mismo

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

Configuración

El gateway lee config.json del directorio de configuración (por defecto ./vm cuando está presente, de lo contrario .). Un ejemplo 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 clave:

CampoPredeterminadoDescripción
Bind[::]:8080Dirección de escucha del servidor.
GatewayProtocolallProtocolos expuestos del gateway: all, sse, o streamhttp. También se puede sobrescribir mediante la bandera --protocol.
Auth.EnabledtrueSi se debe aplicar autenticación. Establezca false para uso MCP local sin autenticación.
Auth.ApiKey123456Token de inicio de sesión de API de gestión de clave única heredado; no se usa como autenticación MCP.
Auth.Modesingle-keysingle-key o saas. El modo SaaS usa cuentas de MCP Gateway y inicio de sesión con contraseña.
Auth.AuthorizationServersvacíoURLs opcionales de emisores OAuth externos. Vacío en modo SaaS significa que el gateway se anuncia a sí mismo como servidor de autorización.
Auth.TokenIssuerprimer servidor de autorizaciónReclamación iss esperada para tokens de acceso OAuth MCP.
Auth.TokenJWKSURIdescubierto del emisorURL de JWKS utilizada para verificar tokens de acceso JWT.
Auth.TokenIntrospectionURLvacíoEndpoint de introspección RFC 7662 para tokens de acceso opacos. Si se establece, se usa introspección en lugar de JWKS.
Auth.TokenAudienceURL del recurso solicitadoReclamación aud esperada. Configure al indicador de recurso MCP utilizado por su servidor de autorización.
Auth.RequiredScopesvacíoÁmbitos requeridos para solicitudes MCP y anunciados en WWW-Authenticate.
Auth.ScopesSupportedvacíoÁmbitos anunciados en Metadatos de Recursos Protegidos.
SessionGCInterval10sIntervalo para la recolección de basura de sesiones proxy inactivas.
ProxySessionTimeout1mTiempo de espera para sesiones proxy inactivas antes de la recolección de basura.
McpServiceMgrConfig.McpServiceRetryCount3Reintentos máximos para un servicio MCP fallido antes de marcarlo como failed.

Selección del protocolo del gateway

Establezca GatewayProtocol en config.json:

{ "GatewayProtocol": "all" }

O pase la bandera CLI (tiene prioridad):

./mcp-gateway --protocol=streamhttp

Valores válidos: all (predeterminado), sse, o streamhttp.

Autenticación

Cuando Auth.Enabled es true, cada solicitud de protocolo MCP debe presentar un token Bearer:

Authorization: Bearer <access-token>

El gateway ya no trata api_key, sessionId, Mcp-Session-Id, o X-Session-Id como credenciales de autenticación. Mcp-Session-Id sigue siendo un identificador de sesión de transporte y debe enviarse junto con el token Bearer en solicitudes HTTP Streamable autenticadas.

Para el descubrimiento OAuth MCP, el gateway expone Metadatos de Recursos Protegidos OAuth:

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

Las solicitudes MCP no autorizadas devuelven 401 con un desafío WWW-Authenticate: Bearer ... resource_metadata="..." cuando el descubrimiento OAuth está disponible. Para desarrollo local sin autenticación, establezca Auth.Enabled a false.

En Auth.Mode = "saas" sin Auth.AuthorizationServers externo, el gateway usa su propio sistema de cuentas para el inicio de sesión MCP. El descubrimiento anuncia el origen del gateway como servidor de autorización y expone:

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

/oauth/token acepta grant_type=password codificado en formulario con username/password y devuelve el mismo JWT del gateway utilizado por /api/v1/auth/login. Configure Auth.AuthorizationServers solo cuando desee Keycloak, Auth0 u otro proveedor OAuth externo.

Los clientes MCP basados en navegador también pueden usar el endpoint de autorización anunciado:

GET /oauth/authorize

El endpoint de autorización integrado muestra un formulario de inicio de sesión de cuenta del Gateway y completa el flujo de código de autorización OAuth, incluido PKCE. /oauth/register implementa registro dinámico mínimo de clientes para clientes como MCP Inspector.

API

Desplegar

soporta: uvx, npx. o 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)

Disponible cuando GatewayProtocol es all (predeterminado) o sse.

GET SSE

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

POST Mensaje

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)

Disponible cuando GatewayProtocol es all (predeterminado) o sse.

La diferencia entre el gateway y la conexión directa a MCP es que solo necesita interactuar con el gateway, y el gateway reenviará automáticamente las solicitudes al servidor MCP correspondiente. Al llamar, debe agregar el contenido mcpServerName antes del método, para identificar de qué servidor MCP proviene la solicitud.

GET SSE

GET /sse HTTP/1.1
Host: localhost:8080

Aquí el sse es el flujo SSE de todos los servidores MCP bajo todo el gateway.

Cuando el cliente se suscribe al sse, el gateway crea una conexión SSE para cada servidor MCP y combina todos los flujos SSE de los servidores MCP.

En todos los resultados de tools/call en la respuesta, se agrega el contenido mcpServerName antes del método, para identificar de qué servidor MCP proviene el resultado.

POST Mensaje

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
}

Obtener todas las herramientas bajo el 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 HTTP Streamable)

Disponible cuando GatewayProtocol es all (predeterminado) o streamhttp.

Implementa el transporte HTTP Streamable MCP definido en la especificación 2025-03-26. El gateway expone un único endpoint agregado /stream que acepta POST, GET y DELETE. Los identificadores de sesión se transportan en el encabezado HTTP Mcp-Session-Id.

1. Establecer una sesión (initialize)

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"}
    }
}

Respuesta:

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>."
    }
}

Conserve el Mcp-Session-Id devuelto y envíelo en cada solicitud posterior.

2. Completar el protocolo de enlace (notification)

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"}

Respuesta: 202 Accepted (cuerpo vacío).

3. Llamar herramientas o 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"}
    }
}

Los nombres de herramientas agregados siguen el patrón <serverName>_<toolName>, misma regla que en el modo gateway SSE.

La respuesta llega de forma síncrona en el cuerpo de la respuesta HTTP:

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

Las notificaciones (mensajes JSON-RPC sin id) se responden con 202 Accepted y se reenvían de forma asíncrona.

4. Suscribirse a eventos iniciados por el 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

El gateway mantiene la conexión abierta y emite tramas event: message para solicitudes y notificaciones JSON-RPC de servidor → cliente (por ejemplo, actualizaciones de progreso, mensajes de registro). Las respuestas JSON-RPC nunca se envían aquí — se devuelven en la respuesta HTTP de la solicitud POST /stream original.

Las líneas que comienzan con : son comentarios de mantenimiento de conexión SSE y pueden ignorarse.

5. Cerrar la sesión

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

Respuesta: 200 OK.

Paso directo de servidor único

En modo HTTP Streamable también puede acceder directamente a un servidor MCP individual:

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

El gateway reenvía la solicitud al endpoint message del MCP objetivo. La gestión de sesiones en este modo es responsabilidad del servidor descendente.

Conexión con MCP Inspector

  1. En Inspector seleccione Tipo de Transporte: Streamable HTTP.
  2. URL: http://localhost:8080/stream.
  3. Complete el flujo OAuth en su servidor de autorización y proporcione Authorization: Bearer <access-token>.
  4. Haga clic en Conectar. El Inspector maneja el intercambio Mcp-Session-Id automáticamente.