feat(client,core): application_type client metadata with native/web inference (SEP-837)#2266
feat(client,core): application_type client metadata with native/web inference (SEP-837)#2266mattzcarey wants to merge 2 commits into
Conversation
…nference (SEP-837) - Add optional application_type to OAuthClientMetadataSchema (and thus OAuthClientInformationFullSchema) so user-supplied values survive DCR request bodies and response parsing. - registerClient() infers application_type when absent: 'native' if every redirect URI is loopback (localhost, 127.0.0.1, [::1]) or a custom non-http(s) scheme, otherwise 'web'. Explicit values are never overridden. - New inferApplicationType(redirectUris) export. - JSDoc on OAuthClientProvider.clientMetadata + docs/client.md note. Closes #2198
🦋 Changeset detectedLatest commit: 7f811c4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@modelcontextprotocol/client
@modelcontextprotocol/codemod
@modelcontextprotocol/server
@modelcontextprotocol/server-legacy
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
…ted-failures baseline The application_type implementation makes the 13 scenarios that failed only on the SEP-837 DCR checks pass, and the conformance runner fails on stale baseline entries. auth/scope-step-up stays: it still emits a SEP-2350 WARNING (scope union on re-authorization), which the evaluator counts as a failure.
There was a problem hiding this comment.
Needs a rebase first (conflicting with main), and the conflict is in test/conformance/expected-failures.yaml. Main is now on conformance 0.2.0-alpha.3; this branch still pins alpha.1. As part of the rebase, two conformance things:
- Bump the pin to
0.2.0-alpha.3and reconcile the baseline. - Drop
auth/scope-step-upfromexpected-failures.yaml.
Once #2265 is in main (scope union plus the harness fix) and application_type is here, auth/scope-step-up passes end to end.
I verified it goes 28/28 with both SEPs in, so it should come off the baseline with this PR once #2265 is fixed & landed and we rebased this one on top.
Summary
Implements SEP-837 (clarify client type requirements) for dynamic client registration. The draft authorization spec,
basic/authorization/client-registration.mdx§ "Application Type and Redirect URI Constraints", is MUST-level:Previously the SDK made compliance impossible:
OAuthClientMetadataSchemauses.strip()and had noapplication_type, so any user-supplied value was silently dropped from DCR request bodies, andOAuthClientInformationFullSchemastripped it from registration responses.What changed
@modelcontextprotocol/core(packages/core/src/shared/auth.ts):OAuthClientMetadataSchemagains optionalapplication_type(typedz.string()because OIDC permits extension values beyond'web'/'native'; JSDoc documents the standard values and SEP-837 semantics). SinceOAuthClientInformationFullSchemamerges the metadata schema, the field now also survives DCR response parsing.@modelcontextprotocol/client(packages/client/src/client/auth.ts):registerClient()infersapplication_typewhen it is absent from the provided metadata:'native'if everyredirect_urisentry is loopback (localhost,127.0.0.1,[::1]) or a custom non-http(s) scheme; otherwise'web'. The rule lives in a new exported helperinferApplicationType(redirectUris). Empty/unparseable inputs conservatively yield'web'(the OIDC default).OAuthClientProvider.clientMetadataand a short note indocs/client.md.native, custom scheme →native, https →web, mixed →web,localhost.example.comis not loopback, explicit value never overridden).clientminor (new export + behavior),corepatch (additive optional schema field).Design choice for maintainers
Inference only applies when the field is absent — an explicitly provided
application_typeis never overridden. Happy to drop the inference entirely and make this docs-only (schema field + JSDoc) if you'd prefer registration bodies stay byte-for-byte what the provider supplies.Validation
pnpm build:all,pnpm typecheck:all,pnpm lint:all— clean@modelcontextprotocol/clienttests: 379/379 pass (11 files)@modelcontextprotocol/coretests: 463/463 pass (22 files)pnpm run test:conformance:client:all): the 13 scenarios that failed only on the SEP-837application_typeDCR check now pass, so they are removed fromtest/conformance/expected-failures.yaml(the runner fails on stale entries).auth/scope-step-upstays in the baseline for an unrelated SEP-2350 scope-union WARNING.Downstream impact: cloudflare/agents'
DurableObjectOAuthClientProvideruses remote https redirect URLs on Workers → inferred'web', which is correct; no changes needed downstream.Closes #2198