Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions packages/client/src/client/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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
Expand Down
28 changes: 19 additions & 9 deletions packages/core-internal/src/shared/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading