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/billing8 herramientas4 solo lectura0
/mcp/analytics89 (+ complemento de exportación de datos)3 solo lectura
/mcp/support903 solo tickets
Cuota600/min60/min20/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.mdDe principio a fin — cómo se construye todo, capa por capa
ONBOARDING.mdRepetir el proceso — añadir un nuevo socio con su propio IdP
PRODUCTION.mdMapear el POC a un entorno real — 50 IdP, tenencia por dominio, tokens opacos, cuotas por usuario, muchos centros de datos
DEMO.mdGuí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: Always mantiene 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.name es el nombre de la herramienta de origen (get_payment), no el nombre federado que ve el cliente (payments_get_payment).
  • mcp.tool.target limita 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 reino platform (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 archivomcp-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:

ComponenteVersión
Solo Enterprise agentgatewayv2026.8.0
Interfaz de usuario de Solo Enterprise (gráfico de gestión)0.5.4
Gateway APIv1.5.0
Keycloak26.0
Prometheusv3.1.0
Protocolo MCP2025-06-18

Anula cualquiera de ellos en .env (consulta .env.example).