docs: correct the audience table and refresh the blockers

- Two audience scopes per MCP server, not one: Kong's own openid-connect
  plugin re-exchanges the token, so it must also name
  kong-token-exchange-broker. Measured on catalog.
- Drop the resolved Konnect-not-persisting blocker; the cause was a dead
  Konnect org, not Konnect refusing writes.
- Drop the resolved kong-token-exchange-broker secret mismatch; its
  client_credentials returns 200.
- Record the real blocker: PydanticAI's MCP client cannot open a session
  against ai-mcp-proxy.
- Document the realm's override-on-start import and ${KC_SECRET_*}
  placeholders, and the a2a-sdk 1.x serving requirements.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Stéphane Tailland
2026-08-28 10:36:47 +02:00
parent ddc0f24cb9
commit 92f0a272a5
+47 -13
View File
@@ -95,13 +95,21 @@ access token five minutes.
MCP servers, one per domain, served by Kong: MCP servers, one per domain, served by Kong:
| Domain | URL | Audience scope | | Domain | URL | Audience scopes to request |
|---|---|---| |---|---|---|
| catalog | `https://catalog-dev.sttlab.local/catalog-mcp` | `mcp-catalog-audience` | | catalog | `https://catalog-dev.sttlab.local/catalog-mcp` | `mcp-catalog-audience kong-token-exchange-broker-audience` |
| customers | `https://customers-dev.sttlab.local/customers-mcp` | `mcp-customers-audience` | | customers | `https://customers-dev.sttlab.local/customers-mcp` | `mcp-customers-audience kong-token-exchange-broker-audience` |
| payments | `https://payments-dev.sttlab.local/payments-mcp` | `mcp-payments-audience` | | payments | `https://payments-dev.sttlab.local/payments-mcp` | `mcp-payments-audience kong-token-exchange-broker-audience` |
| sales | `https://sales-dev.sttlab.local/sales-mcp` | `mcp-sales-audience` | | sales | `https://sales-dev.sttlab.local/sales-mcp` | `mcp-sales-audience kong-token-exchange-broker-audience` |
| workforce | `https://workforce-dev.sttlab.local/workforce-mcp` | `mcp-workforce-audience` | | workforce | `https://workforce-dev.sttlab.local/workforce-mcp` | `mcp-workforce-audience kong-token-exchange-broker-audience` |
**Two scopes per server, not one.** The gateway's own `openid-connect` plugin
exchanges the token again before reaching the backend, so the token must also
name `kong-token-exchange-broker`. Without it the call fails with a bare `401`
and the real cause appears only in the gateway log — *invalid status code
received from the token endpoint (403)*. Measured on catalog: with both scopes
the MCP server answers `200`, with `mcp-catalog-audience` alone it answers
`401`. LibreChat has always requested both.
LLM inference goes through the Kong AI gateway, never direct to a provider: LLM inference goes through the Kong AI gateway, never direct to a provider:
@@ -153,12 +161,32 @@ The realm actually deployed is `infra-keycloak/k8s/keycloak/configmap-realm.yaml
— note that `kong-v2/classic-models-realm.yaml` is an older partial copy and is — note that `kong-v2/classic-models-realm.yaml` is an older partial copy and is
**not** authoritative. **not** authoritative.
## Known blockers **The realm file is re-imported with `--override true` on every pod start.** An
`import-realm` initContainer replays it before Keycloak serves traffic, so the
file wins over the database and nothing created through the admin console
survives a restart. Add clients here, never in the console.
**Konnect is not persisting configuration.** The operator loops on the same **Client secrets and user passwords in that file are `${KC_SECRET_*}` /
objects without converging and the dataplanes stay behind the control plane, so `${KC_USER_*}` placeholders**, substituted from the `keycloak-realm-secrets`
no new Kong config reaches the gateways — including any new agent audience. Secret (Vault + ExternalSecret). Never commit a literal secret back into it.
Resolve this before expecting an end-to-end run.
## Serving A2A
The `a2a-sdk` 1.x server rejects a request that does not carry the protocol
version header:
```
A2A-Version: 1.0
```
`DefaultRequestHandler` speaks the older 0.3 shape; use
`DefaultRequestHandlerV2`, and mount both `create_agent_card_routes(...)` and
`create_jsonrpc_routes(handler, "/", enable_v0_3_compat=True)`. An executor must
enqueue the `Task` before any status update, or the client blocks waiting for a
task it never saw. `AgentInterface` names the field `protocol_binding`, not
`transport`.
## Known blockers
**No `employeeNumber` in tokens.** Sales territory scoping (a rep sees their **No `employeeNumber` in tokens.** Sales territory scoping (a rep sees their
territory, a manager sees all) resolves through territory, a manager sees all) resolves through
@@ -166,8 +194,14 @@ territory, a manager sees all) resolves through
identity, so the API cannot filter server-side. The realm's users identity, so the API cannot filter server-side. The realm's users
(`admin-user`, `pm-user`, …) are role placeholders, not real employees. (`admin-user`, `pm-user`, …) are role placeholders, not real employees.
**The `kong-token-exchange-broker` secret does not match the realm** — its **PydanticAI's MCP client cannot open a session against Kong's
`client_credentials` returns `unauthorized_client`. `ai-mcp-proxy`.** It fails with `Failed to initialize server session` and
swallows the cause. Everything else was eliminated: identity, TLS, DNS and the
token are all fine, and the three handshake steps — `initialize` (200 with a
session id), the SSE `GET` (200), `notifications/initialized` (202) — all
succeed with curl using the very token the agent minted. This blocks the
Catalogue agent end to end, and with it the Kong AI gateway path, which has
therefore never been exercised.
## Conventions ## Conventions