A receita do Kong

Duas formas de colocar a verificação na frente das suas ferramentas a partir do Kong AI Gateway. Cada arquivo que a receita aplica pode ser baixado separadamente:

Arquivo O que é
kong-tool-check.yaml rota 2: a rota das ferramentas com a sua pre-function
pre-function.lua a própria pre-function, como a rota a carrega
kong-opa.yaml rota 1: as configurações do plugin OPA
opa-input.example.json a entrada com que o OPA é consultado
network-policy.yaml a NetworkPolicy do Kubernetes que faz do Kong o único caminho até as ferramentas

As duas formas colocam o Kong entre os seus agentes e os seus servidores de ferramentas. A verificação da rota 2 é o seu próprio qls-router enterprise, que você executa no seu próprio ambiente, em uma URL que você escolhe: as verificações de ferramentas e os recibos leem as chamadas que os seus agentes fazem, por isso a API hospedada da Stalwart (api.stalwart.it) não os serve. Nenhuma das duas formas funciona se as ferramentas não puderem ser alcançadas só pelo Kong, então aplique a política de rede em network-policy.yaml (com os pods do Kong como gateway), ou o equivalente.

1. O portão do rulebook no OPA

Emita o portão do seu rulebook como um pacote Rego:

$ RulebookTranslator emit policy.co --target rego --out-dir ./opa
wrote ./opa/rulebook.ir.json
wrote ./opa/Rulebook.rego

O pacote é stalwart.<the policy file's name> (stalwart.policy aqui), e a sua decision é {"decision": "allow" | "refuse" | "clarify" | "silent", "rule": ...} para input.tool sob input.context. Carregue Rulebook.rego no servidor OPA que o plugin OPA do Kong consulta, defina opa_path em kong-opa.yaml como o seu pacote e aplique-o. opa-input.example.json mostra a entrada. Um refuse é um 403; trate clarify como uma chamada que precisa de uma pessoa, a quem esta rota não pode perguntar, e recuse-a também.

O Rego é o portão e apenas o portão. O esquema dos argumentos, a origem dos argumentos, o histórico da sessão e as regras de ordem, os rótulos de confiança, as confirmações e as reversões não estão nele, e o cabeçalho do arquivo emitido diz isso. Use a rota 2, ou o stalwart gateway, para isso.

2. Uma rota que chama a verificação completa

kong-tool-check.yaml coloca uma pre-function na rota das ferramentas (pre-function.lua). Ela lê cada tools/call MCP (ou uma solicitação simples {"tool", "arguments"}); toda outra mensagem MCP -- initialize, tools/list, uma notificação -- segue sem verificação, já que não executa nada.

Uma chamada de um agente que verifica as próprias chamadas -- um agente stalwart-agents cuja fonte de ferramentas diz receipts: true, ou um agente LangGraph no nosso nó de ferramentas protegido -- leva o seu recibo: o histórico da sessão do agente, terminando na verificação que permitiu esta chamada, no _meta.stalwart do MCP ou no cabeçalho X-Stalwart-Receipt. A rota o envia ao POST /v1/tool-receipts do seu roteador, que diz se o token do histórico confere e se a sua última entrada permite exatamente esta chamada. A chamada é admitida pela verificação do próprio agente, de modo que uma escrita que a pessoa aprovou no agente não é perguntada uma segunda vez; um recibo para outros argumentos, outra ferramenta ou uma chamada já relatada é um 403.

O agente é o que está assinado na última entrada do histórico: a rota não envia nenhum agente próprio, e um recibo não pode ser apresentado como de outro agente. Um recibo admite uma chamada. O roteador o gasta na primeira chamada que admite e guarda só os digests do token do histórico e dessa chamada. Apresentado de novo, o recibo é um 403 receipt_spent, a menos que seja a mesma chamada -- com os mesmos argumentos e a mesma chave de idempotência -- e que reenviá-la seja seguro: uma leitura, uma ferramenta declarada idempotente ou uma escrita cuja chave vai em um argumento. Essa é a nova tentativa do próprio agente. A repetição de uma escrita sob uma chave nova é recusada.

Uma chamada sem recibo é verificada por completo no POST /v1/tool-checks do seu roteador e só passa com allow. confirm volta ao cliente como um 409 com o texto e o token da confirmação; o cliente os mostra a uma pessoa e, depois, envia a chamada de novo com o token em X-Stalwart-Confirmation. O histórico da sessão trafega em X-Stalwart-History, e a entrada e o token de cada resposta voltam em X-Stalwart-Entry e X-Stalwart-History-Token para o cliente acrescentá-los a ele, de modo que nenhum estado fica no Kong.

Defina STALWART_ROUTER_URL (a URL base do seu qls-router, como https://qls-router.internal:8080) e QLS_API_KEY no ambiente do Kong. A rota consulta /v1/tool-checks e /v1/tool-receipts sob ela. Com STALWART_ROUTER_URL não definida, toda chamada é recusada com um 503 router_not_configured: a rota nunca recorre a outra verificação.

Como a receita é testada

A própria suíte do pacote lê as duas configurações e executa o próprio pre-function.lua em uma fase de acesso emulada: chamadas MCP de um agente com e sem recibo, um recibo adulterado, as mensagens que não chamam nada, uma verificação inalcançável e uma configuração que não nomeia nenhum roteador.