MCP Gateway
MCP Gateway — Fase 1 (KLIP, solo lectura)
Un único servicio seguro que permite al personal autorizado de Energi-Up consultar KLIP en lenguaje natural a través de Claude, sin abrir la aplicación. Estrictamente de solo lectura.
Objetivo de despliegue (PRD Q3, ahora cerrado): <gateway-hostname> -> <gateway-public-ip> (ECS-MCP, ap-southeast-5). Tenga en cuenta que el nombre de host es mcp-gw, no mcp.example.com que suponían los documentos v0.9; el PRD/TSD debe actualizarse para reflejarlo.
Documentos: PRD v0.9 · TSD v0.9 · Guía de implementación · Revisión de diseño · Runbook de despliegue
1. Versiones fijadas (T-1)
La revisión de la especificación MCP y la versión del SDK se fijan aquí. Cuando la documentación del SDK discrepe de los documentos de diseño, el SDK manda — define la API exacta.
Componente | Versión fijada | Notas |
| 1.30.0 (exacta, sin caret) | Publicado el 27 de julio de 2026. Proporciona el AS de OAuth, el transporte HTTP Streamable y |
Revisión del protocolo MCP | 2025-11-25 implementada; formato de red compatible con clientes | La revisión |
Node.js | 22 LTS ( | |
TypeScript | 5.9.3, | |
express | 5.2.1 | Elevada desde la 4.x del TSD: el SDK depende de |
zod | 4.4.3 | Elevada desde la 3.x del TSD: las definiciones de tipos del SDK apuntan a zod 4. Tenga en cuenta que |
jose | 6.2.9 | Tokens de gateway RS256. |
| 2.1.0 | Sustituye a |
axios | 1.19.0 | Cliente KLIP, detrás de la protección de métodos. |
pg | 8.23.0 | |
PostgreSQL | 16 (contenedor) | |
nginx | mainline de nginx.org | Configuración en |
Related MCP server: Snowflake MCP Server
2. Estructura
src/
core/ config, logger, db, audit, cache, rateLimit, semaphore, migrate, errors
adapters/klip/ routes(APPENDIX A) · fields(APPENDIX A) · client(guard) · session · paginate · normalize
tools/klip/ 9 tool definitions + shared parameter plumbing
mcp/ server, envelope, runner
auth/ keys, hub(OIDC RP), users, clients, tokens, provider, loginPage
http/ app, consent(Hub + break-glass), health, origin, clientIp
migrations/ idempotent SQL (001 schema, 002 Hub OIDC)
deploy/ nginx config, backup sidecar
test/ 120 tests + mock KLIP and mock Hub fixturesRegla de capas (T-3): tools → adapters → core. Nada importa http/ excepto el punto de entrada. Toda la normalización de negocio vive en adapters/klip/normalize.ts, que no importa nada, por lo que se puede probar unitariamente de forma aislada.
3. Autenticación — Downstream Hub OIDC
Los usuarios piloto inician sesión con Downstream Hub (OIDC). El gateway sigue siendo el servidor de autorización con el que habla Claude; el Hub es un paso dentro de su propio flujo /authorize.
Deliberadamente no usamos el ProxyOAuthServerProvider del SDK. El proxy entregaría a Claude un token del Hub, lo que rompería la vinculación de audiencia RFC 8707, daría a Claude ámbitos del Hub más amplios que klip:read, y sacaría la emisión de tokens de nuestro control, de modo que el interruptor de corte S8 ya no podría invalidar las sesiones activas.
Claude ──/authorize──▶ gateway ──302──▶ Downstream Hub ──302──▶ /authorize/hub/callback
│ │
│ validate id_token (sig/iss/aud/nonce) │
│ check the pilot ALLOWLIST │
◀──────────────────────────────────────────────┘
└──302 code──▶ Claude ──/token──▶ gateway token (klip:read)La autenticación no es autorización. El Hub demuestra quién es alguien; la tabla users decide si puede usar el conector. La Fase 1 usa una cuenta de servicio KLIP compartida, por lo que todo usuario admitido puede leer todo lo que MCP_READONLY puede leer (revisión H8) — la pertenencia al piloto es el control de acceso a los datos. Una cuenta del Hub que no esté en la lista recibe un 403, y el límite de <= 15 se aplica mediante user:add.
En el flujo se ejecutan dos intercambios PKCE; no los confunda. El code_challenge propio de Claude protege el tramo Claude→gateway (gestionado por el SDK); un verificador separado que el gateway mantiene en el lado del servidor protege el tramo gateway→Hub.
¿Qué cliente del Hub? El propio del gateway — no el de KLIP
Registre un nuevo cliente OIDC para MCP Gateway. No reutilice el registro del Hub de KLIP, aunque KLIP ya esté registrado en el Hub DWS de pruebas.
El Hub solo toca uno de los dos límites de confianza:
Límite | Credencial | ¿Interviene el Hub? |
Claude → gateway (qué humano pregunta) | cliente del Hub propio del gateway + la lista de permitidos del piloto | sí |
gateway → KLIP (lectura de datos) |
| no — el PRD §7 lo excluye explícitamente |
Reutilizar el cliente de KLIP rompe el primer límite de forma concreta. El gateway valida el aud del token de ID contra su propio HUB_CLIENT_ID; compartir el id de cliente de KLIP significa que un token de ID emitido durante un inicio de sesión de KLIP sería aceptado por el gateway, que es el patrón de diputado confundido. Los clientes separados son lo que hace distinguibles a las dos partes que confían. También mantiene independientes las listas de permitidos de redirect-URI, los secretos de cliente, los calendarios de rotación, las entradas de auditoría SSO del Hub y los interruptores de desactivación — apagar el cliente del Hub del conector no debe tumbar el inicio de sesión de KLIP.
Nada de esto requiere ningún cambio en el lado de KLIP. K1–K4 no se ven afectados.
Dos instancias del Hub, dos registros
Registre el gateway por separado en cada Hub y empareje cada uno con el KLIP correspondiente:
Etapa | KLIP_ENV | HUB_ISSUER | Cliente |
4–6 (construcción, UAT de staging) |
| Hub DWS de pruebas | cliente del gateway en el Hub de pruebas |
7+ (corte a producción) |
| Hub de producción | un cliente del gateway separado en el Hub de producción |
El emparejamiento se aplica al arrancar, porque equivocarse en una dirección es peligroso, no solo desordenado:
KLIP_ENV=production+ unHUB_ISSUERde pruebas → el gateway se niega a arrancar. De lo contrario, cualquiera que pudiera crear una cuenta en el Hub de pruebas llegaría a datos comerciales reales.KLIP_ENV=staging+ elHUB_ISSUERde producción → avisa y continúa.
hub:check imprime el emparejamiento que ha detectado, de modo que el corte es verificable:
pairing: KLIP staging <-> Hub testingQué requiere DWS Hub (no es OIDC estándar)
Según Docs/SSO-TARGET-APP-INTEGRATION.md. Cuatro de estos puntos difieren de los valores predeterminados que asume una biblioteca de cliente OIDC, y tres fallarían directamente:
DWS Hub | |
Tipo de cliente | público, PKCE S256 — |
Descubrimiento |
|
Cuerpo del token | JSON; codificado como formulario devuelve |
Ámbitos | solo |
| obligatorio en la solicitud de token y exacto byte a byte |
Por lo tanto, HUB_DISCOVERY_URL se configura explícitamente, HUB_CLIENT_SECRET es opcional, y HUB_TOKEN_BODY tiene como valor predeterminado json con un respaldo de una sola vez a form ante unsupported_grant_type (registrando cuál funcionó, para poder fijarlo).
Configuración
Registre el gateway como cliente OIDC en el Hub con la URI de redirección
<PUBLIC_URL>/authorize/hub/callback. Es un cliente público — no pida un secreto.Ponga
HUB_ISSUER,HUB_DISCOVERY_URLyHUB_CLIENT_IDen/opt/mcp/.env.Verifique antes de que ningún usuario piloto lo intente:
docker compose exec -T gateway node dist/cli.js hub:checkEsto imprime la URI de redirección a registrar, ejecuta el descubrimiento y avisa si el Hub no anuncia PKCE S256. El gateway también sondea el descubrimiento al arrancar y lo informa en
/healthzcomohub_oidc.Añada usuarios piloto (sin contraseñas — el Hub los autentica):
docker compose exec -T gateway node dist/cli.js user:add someone@example.com "Their Name"
La cuenta de emergencia
Se permite exactamente una cuenta local con contraseña, para usarla cuando el Hub esté caído o mal configurado:
docker compose exec -T gateway node dist/cli.js user:add-break-glass it-emergency@example.comEstá oculta detrás de una divulgación en la página de inicio de sesión, obliga a cambiar la contraseña en el primer uso, y cada inicio de sesión a través de ella se audita con break_glass: true con severidad alta. Un usuario autenticado por el Hub no puede usar la ruta de contraseña en absoluto, por lo que apagar el Hub no es una forma de recurrir a una contraseña que nadie estableció.
Establece BREAK_GLASS_ENABLED=false una vez que la ruta Hub esté probada en producción para eliminar
por completo la superficie de contraseña.
Puerta de grupo opcional — no disponible en DWS Hub
HUB_REQUIRED_GROUP añade una segunda comprobación contra una reclamación de grupos. DWS Hub no
emite una (solo anuncia openid profile email), por lo que establecerla rechazaría
a todos los usuarios. hub:check avisa si lo haces. La lista de permitidos piloto en la tabla users
sigue siendo el control de autorización.
Inicio de sesión iniciado por IdP
El Hub puede enviar a un usuario directamente a una devolución de llamada desde su mosaico de panel. Eso no puede funcionar para un conector: la devolución de llamada existe para completar una solicitud de autorización que Claude inició, por lo que llegar sin una no deja nada contra lo que emitir un código. La puerta de enlace lo detecta y dice "empieza desde Claude en su lugar" en lugar de fallar como "inicio de sesión caducado".
4. Inicio rápido (local)
npm ci
docker run -d --name mcpgw-devdb -e POSTGRES_DB=gateway -e POSTGRES_USER=gateway \
-e POSTGRES_PASSWORD=devpassword -p 127.0.0.1:55432:5432 postgres:16-alpine
cp .env.example .env.dev # then edit: PUBLIC_URL=http://localhost:8787, DATABASE_URL=...55432...
npx tsx test/fixtures/mockKlip.ts 5099 & # mock KLIP, behaves like the real one
set -a; . ./.env.dev; set +a
npm run migrate
npx tsx src/index.tsCrea un usuario piloto (funciona con una TTY o entrada canalizada):
printf 'a-strong-password\na-strong-password\n' | npx tsx src/cli.ts user:add you@example.com "Your Name"5. Pruebas
npm testSuite | Cubre |
| La matriz Incoterm × estado × nulo, kg→MT, orden de redondeo, pendiente negativo, marcas de tiempo WIB |
| Tabla exhaustiva de método/ruta para T-6, escapes de recorrido y origen |
| Sobre T-5, truncamiento |
| Una búsqueda acotada publica |
| Las 9 herramientas contra KLIP simulado; ningún no-GET llega jamás a KLIP; re-inicio de sesión 401; |
| Vinculación de audiencia RFC 8707: solo se acepta el recurso canónico de este servidor |
| Redacción S5 — agresiva en cadenas, inerte en números |
| OIDC de Hub contra un proveedor simulado: discrepancia de emisor de descubrimiento, clave de firma externa, emisor/audiencia incorrectos, token caducado, nonce faltante y reproducido, reclamación sin correo electrónico |
| Admisión de |
| KLIP de producción detrás de un Hub de prueba se niega a arrancar; los emparejamientos normales no |
| Método de autenticación de punto final de token elegido del descubrimiento, incluidos Hubs de solo POST y de cliente público |
| DWS Hub modelado exactamente: descubrimiento |
El Hub simulado es un mini-proveedor OIDC funcional — documento de descubrimiento real, JWKS real, tokens de ID RS256 reales y verificación PKCE en el código de autorización — con cada perilla necesaria para forjar un token malo, porque los casos negativos son el punto.
El fixture KLIP simulado reproduce deliberadamente las peculiaridades del sistema real: kilogramos
etiquetados como MT, estados en idiomas mixtos, un incoterm fuera de los cuatro estándar, cantidades
nulas, un contrato con entrega excesiva, un limit silenciosamente limitado a 100 y un
comentario de contrato que lleva una carga útil de inyección de indicaciones.
6. Despliegue
Procedimiento completo específico del host, con la IP real, el nombre de host y la tabla de grupos de seguridad: deploy/RUNBOOK.md.
# on ECS-MCP
cd /opt/mcp && git pull
docker compose build gateway && docker compose up -d
curl -fsS http://127.0.0.1:8787/healthzCLI de administración — ten en cuenta que esto se ejecuta dentro del contenedor, porque el host instala solo Docker y no tiene Node.js:
docker compose exec -T gateway node dist/cli.js user:list
docker compose exec -T gateway node dist/cli.js audit:summary --days 7
docker compose exec -T gateway node dist/cli.js audit:export --from 2026-08-01 --to 2026-09-01 --out /tmp/audit.csv
docker compose exec -T gateway node dist/cli.js routes:verify # probes KLIP, reports Appendix A gapsInterruptor de emergencia (S8) — objetivo en menos de 5 minutos:
docker compose exec -T gateway node dist/cli.js tokens:revoke-all --reason "incident 2026-xx"
docker compose stop gatewayBreak-glass si el contenedor de la aplicación no está saludable:
docker compose exec -T db psql -U gateway -d gateway -c "UPDATE oauth_tokens SET revoked_at=now() WHERE revoked_at IS NULL;"7. El Apéndice A es una puerta estricta
src/adapters/klip/routes.ts y src/adapters/klip/fields.ts contienen cada ruta KLIP,
nombre de parámetro de consulta, límite máximo de tamaño de página, nombre de campo de respuesta y valor de enumeración del que
depende el adaptador. Cada entrada está actualmente sin verificar.
La puerta es ejecutable, no administrativa: con KLIP_ENV=production el proceso
se niega a iniciar mientras cualquier ruta esté sin verificar. Ejecuta routes:verify contra
staging, registra los resultados, establece verified: true por ruta y enums.verified = true.
Dos campos importan más que el resto:
maxLimit— ellimitmás grande que KLIP realmente acepta. Si lo limita silenciosamente a 100, unKLIP_PAGE_SIZEde 1000 convierte una página en diez y rompe el objetivo de latencia.enums.*— los valores canónicos de estado e incoterm. Cualquier cosa no mapeada se excluye de los totales con una nota de calidad de datos, nunca se asigna un valor predeterminado.
8. Desviaciones de TSD v0.9
Cada una proviene de la revisión de diseño y está comentada en su sitio de llamada.
# | Cambio | Por qué |
B3 | Origin rechazado solo cuando está presente y es inválido | La especificación exige 403 solo para un Origin presente e inválido. Claude llama al conector de servidor a servidor y puede no enviar ninguno; rechazar la ausencia daría 403 en cada llamada de herramienta. |
B4 |
| Vinculación de audiencia RFC 8707. Se añadieron |
B5 | Transporte sin estado; identidad del token por solicitud | La revisión 2026-07-28 eliminó las sesiones de protocolo. T-4 se cumple por construcción: no hay sesión que secuestrar. |
B6 | El interruptor de apagado ejecuta | El host no tiene Node.js, por lo que |
B7 | nginx es la base antiahuso; límite por usuario basado en el | Todo el tráfico llega desde el rango de salida compartido de Anthropic, por lo que la limitación por IP pondría todo el piloto en un solo cubo. Se añadió |
H1 | OAuth construido sobre | El SDK incluye el AS, incluida la revocación y la limitación de velocidad predeterminada. Solo el almacenamiento y la autenticación de usuarios son nuestros. |
H2 | El OIDC del Hub descendente es la ruta de inicio de sesión, con una cuenta local de emergencia | Adelantado desde la Fase 2. El Hub autentica; la tabla de usuarios sigue siendo la lista de permitidos del piloto. |
H3 | Andamiaje de lista de permitidos de nginx para | Anthropic publica rangos de salida estables y recomienda la lista de permitidos; |
H4 | Los resultados truncados publican | Cuatro rutas distintas hacia un número erróneo con confianza. |
H5 | Caché TTL corto; páginas 2..N obtenidas simultáneamente; tamaño de página limitado a | Diez idas y vueltas secuenciales no pueden cumplir P95 ≤ 5 s. |
H6 | Se añadió una 9.ª herramienta, | Sin ella, un nombre de planta mal escrito devuelve un conjunto vacío que se lee como «nada está pendiente». |
H7 |
| La cadena fija especificada «KLIP production» habría hecho que cada respuesta de UAT en staging afirmara ser producción. |
H9 |
| La retención mediante eliminación de particiones es lo único que permite el disparador de solo anexión; la exportación de U5 no tenía implementación; las IP de los clientes se habrían registrado todas como 127.0.0.1. |
H10 | Sidecar de respaldo, healthcheck del contenedor, detalle de | Especificado en el TSD pero nunca implementado en la guía, por lo que no habría existido en el lanzamiento. |
— | Parámetros de herramienta desconocidos rechazados mediante | El PRD 8.1 exige el rechazo; un |
— | Las herramientas devuelven | Datos tipados en lugar de JSON incrustado en prosa: menos errores de transcripción, que es lo que mide M1. |
9. Aún pendiente
Conciliación del Apéndice A (P1): bloquea la producción, aplicada al inicio.
K1–K4 del lado KLIP: el rol
MCP_READONLY, la cuentasvc-mcp, la regla del grupo de seguridad.Sincronización de respaldo fuera del host y una restauración probada: el sidecar solo escribe localmente.
CIDR de salida corporativos reales en
deploy/nginx/mcp.confy luego habilitar las dos líneasreturn 403comentadas.Prueba de carga en el NFR de capacidad de 30 usuarios simultáneos.
Registro del cliente Hub: el propio de la puerta de enlace, primero en el Hub DWS de pruebas:
HUB_ISSUER, id y secreto de cliente, con URI de redirección<PUBLIC_URL>/authorize/hub/callbackregistrada en el lado del Hub. Confirmar los nombres de las reclamaciones de correo electrónico y grupos del Hub y luego ejecutarhub:check. Se necesita un segundo registro, separado, en el Hub de producción en la Etapa 7.Decidir si establecer
HUB_REQUIRED_GROUPy si establecerBREAK_GLASS_ENABLED=falsedespués de que la ruta del Hub se demuestre en producción.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseAqualityBmaintenanceEnables read-only querying of the gong-nl-db Postgres database through natural language via Claude Desktop.9
- AlicenseNot gradedqualityDmaintenanceEnables natural language queries against Snowflake Gold-layer tables through Claude Desktop, allowing users to ask business questions in plain English without SQL knowledge.MIT
- AlicenseNot gradedqualityCmaintenanceGives Claude live access to your Observe tenant, enabling natural language queries about errors, logs, and metrics without writing OPAL pipelines.17MIT
- FlicenseNot gradedqualityBmaintenanceEnables natural language interaction with Kintone data via Claude, allowing listing apps, field definitions, querying and modifying records.
Related MCP Connectors
WHOOP recovery, strain, sleep and workouts in Claude via official WHOOP OAuth. Free, open source.
Connect Claude to Fathom meeting recordings, transcripts, and summaries
Garmin data in Claude & ChatGPT via the Garmin Health API. OAuth sign-in, no password sharing.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/dwsitproject-hub/MCP-Gateway'
If you have feedback or need assistance with the MCP directory API, please join our Discord server