Federated MCP on agentgateway
Um exemplo de docker compose que federa vários servidores MCP em endpoints de domínio. Entitlements e quotas são aplicados no gateway. Os próprios servidores MCP não contêm código de autenticação.
Documentação
Federated MCP no agentgateway
📖 Leia o artigo: Federação MCP Multi-tenant com agentgateway
Um exemplo funcional de execução de vários servidores MCP como um produto multi-tenant governado: federados em domínios de negócio, protegidos por vários provedores OAuth, com direitos de ferramentas por cliente, cotas por cliente e medição de uso por cliente para cobrança.
Três empresas — Acme, Globex, Initech — cada uma autentica-se no seu próprio provedor de identidade, conectam-se às mesmas três URLs e obtêm três visões completamente diferentes do que podem fazer.
Acme ─┐ ┌─ /mcp/billing → payments, invoicing
Globex ─┼─→ JWT ─→ agentgateway ─→ federation ├─ /mcp/analytics → reporting, telemetry
Initech ─┘ authn · authz └─ /mcp/support → tickets, crm
quota · metering
| Acme (enterprise) | Globex (standard) | Initech (trial) | |
|---|---|---|---|
/mcp/billing | 8 ferramentas | 4 somente leitura | 0 |
/mcp/analytics | 8 | 9 (+ complemento de exportação de dados) | 3 somente leitura |
/mcp/support | 9 | 0 | 3 somente tickets |
| Cota | 600/min | 60/min | 20/min |
A Globex tem mais ferramentas de análise do que a conta enterprise. Os direitos seguem acordos comerciais, não uma escada de níveis — e o gateway expressa isso diretamente.
Os seis servidores MCP não contêm código de autenticação, autorização, cota ou cobrança. Cada uma dessas propriedades é adicionada na frente deles, de forma declarativa, no controle de versão.
Documentação
| WALKTHROUGH.md | Do início ao fim — como tudo é construído, uma camada por vez |
| ONBOARDING.md | Repita o processo — adicionando um novo parceiro com seu próprio IdP |
| PRODUCTION.md | Mapeando o POC para um cenário real — 50 IdPs, locação por domínio, tokens opacos, cotas por usuário, vários DCs |
| DEMO.md | Guia do apresentador e roteiro para ./demo.sh |
Início rápido
Pré-requisitos: kubectl, helm, python3 e k3d (para criar um
cluster) ou um cluster existente. Além de uma chave de licença Solo agentgateway.
cp .env.example .env # add your AGENTGATEWAY_LICENSE_KEY
./setup.sh # creates a k3d cluster and installs everything
Depois, em um segundo terminal:
./port-forward.sh # leave running: gateway :8080, Keycloak :8180, Prometheus :9090, UI :9080
E de volta no primeiro:
./demo.sh # guided six-act walkthrough
Outros modos de configuração:
./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
Explore diretamente
scripts/mcp.py matrix
A matriz de direitos — cada ferramenta, cada empresa, lado a lado. Esta é a visão mais útil do repositório.
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
Integre um quarto parceiro:
scripts/add-partner.sh --name umbrella --display "Umbrella Corp" \
--tier standard --domains analytics,support
Como cada capacidade funciona
1. Federação — seis servidores, três endpoints
manifests/federation/backends.yaml
Um AgentgatewayBackend distribui um endpoint MCP por vários servidores. Os clientes
conectam-se uma vez e veem a união das ferramentas, com namespace <server>_<tool>.
prefixMode: Alwaysmantém os nomes das ferramentas estáveis — as regras de autorização dependem deles.- Alvos estáticos, não seletores: um seletor deriva seu prefixo do
Service descoberto (
mcp-payments-8080_get_payment); alvos estáticos mantêm o nome que você declarou. failureMode: FailOpen— um alvo inativo não derruba toda a federação.
2. Autenticação — três provedores OAuth
manifests/security/01-jwt-authentication.yaml
Uma política, três emissores. O gateway lê iss, escolhe o provedor e
verifica contra o JWKS daquele provedor. mode: Strict bloqueia acesso anônimo;
audiences fixa os tokens a este gateway.
Cada token carrega as claims company e tier — os dois valores dos quais
todo o resto depende.
3. Autorização — direitos de ferramentas por empresa
manifests/security/02-, 03-, 04-
Expressões CEL, combinadas com OR, negadas por padrão:
- 'jwt.company == "globex" && mcp.tool.target == "payments" && mcp.tool.name in ["get_payment", "list_payment_methods"]'
Duas coisas verificadas contra um gateway em execução, em vez de presumidas:
mcp.tool.nameé o nome da ferramenta de origem (get_payment), não o nome federado que o cliente vê (payments_get_payment).mcp.tool.targetlimita uma regra a um servidor — sem ele, uma regra vaza para uma ferramenta de mesmo nome em outro lugar da federação.
A filtragem aplica-se a tools/list e tools/call; uma chamada não autorizada retorna
Unknown tool, então quem chama não consegue confirmar que a ferramenta existe.
4. Cotas — aplicáveis por empresa
manifests/quotas/company-quotas.yaml
Uma tabela de descritores baseada em jwt.company, apoiada pelo Redis que acompanha
a instalação enterprise, para que os limites sejam mantidos em todas as réplicas
do gateway. Uma linha abrangente dá a qualquer empresa não listada seu próprio
contador a uma taxa padrão — novos parceiros são protegidos antes que alguém edite
o arquivo.
5. Medição — cobrança por cliente
manifests/observability/01-metering-attributes.yaml
O agentgateway já conta chamadas MCP por servidor e ferramenta; isto adiciona os
rótulos company e tier do JWT validado. A cobrança então se torna
uma única consulta:
sum by (company, server, resource) (agentgateway_mcp_requests_total{method="tools/call"})
scripts/chargeback.py executa-a, precifica-a a partir de scripts/pricing.json e também
relata permitido vs negado vs limitado por empresa.
6. A interface — veja, não apenas use curl
setup.sh também instala a Solo Enterprise UI — o painel do agentgateway
(gateways, rotas, backends, políticas, tráfego, rastreamento e as visualizações de
Gerenciamento de Custos). Ela roda em http://localhost:9080 assim que ./port-forward.sh estiver ativo.
-
Login:
operator/operator— uma identidade de operador de plataforma do realmplatform(manifests/keycloak/realms/platform.json), que é deliberadamente separado dos realms de clientes: operadores veem todo o ambiente, clientes apenas falam MCP com o gateway. -
Entrada única de hosts (SSO no navegador — o emissor OIDC deve ser resolvível no seu navegador):
echo "127.0.0.1 keycloak.mcp-federation.svc.cluster.local" | sudo tee -a /etc/hosts -
Rastreamento: manifests/ui/tracing.yaml envia os spans do gateway para o coletor de telemetria da interface, então cada chamada MCP federada aparece na visualização de Rastreamento com seu servidor de destino, ferramenta e latência.
-
Pule tudo isso com
./setup.sh --no-ui— nada mais depende disso.
Mapa do repositório
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
Os servidores MCP de exemplo
Todos os seis executam um único arquivo — mcp-server/server.py, apenas biblioteca
padrão do Python — em uma imagem python:3.12-alpine padrão. Tudo específico do servidor
(nome, ferramentas, esquemas, respostas falsas) vem de uma especificação JSON de
ferramentas em um ConfigMap.
Isso significa nenhuma imagem para construir e nenhum registro para enviar. setup.sh publica
server.py como um ConfigMap e os pods o montam. Para alterar uma ferramenta, edite o JSON
em manifests/mcp-servers/ e reaplique.
Você também pode executar um localmente:
TOOLS_FILE=<(kubectl get cm mcp-tools-payments -n mcp-federation -o jsonpath='{.data.tools\.json}') \
python3 mcp-server/server.py
As ferramentas retornam dados falsos realistas e determinísticos — os mesmos argumentos sempre produzem o mesmo número de fatura, então uma nova execução da demonstração parece idêntica.
Notas e pegadinhas
O emissor do Keycloak é fixado. KC_HOSTNAME está definido para a URL do serviço
dentro do cluster, para que tokens emitidos por meio de kubectl port-forward ainda carreguem o
iss dentro do cluster e sejam aceitos pelo gateway. Sem isso, um token obtido do seu
laptop alegaria iss=http://localhost:8180 e seria rejeitado.
Realms são importados na inicialização. Adicionar um realm requer
kubectl rollout restart deploy/keycloak -n mcp-federation.
Políticas se propagam via xDS. Aguarde alguns segundos após kubectl apply
antes de testar. demo.sh já inclui isso.
Descoberta de namespace. Se o agentgateway já foi instalado com
discoveryNamespaceSelectors — comum ao reutilizar um cluster que executa outras
demonstrações — seu controlador ignora namespaces que não correspondem, e o Gateway fica
em Waiting for controller sem causa óbvia. setup.sh detecta isso e
rotula o namespace automaticamente; se o seletor usar matchExpressions
em vez de matchLabels, ele avisa, pois isso exige uma decisão humana.
Sua própria instalação não define seletor.
As unidades de cota são requisições HTTP, não chamadas de ferramentas. Uma sessão MCP gasta algumas
(initialize, tools/list, depois uma por chamada), por isso o nível trial é excedido
após algumas operações.
Chamadas negadas ainda são contadas em agentgateway_mcp_requests_total — ele não tem
rótulo de status. scripts/chargeback.py cruza referências com
agentgateway_requests_total (que tem) para separar permitidas de negadas e
limitadas. Uma tabela de preços em produção cobraria apenas por sucessos.
Versões
Validado contra:
| Componente | Versão |
|---|---|
| Solo Enterprise agentgateway | v2026.8.0 |
| Solo Enterprise UI (chart de gerenciamento) | 0.5.4 |
| Gateway API | v1.5.0 |
| Keycloak | 26.0 |
| Prometheus | v3.1.0 |
| Protocolo MCP | 2025-06-18 |
Substitua qualquer um deles em .env (veja .env.example).