Send application_type during Dynamic Client Registration (SEP-837) (#2930)

This commit is contained in:
Marcelo Trylesinski
2026-06-20 17:19:12 +01:00
committed by GitHub
parent 60f37e9d7c
commit cf41441e44
6 changed files with 35 additions and 38 deletions
@@ -28,32 +28,6 @@ client:
# branch takes the per-request _meta path.
- tools_call
# --- SEP-837 (application_type during DCR) ---
# The sep-837-application-type-present check only fires on 2026-version
# runs; the client omits application_type during Dynamic Client
# Registration, so every auth scenario that reaches DCR fails it on this
# leg (the same scenarios pass at their default version in the 2025 legs).
- auth/metadata-default
- auth/metadata-var1
- auth/metadata-var2
- auth/metadata-var3
- auth/scope-from-www-authenticate
- auth/scope-from-scopes-supported
- auth/scope-omitted-when-undefined
- auth/token-endpoint-auth-basic
- auth/token-endpoint-auth-post
- auth/token-endpoint-auth-none
- auth/offline-access-not-supported
# SEP-2468 (authorization response iss parameter) is implemented, but these
# 2026-introduced scenarios reach DCR and so still fail the application_type
# check above; they unblock with SEP-837, not SEP-2468.
- auth/iss-supported
- auth/iss-not-advertised
- auth/iss-supported-missing
- auth/iss-wrong-issuer
- auth/iss-unexpected
- auth/iss-normalized
# --- Auth scenarios cut short by the 2026 connection lifecycle ---
# The auth fixture flow drives the 2025 stateful lifecycle; the 2026-mode
# mock rejects the MCP POST before the scope-escalation behaviour these
@@ -26,18 +26,6 @@ client:
# SEP-2352 (authorization server migration): client does not re-register when
# PRM authorization_servers changes.
- auth/authorization-server-migration
# SEP-837 (application_type during DCR): the check fires on every non-legacy
# spec version (the default LATEST is 2026-07-28). The client omits
# application_type during Dynamic Client Registration, so every scenario that
# reaches DCR fails it. SEP-2468 iss validation is implemented, so these now
# fail only on the application_type check, not on iss.
- auth/offline-access-not-supported
- auth/iss-supported
- auth/iss-not-advertised
- auth/iss-supported-missing
- auth/iss-wrong-issuer
- auth/iss-unexpected
- auth/iss-normalized
# --- Pre-existing scenarios that fail on checks added after conformance 0.1.15 ---
# SEP-990 (enterprise-managed authorization extension): no fixture handler /
+15
View File
@@ -1303,6 +1303,21 @@ If you relied on extra fields round-tripping through MCP types, move that data i
## New Features
### OAuth Dynamic Client Registration sends `application_type` (SEP-837)
`OAuthClientMetadata` now carries an `application_type` field that is sent during Dynamic Client Registration. It defaults to `"native"`, which suits MCP clients that use loopback redirect URIs (CLI and desktop apps); browser-based clients served from a non-local host should set it to `"web"`:
```python
from mcp.shared.auth import OAuthClientMetadata
client_metadata = OAuthClientMetadata(
redirect_uris=["https://app.example.com/callback"],
application_type="web",
)
```
Under OIDC, omitting `application_type` defaults to `"web"`, which an authorization server may reject for the `localhost` redirect URIs native clients use; sending `"native"` avoids that. Non-OIDC servers ignore the parameter.
### 2025-11-25 and 2026-07-28 protocol fields modeled
`mcp.types` models the 2025-11-25 and 2026-07-28 protocol fields (e.g. `resultType`, `ttlMs`/`cacheScope` on cacheable results, `inputResponses`/`requestState` on retried requests), so inbound payloads carrying these keys parse into typed fields and round-trip. `ttlMs`/`cacheScope` default to `0`/`"private"` (immediately stale, not shared-cacheable); `resultType` defaults to `"complete"` on concrete results (`None` on `EmptyResult`); the server strips all of them from the wire at pre-2026 versions.
+3
View File
@@ -67,6 +67,9 @@ class OAuthClientMetadata(BaseModel):
# servers may also return additional types they support
response_types: list[str] = ["code"]
scope: str | None = None
# SEP-837: OIDC application_type. Defaults to "native" since MCP clients typically use
# loopback redirect URIs; set "web" for remote browser-based clients on a non-local host.
application_type: Literal["web", "native"] = "native"
# these fields are currently unused, but we support & store them for potential
# future use
+16
View File
@@ -1,6 +1,7 @@
"""Tests for refactored OAuth client authentication implementation."""
import base64
import json
import time
from unittest import mock
from urllib.parse import parse_qs, quote, unquote, urlparse
@@ -1049,6 +1050,21 @@ class TestCreateClientRegistrationRequest:
assert request.method == "POST"
def test_registration_request_sends_application_type():
"""SEP-837: the DCR body carries application_type, defaulting to native and overridable."""
redirect_uris: list[AnyUrl] = [AnyUrl("http://localhost:3000/callback")]
default_request = create_client_registration_request(
None, OAuthClientMetadata(redirect_uris=redirect_uris), "https://auth.example.com"
)
assert json.loads(default_request.content)["application_type"] == "native"
web_request = create_client_registration_request(
None, OAuthClientMetadata(redirect_uris=redirect_uris, application_type="web"), "https://auth.example.com"
)
assert json.loads(web_request.content)["application_type"] == "web"
class TestAuthFlow:
"""Test the auth flow in httpx."""
+1
View File
@@ -203,6 +203,7 @@ async def test_the_dcr_request_carries_the_client_metadata() -> None:
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"scope": "mcp",
"application_type": "native",
"client_name": "interaction-suite",
"software_id": "interaction-test-suite",
}