Skip to content

Use the resource URL as the hosted k8s MCP token audience

Date: 2026-07-03 Status: Accepted Decision: hl-94n1 Deciders: Sean Brandt

Given the optional-scope audience mechanism (see the per-resource-audience mechanism ADR), the audience value could be an arbitrary opaque string (e.g. the existing static client id mcp-kubernetes) or the literal resource URL already used as the client’s RFC 8707 resource= parameter and the route’s resourceMetadata.resource. This decision fixes the on-the-wire audience value that every validator (agentgateway route, k3s API server) must accept.

The token audience is the canonical resource URL https://mcp-gw.fzymgc.house/mcp/kubernetes, matching the resource= the client sends and the route’s declared resourceMetadata.resource. The agentgateway route audiences and the k3s k3s_oidc_audiences list are both updated to trust this URL (replacing the opaque mcp-kubernetes string).

  • RFC 8707 fidelity: the audience literally names the resource the token is bound to.
  • Self-descriptive tokens make the trust boundary auditable from the token alone, independent of Keycloak client configuration.
  • Aligns the audience with the identifier already used across OAuth discovery and authorization.
  • Resource URL https://mcp-gw.fzymgc.house/mcp/kubernetes (chosen): RFC 8707-faithful and self-descriptive; matches resource= and resourceMetadata.resource already in use.
  • Opaque client-id string mcp-kubernetes / status quo (rejected): requires no gateway or k3s change (both already validate that literal), but the aud does not name the resource it authorizes, undermining self-description.
  • Positive: the token aud is self-describing for audits and debugging.
  • Negative: requires changing the gateway route audiences and rerolling k3s_oidc_audiences via a serial, health-gated Ansible play — a larger blast radius than the opaque token.
  • Neutral: the k3s audience list stays multi-element, so audienceMatchPolicy: MatchAny is still emitted (#1578).