Design: MCP DCR honesty — drop the mock-DCR short-circuit, pin agentgateway GA
- Bead: hl-0sr.26 (child of epic hl-0sr)
- Date: 2026-06-28
- Status: Draft (pending design-reviewer gate)
- Author: Sean (with Claude)
- Corrects:
docs/engineering/specs/2026-06-06-llm-gw-mcp-gw-migration-design.md(D3/D10/D13) and ADRhl-23m
Problem
Section titled “Problem”The mcp-engram-oauth AgentgatewayPolicy (argocd/app-configs/agentgateway/mcp-engram.yaml)
sets jwtAuthentication.mcp.clientId: "mcp-public" with a comment reading “mock-DCR
short-circuit (Authentik has no RFC 7591 DCR)”. The intent (spec D3/D13, ADR hl-23m)
was that agentgateway serve a mock RFC 7591 /register returning the pre-configured
mcp-public client_id, so DCR-requiring MCP clients work despite Authentik lacking DCR.
Deployed reality: the mock-DCR is unreachable — POST /register and
GET /.well-known/oauth-authorization-server/... both 404 (found verifying hl-0sr.17).
The clientId field is, in effect, inert dead config. Current clients (Claude Code) are
unaffected because they use the static mcp-public PKCE client directly (verified
end-to-end in hl-0sr.17); they never exercise the mock-DCR path.
The spec and ADR therefore describe a capability the cluster does not have and that the deployed config implies but does not deliver. This design stops the pretense.
Root cause (grounded)
Section titled “Root cause (grounded)”The mock-DCR is not “a missing HTTPRoute match away from working” — a 2026-06-07
source-level read of agentgateway v1.3.0-alpha.1 (crates/agentgateway/src/mcp/auth.rs)
showed it is structurally unusable against Authentik:
- Without
mcp.providerthe gateway never interposes as the authorization server: the PRM’sauthorization_serverspoints at the raw Authentik issuer and/.well-known/oauth-authorization-serveris not served, so theclientId/registershort-circuit is unreachable regardless of route matches. - With
mcp.provider(enumAuth0 | Keycloak | Oktaonly — no generic, no Authentik adapter) the AS-metadata proxy hard-requiresregistration_endpointin the IdP discovery document and returns HTTP 500 (confirmed for Okta/Keycloak viaProxyError::ProcessingString; Auth0 unconfirmed — moot, since Authentik is not in the enum at all) when absent. Authentik’s discovery has none.
Net: mock-DCR cannot work against Authentik in this version, with or without provider.
A 2026-06-28 deepwiki query asserted clientId short-circuits client_registration
independent of provider; that assertion is accurate at the function level
(clientId → build_mock_dcr_response regardless of provider) but does not establish
route reachability. The source read above shows the mock-DCR route (/register) and
/.well-known/oauth-authorization-server are not served without provider, so no
request ever reaches the short-circuit — and the bead’s live 404 probes confirm the
unreachability. Function-level provider-independence and route-level reachability are
non-contradictory; the latter is what makes mock-DCR unusable against Authentik here.
See engram memory 9b080d3a for the trace.
Go-forward DCR stance (grounded 2026-06-28: gh releases + issues, deepwiki, context7)
Section titled “Go-forward DCR stance (grounded 2026-06-28: gh releases + issues, deepwiki, context7)”- Neither DCR mode is deprecated. Mock-DCR (
clientId→build_mock_dcr_response) is “stable and supported”; real DCR-proxy (provider) is the dynamic-registration path. - Mock-DCR is incomplete: agentgateway issue #1936 (OPEN) — “MCP clientId mock DCR response is missing RFC 7591 metadata fields” — strict DCR clients may reject the response even if reachable.
- Real DCR-proxy is IdP-adapter-gated:
McpIDP=Auth0 | Keycloak | Oktaonly. No Authentik adapter exists and none is planned. Generic-issuer DCR-proxy (noprovider) is unreliable (provider-specific CORS + AS-metadata rewriting); “might work if the IdP exposes a spec-compliantregistration_endpointat the default path” but with no guarantee. context7 frames non-listed IdPs as “added with minimal code changes” = write an adapter. - Authentik DCR is on a real roadmap:
goauthentik/authentik#8751is OPEN, labeledenhancement+enhancement/confirmed(“will be implemented”), milestoned to “Release 2026.8.0: Required” (~Aug 2026, ~5 weeks from 2026-06-28). Not “no roadmap” — a committed deliverable. (The epic’s earlier “planned ~2026-08” note understated this as a vague hope.) - Relevant fixed bug: agentgateway #1205 (CLOSED)
— “RFC 8414 URL construction incorrect for path-based issuers.” Authentik uses path-based
issuers (
/application/o/<slug>/), so this fix matters for any future Authentik DCR-proxy.
Decision
Section titled “Decision”Approach B — get honest. Drop the mock-DCR; standardize on the static mcp-public
PKCE client; correct the spec/ADR/ops docs to match deployed reality; dissolve the
obsolete alpha pin by bumping to GA.
D1 — Remove the inert clientId field
Section titled “D1 — Remove the inert clientId field”Delete clientId: "mcp-public" (and its “mock-DCR short-circuit” comment) from the
mcp-engram-oauth policy. Keep jwtAuthentication.mode: Strict + the Authentik
issuer/audiences/jwks provider + resourceMetadata (the PRM) — those are the real,
working enforcement + discovery pieces. The PRM continues to advertise Authentik directly
as the authorization server, which is correct for the static-client model.
No client-facing change; no cutover; no outage. Claude Code already uses static
mcp-public and is unaffected.
D2 — Bump agentgateway v1.3.0-alpha.1 → v1.3.1 GA
Section titled “D2 — Bump agentgateway v1.3.0-alpha.1 → v1.3.1 GA”The alpha-pin override (ADR hl-23m addendum / spec D10: “no GA release exposes
clientId”) is dissolved: v1.3.0 GA
shipped 2026-06-18 and v1.3.1 GA
is latest (2026-06-22); clientId (added in 1.3.0-alpha) is in GA. Return to the
cluster’s latest-GA policy (argocd/CLAUDE.md). Four coupled pin sites:
| File | Field | 1.3.0-alpha.1 → |
|---|---|---|
argocd/cluster-app/templates/agentgateway-controller.yaml:22 | chart agentgateway-crds targetRevision | 1.3.1 |
argocd/cluster-app/templates/agentgateway-controller.yaml:27 | chart agentgateway targetRevision | 1.3.1 |
argocd/app-configs/agentgateway-controller/values-controller.yaml:3 | controller image tag | v1.3.1 |
argocd/app-configs/agentgateway/parameters.yaml:17 | data-plane image tag | v1.3.1 |
The two image-tag comments currently cite “alpha required for mcp.clientId CRD field
(decision A / ADR hl-23m)” — rewrite them to reflect GA + the D1 removal (the field is
no longer used; the pin is latest-GA, no override). The openrouter-generic.yaml:6
comment (“as of agentgateway v1.3.0-alpha.1 Detect does not apply pathPrefix”) is a
behavioral observation owned by hl-rpm9.8 — left untouched here.
Side benefit: the bump may resolve hl-0sr.24 (alpha configMapper bug on standalone
configs blocks the UI dashboard). Re-verify hl-0sr.24 after the bump; close if resolved.
D3 — Doc corrections (the “get honest” paperwork)
Section titled “D3 — Doc corrections (the “get honest” paperwork)”- Spec
2026-06-06-llm-gw-mcp-gw-migration-design.md: add a dated correction note at D3/D10/D13 pointing to this spec and the ADR addendum — agentgateway is the JWT enforcement point + serves the PRM; DCR is not supported against Authentik (mock-DCR structurally unusable: no provider adapter + noregistration_endpoint; real DCR-proxy is provider-gated with no Authentik adapter). Staticmcp-publicpre-provisioning is the cluster standard. D10 alpha-override superseded by the GA pin. - ADR
hl-23m: append a dated Addendum recording the reversal — the mock-DCR short-circuit was found structurally unusable against Authentik at execution time (source-level, 2026-06-07); the design reverts to static-client “model B”; real DCR-proxy remains contingent (see Future). Preserves the audit trail (original decision and empirical reversal), matching the existing v1.2.1→alpha addendum pattern. - Ops/client doc (
docs/reference/services.mdclient-config section, perhl-bb8): state plainly — MCP clients must be pre-configured withmcp-public; DCR is unavailable until Authentik 2026.8.0 ships and agentgateway gains Authentik adapter support (or Authentik’s DCR metadata proves spec-compliant without an adapter). Also bump the stale chart-version reference atdocs/reference/services.md:277(1.3.0-alpha.1→1.3.1) so the docs match the D2 pin — this file is already touched by this bullet, so it is a free fix, but call it out so the plan doesn’t miss it.
Future (contingent — recorded, not promised)
Section titled “Future (contingent — recorded, not promised)”Real DCR-proxy requires all of:
- (a) Authentik
2026.8.0native DCR shipping (milestoned “Required”, ~Aug 2026), and - (b) either an agentgateway Authentik
provideradapter (does not exist, not planned) or Authentik’s DCR + AS-metadata being spec-compliant at the default path with no CORS/metadata quirk requiring an adapter.
(b) is the open unknown — verify at the 2026.8.0 ship date. Until then, static
mcp-public is the cluster standard and no clientId config is carried. A deferred bead
(hl-0sr.29) is filed to re-evaluate DCR post-2026.8.0 so this is not lost.
Files modified
Section titled “Files modified”argocd/app-configs/agentgateway/mcp-engram.yaml— removeclientId+ comment (D1).argocd/cluster-app/templates/agentgateway-controller.yaml— twotargetRevision(D2).argocd/app-configs/agentgateway-controller/values-controller.yaml— imagetag+ comment (D2).argocd/app-configs/agentgateway/parameters.yaml— imagetag+ comment (D2).docs/engineering/specs/2026-06-06-llm-gw-mcp-gw-migration-design.md— correction note at D3/D10/D13 (D3).docs/adr/hl-23m-agentgateway-is-mcp-auth-enforcement-point-dcr-short-circuit.md— Addendum (D3).docs/reference/services.md— client-config DCR note (D3).
Verification
Section titled “Verification”jj diff --gitshows only the files above;clientIdremoved, four pins at1.3.1, comments rewritten.kubectl --context fzymgc-house apply --dry-run=client -k argocd/app-configs/agentgatewaysucceeds (manifests still valid afterclientIdremoval).docker run --rm -v <final-config.yaml>:/c.yaml ghcr.io/agentgateway/agentgateway:v1.3.1 --validate-only -f /c.yamlexits 0 (config valid on the GA binary; uses the--validate-onlyform per hl-0sr.1).- Post-merge ArgoCD sync:
mcp-engram-oauthpolicy reconciles with noclientId; engram MCP athttps://mcp-gw.fzymgc.house/mcp/engramstill 401-without-token / 200-with-token (staticmcp-publicflow unchanged — re-run the hl-0sr.17 client round-trip). - UI dashboard (
hl-0sr.24): re-check whether the v1.3.1 GAconfigMapperbug is resolved; closehl-0sr.24if so.
- GA
clientIdremoval is safe — the field was inert (unreachable). The Strict JWT validation + PRM are independent ofclientIdand remain the enforcement + discovery surface. - GA bump carries the usual chart/CRD reconciliation risk — the controller app uses
ServerSideApply+RespectIgnoreDifferencesfor the large CRDs, andsync-wave: -1orders CRDs before the gateway app. Watch the ArgoCD sync for CRD-drift on theAgentgatewayPolicyCRD; theignoreDifferencesforlast-applied-configurationalready covers the known large-CRD annotation overflow. - The mock-DCR removal forecloses DCR-requiring clients until the Future conditions
land. Accepted: no such client exists today, and the static-client path is the
documented standard. Tracked by
hl-0sr.29.