Federated MCP on agentgateway
Un ejemplo de docker compose que federiza varios servidores MCP en endpoints de dominio. Los derechos y cuotas se aplican en la puerta de enlace. Los propios servidores MCP no contienen código de autenticación.
Documentación
Federated MCP en agentgateway
📖 Lee el artículo completo: Federación MCP multiinquilino con agentgateway
Un ejemplo funcional de ejecución de múltiples servidores MCP como un producto multiusuario gobernado: federados en dominios de negocio, con varios proveedores OAuth al frente, con derechos de herramientas por cliente, cuotas por cliente y medición de uso por cliente para el cargo por uso.
Tres empresas — Acme, Globex, Initech — cada una se autentica contra su propio proveedor de identidad, se conecta a las mismas tres URL y obtiene tres vistas completamente diferentes de lo que pueden hacer.
Acme ─┐ ┌─ /mcp/billing → payments, invoicing
Globex ─┼─→ JWT ─→ agentgateway ─→ federation ├─ /mcp/analytics → reporting, telemetry
Initech ─┘ authn · authz └─ /mcp/support → tickets, crm
quota · metering
| Acme (empresa) | Globex (estándar) | Initech (prueba) | |
|---|---|---|---|
/mcp/billing | 8 herramientas | 4 solo lectura | 0 |
/mcp/analytics | 8 | 9 (+ complemento de exportación de datos) | 3 solo lectura |
/mcp/support | 9 | 0 | 3 solo tickets |
| Cuota | 600/min | 60/min | 20/min |
Globex tiene más herramientas de análisis que la cuenta empresarial. Los derechos siguen acuerdos comerciales, no una escalera de niveles — y la puerta de enlace lo expresa directamente.
Los seis servidores MCP no contienen código de autenticación, autorización, cuota ni facturación. Cada una de esas propiedades se añade delante de ellos, de forma declarativa, en el control de versiones.
Documentación
| WALKTHROUGH.md | De principio a fin — cómo se construye todo, capa por capa |
| ONBOARDING.md | Repetir el proceso — añadir un nuevo socio con su propio IdP |
| PRODUCTION.md | Mapear el POC a un entorno real — 50 IdP, tenencia por dominio, tokens opacos, cuotas por usuario, muchos centros de datos |
| DEMO.md | Guía del presentador y guion para ./demo.sh |
Inicio rápido
Requisitos previos: kubectl, helm, python3 y k3d (para crear un
clúster) o un clúster existente. Además, una clave de licencia de agentgateway Solo.
cp .env.example .env # add your AGENTGATEWAY_LICENSE_KEY
./setup.sh # creates a k3d cluster and installs everything
Luego, en una segunda terminal:
./port-forward.sh # leave running: gateway :8080, Keycloak :8180, Prometheus :9090, UI :9080
Y de vuelta en la primera:
./demo.sh # guided six-act walkthrough
Otros modos de configuración:
./setup.sh --use-current-context # install into whatever kubectl points at
./setup.sh --skip-install # re-apply manifests only (fast iteration)
./teardown.sh # remove the demo namespace
./teardown.sh --cluster # delete the whole k3d cluster
Explóralo directamente
scripts/mcp.py matrix
La matriz de derechos — cada herramienta, cada empresa, lado a lado. Esta es la vista más útil del repositorio.
scripts/mcp.py list acme billing # what one company can see
scripts/mcp.py token globex # the token and its claims
# an entitled call succeeds
scripts/mcp.py call globex analytics reporting_export_dataset '{"dataset":"fact_transactions"}'
# an unentitled call is not merely blocked — the tool is invisible
scripts/mcp.py call globex billing payments_create_charge '{"customer_id":"c","amount_cents":1}'
scripts/mcp.py quota initech 25 # watch the quota engage
scripts/chargeback.py --by-tool # usage and cost by customer
scripts/chargeback.py --csv # same data for a billing pipeline
Incorporar un cuarto socio:
scripts/add-partner.sh --name umbrella --display "Umbrella Corp" \
--tier standard --domains analytics,support
Cómo funciona cada capacidad
1. Federación — seis servidores, tres puntos finales
manifests/federation/backends.yaml
Un AgentgatewayBackend distribuye un punto final MCP entre varios servidores. Los
clientes se conectan una vez y ven la unión de las herramientas, con espacios de
nombres <server>_<tool>.
prefixMode: Alwaysmantiene estables los nombres de las herramientas — las reglas de autorización dependen de ellos.- Destinos estáticos, no selectores: un selector deriva su prefijo del
Servicio descubierto (
mcp-payments-8080_get_payment); los destinos estáticos mantienen el nombre que declaraste. failureMode: FailOpen— que un destino caiga no hace fallar toda la federación.
2. Autenticación — tres proveedores OAuth
manifests/security/01-jwt-authentication.yaml
Una política, tres emisores. La puerta de enlace lee iss, elige el
proveedor y verifica contra el JWKS de ese proveedor. mode: Strict cierra el
acceso anónimo; audiences fija los tokens a esta puerta de enlace.
Cada token lleva las reclamaciones company y tier — los dos
valores de los que depende todo lo demás.
3. Autorización — derechos de herramientas por empresa
manifests/security/02-, 03-, 04-
Expresiones CEL, combinadas con OR, denegadas por defecto:
- 'jwt.company == "globex" && mcp.tool.target == "payments" && mcp.tool.name in ["get_payment", "list_payment_methods"]'
Dos cosas verificadas contra una puerta de enlace en ejecución en lugar de supuestas:
mcp.tool.namees el nombre de la herramienta de origen (get_payment), no el nombre federado que ve el cliente (payments_get_payment).mcp.tool.targetlimita una regla a un servidor — sin él, una regla se filtra a una herramienta con el mismo nombre en otra parte de la federación.
El filtrado se aplica a tools/list y a tools/call; una llamada no
autorizada devuelve Unknown tool, por lo que un llamador no puede confirmar
que la herramienta existe.
4. Cuotas — aplicables por empresa
manifests/quotas/company-quotas.yaml
Una tabla de descriptores con clave en jwt.company, respaldada por el Redis
que incluye la instalación empresarial, de modo que los límites se mantienen
en todas las réplicas de la puerta de enlace. Una fila general da a cualquier
empresa no listada su propio contador a una tasa predeterminada — los nuevos
socios están protegidos antes de que alguien edite el archivo.
5. Medición — cargo por uso por cliente
manifests/observability/01-metering-attributes.yaml
agentgateway ya cuenta las llamadas MCP por servidor y herramienta; esto añade
las etiquetas company y tier del JWT validado. El cargo por
uso se convierte entonces en una consulta:
sum by (company, server, resource) (agentgateway_mcp_requests_total{method="tools/call"})
scripts/chargeback.py la ejecuta, la valora desde scripts/pricing.json y también
informa de permitidas frente a denegadas frente a limitadas por empresa.
6. La interfaz de usuario — verlo, no solo con curl
setup.sh también instala la Interfaz de usuario de Solo Enterprise —
el panel de agentgateway (puertas de enlace, rutas, backends, políticas, tráfico,
trazado y las vistas de Gestión de costes). Se ejecuta en http://localhost:9080 una
vez que ./port-forward.sh está activo.
-
Inicio de sesión:
operator/operator— una identidad de operador de plataforma del reinoplatform(manifests/keycloak/realms/platform.json), que está deliberadamente separada de los reinos de clientes: los operadores ven todo el entorno, los clientes solo hablan MCP con la puerta de enlace. -
Entrada de hosts de una sola vez (SSO del navegador — el emisor OIDC debe resolverse en tu navegador):
echo "127.0.0.1 keycloak.mcp-federation.svc.cluster.local" | sudo tee -a /etc/hosts -
Trazado: manifests/ui/tracing.yaml envía los spans de la puerta de enlace al recopilador de telemetría de la interfaz, de modo que cada llamada MCP federada aparece en la vista de Trazado con su servidor de destino, herramienta y latencia.
-
Omite todo con
./setup.sh --no-ui— nada más depende de ello.
Mapa del repositorio
setup.sh build everything
demo.sh six-act interactive walkthrough
port-forward.sh gateway :8080, Keycloak :8180, Prometheus :9090
teardown.sh remove it
mcp-server/server.py the stub MCP server — stdlib only, all six run this file
manifests/
00-namespace.yaml
keycloak/
keycloak.yaml one Keycloak, three realms
realms/*.json one file per company — add a file to add a company
mcp-servers/ six servers: tools + Deployment + Service each
federation/
backends.yaml three virtual MCP servers
gateway.yaml the Gateway and its three routes
security/
01-jwt-authentication.yaml
02-authorization-billing.yaml
03-authorization-analytics.yaml
04-authorization-support.yaml
quotas/company-quotas.yaml
observability/
01-metering-attributes.yaml
02-prometheus.yaml
ui/
tracing.yaml gateway spans → the UI's telemetry collector
scripts/
mcp.py MCP client: list, call, matrix, quota, token
chargeback.py usage and cost report from Prometheus
pricing.json the rate card — edit freely
add-partner.sh onboard a new partner
Los servidores MCP de demostración
Los seis ejecutan un solo archivo — mcp-server/server.py, solo biblioteca estándar
de Python — en una imagen estándar de python:3.12-alpine. Todo lo específico del
servidor (nombre, herramientas, esquemas, respuestas falsas) proviene de una
especificación JSON de herramientas en un ConfigMap.
Eso significa que no hay imagen que construir ni registro al que enviar.
setup.sh publica server.py como ConfigMap y los pods lo montan.
Para cambiar una herramienta, edita el JSON en manifests/mcp-servers/ y vuelve a
aplicarlo.
También puedes ejecutar uno localmente:
TOOLS_FILE=<(kubectl get cm mcp-tools-payments -n mcp-federation -o jsonpath='{.data.tools\.json}') \
python3 mcp-server/server.py
Las herramientas devuelven datos falsos realistas y deterministas — los mismos argumentos siempre producen el mismo número de factura, por lo que una repetición de la demostración se ve idéntica.
Notas y advertencias
El emisor de Keycloak está fijado. KC_HOSTNAME está configurado con la
URL del servicio dentro del clúster para que los tokens emitidos a través de
kubectl port-forward sigan llevando el iss dentro del clúster y sean
aceptados por la puerta de enlace. Sin ello, un token obtenido desde tu portátil
reclamaría iss=http://localhost:8180 y sería rechazado.
Los reinos se importan al inicio. Añadir un reino requiere
kubectl rollout restart deploy/keycloak -n mcp-federation.
Las políticas se propagan a través de xDS. Espera unos segundos después de
kubectl apply antes de probar. demo.sh incluye esto.
Descubrimiento de espacios de nombres. Si agentgateway ya estaba instalado
con discoveryNamespaceSelectors — común al reutilizar un clúster que ejecuta otras
demostraciones — su controlador ignora los espacios de nombres que no coinciden,
y la puerta de enlace queda en Waiting for controller sin causa evidente.
setup.sh lo detecta y etiqueta el espacio de nombres automáticamente; si
el selector usa matchExpressions en lugar de matchLabels, avisa en su lugar,
ya que eso requiere una decisión humana. Su propia instalación no establece
ningún selector.
Las unidades de cuota son solicitudes HTTP, no llamadas a herramientas. Una
sesión MCP gasta unas pocas (initialize, tools/list, y luego una por
llamada), por lo que el nivel de prueba se agota después de unas pocas
operaciones.
Las llamadas denegadas también se cuentan en agentgateway_mcp_requests_total — no tiene
etiqueta de estado. scripts/chargeback.py cruza referencias con
agentgateway_requests_total (que sí la tiene) para separar permitidas de denegadas y
limitadas. Una tarifa de producción facturaría solo los éxitos.
Versiones
Validado contra:
| Componente | Versión |
|---|---|
| Solo Enterprise agentgateway | v2026.8.0 |
| Interfaz de usuario de Solo Enterprise (gráfico de gestión) | 0.5.4 |
| Gateway API | v1.5.0 |
| Keycloak | 26.0 |
| Prometheus | v3.1.0 |
| Protocolo MCP | 2025-06-18 |
Anula cualquiera de ellos en .env (consulta .env.example).