Repository navigation
feat(mcp): RFC 7591 Dynamic Client Registration for the consent proxy - #2710
Merged
Merged
Conversation
Native-HTTP OAuth MCP clients (Claude Code/Desktop, Joule Work Desktop) bootstrap via DCR — they POST to a registration_endpoint to get a client_id. The consent proxy had none (discovery registration_endpoint=null, POST /mcp-oauth/register 404), so the advertised "native HTTP, no bridge" path failed; the only working path was the mcp-remote bridge with an explicit, env-specific client_id (DEV 0b1e8b56…, PROD 5a7cb5fd…) — the fragility behind the multi-day PROD aud-mismatch saga. Add a facade DCR endpoint. /mcp-oauth/register returns THIS environment's existing tutorials-identity public PKCE client_id, read from the bound VCAP_SERVICES.identity[].credentials.clientid (resolveIasClientId). That is the same clientid tutorials-srv-mcp validates token aud against, so native-HTTP clients get the correct per-env id automatically — eliminating the wrong-env client_id failure class by construction. No IAS client is created; no secret or registration_access_token is minted (public client). The client's redirect_uris are echoed for the client↔proxy leg only (validated at /authorize); the proxy↔IAS leg always uses our fixed callback. If no identity binding is present, /register returns 500 (never guesses). Gated by the existing MCP_CONSENT_ENABLED flag. - approuter/lib/mcp-consent.js: REGISTER_PATH, resolveIasClientId(), handleRegister(), dispatch + exports - approuter/lib/well-known-oauth.js: advertise registration_endpoint under the flip - docs/end-users/mcp-quickstart.md + hugo/content/api-docs/_index.md: native-HTTP/DCR path now works (no client_id); reverse the stale "XSUAA does not support DCR" notes; mcp-remote + per-env client_id kept as the fallback - tests: +5 DCR (mcp-consent) +2 discovery (well-known); 60 green
jung-thomas
marked this pull request as ready for review
October 11, 2026 15:55
This was referenced Oct 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a facade Dynamic Client Registration (RFC 7591) endpoint to the MCP consent proxy, so native-HTTP OAuth clients (Claude Code/Desktop, Joule Work Desktop) connect with zero config and no
client_id.Why
Native-HTTP OAuth MCP clients bootstrap via DCR — they POST to a
registration_endpointto obtain aclient_id. The consent proxy had none (registration_endpoint: null,POST /mcp-oauth/register→ 404), so the "native HTTP, no bridge" path the API-docs page advertises actually failed. The only working path was themcp-remotebridge with an explicit, environment-specificclient_id(DEV0b1e8b56…, PROD5a7cb5fd…) — the exact fragility behind the recent multi-day PRODaud-mismatch debugging.How (facade, not pass-through)
/mcp-oauth/registerreturns this environment's existingtutorials-identitypublic PKCEclient_id, read from the boundVCAP_SERVICES.identity[].credentials.clientid. That's the same clientidtutorials-srv-mcpvalidates tokenaudagainst — so native-HTTP clients get the correct per-env id automatically, eliminating the wrong-envclient_idfailure class by construction.client_secret/registration_access_tokenminted (public client).redirect_urisechoed for the client↔proxy leg only (validated at/authorize); the proxy↔IAS leg always uses our fixed callback — registration input can't influence it.MCP_CONSENT_ENABLEDflag.Changes
approuter/lib/mcp-consent.js:REGISTER_PATH,resolveIasClientId(),handleRegister(), dispatch + exportsapprouter/lib/well-known-oauth.js: advertiseregistration_endpointunder the consent flipmcp-remote+ per-env client_id kept as fallbackVerification
Unit green. Post-DEV-deploy:
curl .../.well-known/oauth-authorization-server | jq .registration_endpoint→…/mcp-oauth/register;POST .../mcp-oauth/register→ 201 with DEV's0b1e8b56…, no secret. End-to-end: a native-HTTP OAuth MCP client (no--static-oauth-client-info) connects to/mcp-auth/api— self-registers, consents, signs in, connects. Then PROD after promotion. (Themcp-remote+ explicit-client_id path still works — back-compat.)