diff --git a/packages/client/src/client/auth.ts b/packages/client/src/client/auth.ts index a4d5b14c62..eac23d828c 100644 --- a/packages/client/src/client/auth.ts +++ b/packages/client/src/client/auth.ts @@ -244,6 +244,13 @@ export interface OAuthClientProvider { /** * Metadata about this OAuth client. + * + * Per the MCP authorization specification (SEP-837), clients MUST specify an + * appropriate `application_type` when registering dynamically: `'native'` for + * desktop/CLI apps using loopback or custom-scheme redirect URIs, `'web'` for + * remote browser-based apps. If `application_type` is omitted, {@linkcode registerClient} + * infers `'native'` when any `redirect_uris` entry uses a loopback host or custom + * URI scheme; otherwise it infers `'web'`. */ get clientMetadata(): OAuthClientMetadata; @@ -818,9 +825,9 @@ export function assertSecureTokenEndpoint(tokenEndpoint: string | URL): URL { /** * Derives an OIDC `application_type` from a client's registered redirect URIs - * when the consumer has not set one explicitly (SEP-837). Loopback hosts and - * non-`http(s)` custom URI schemes indicate a native application (RFC 8252); - * everything else is treated as a web application. The result is a heuristic + * when the consumer has not set one explicitly (SEP-837). Any loopback host or + * non-`http(s)` custom URI scheme indicates a native application (RFC 8252); + * otherwise the client is treated as a web application. The result is a heuristic * default — callers that know better should set `clientMetadata.application_type` * themselves, which {@linkcode resolveClientMetadata} never overwrites. * @@ -855,8 +862,8 @@ function deriveApplicationType(redirectUris: readonly string[] | undefined): 'na * `redirectUrl`) get no `grant_types` default. This default applies to the * Dynamic Client Registration body only — it does **not** drive * {@linkcode determineScope}'s `offline_access` augmentation. - * - `application_type` defaults from `redirect_uris`: loopback redirect hosts - * and custom URI schemes → `'native'`, otherwise `'web'` (SEP-837 / RFC 8252). + * - `application_type` defaults from `redirect_uris`: any loopback redirect host + * or custom URI scheme → `'native'`, otherwise `'web'` (SEP-837 / RFC 8252). * * A field the consumer set explicitly is **never** overwritten. {@linkcode auth} * calls this once at the top of the flow; direct callers of diff --git a/packages/core-internal/src/shared/auth.ts b/packages/core-internal/src/shared/auth.ts index e21076d817..6367909c3b 100644 --- a/packages/core-internal/src/shared/auth.ts +++ b/packages/core-internal/src/shared/auth.ts @@ -183,18 +183,28 @@ export const OptionalSafeUrlSchema = SafeUrlSchema.optional().or(z.literal('').t export const OAuthClientMetadataSchema = z .object({ redirect_uris: z.array(SafeUrlSchema), - token_endpoint_auth_method: z.string().optional(), - grant_types: z.array(z.string()).optional(), - response_types: z.array(z.string()).optional(), /** - * OIDC Dynamic Client Registration `application_type`. MCP clients MUST set - * this to `'native'` or `'web'` when registering (SEP-837); the SDK defaults - * it from `redirect_uris` when omitted. Typed as `string` (not an enum) so - * that parsing an authorization server's registration response — which under - * RFC 7591 may echo extension values — never rejects the document on this - * field alone. + * OpenID Connect Dynamic Client Registration `application_type`. + * + * The standard values are `'web'` and `'native'`. OIDC-based authorization + * servers default this to `'web'` when omitted, which conflicts with + * native/loopback redirect URIs (e.g. `http://localhost`, `http://127.0.0.1`, + * or custom URI schemes) and can cause registration to fail. Per the MCP + * authorization specification (SEP-837), clients MUST specify an appropriate + * `application_type` when registering: native apps (desktop, CLI, anything + * using loopback or custom-scheme redirects) SHOULD use `'native'`, while + * remote browser-based apps SHOULD use `'web'`. Authorization servers that + * do not implement OIDC registration ignore this field. + * + * Typed as a plain string because OIDC permits extension values beyond + * `'web'` and `'native'`. + * + * @see https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata */ application_type: z.string().optional(), + token_endpoint_auth_method: z.string().optional(), + grant_types: z.array(z.string()).optional(), + response_types: z.array(z.string()).optional(), client_name: z.string().optional(), client_uri: SafeUrlSchema.optional(), logo_uri: OptionalSafeUrlSchema,