Title: [v1.x] fix(auth): coerce empty-string optional URL fields to None in OAuthClientMetadata by felixweinberger · Pull Request #2405 · modelcontextprotocol/python-sdk · GitHub
Open Graph Title: [v1.x] fix(auth): coerce empty-string optional URL fields to None in OAuthClientMetadata by felixweinberger · Pull Request #2405 · modelcontextprotocol/python-sdk
X Title: [v1.x] fix(auth): coerce empty-string optional URL fields to None in OAuthClientMetadata by felixweinberger · Pull Request #2405 · modelcontextprotocol/python-sdk
Description: Coerce "" to None for the five OPTIONAL URL fields on OAuthClientMetadata so that DCR responses from servers that echo omitted metadata as empty strings parse successfully. v1.x backport of the same change targeting main (#2404). Motivation and Context RFC 7591 §2 marks client_uri, logo_uri, tos_uri, policy_uri, and jwks_uri as OPTIONAL. Some authorization servers in the wild (observed: Udemy, deepsense.ai, Apify, windsor.ai, firecrawl, and others) echo the client's omitted metadata back as "" instead of dropping the keys: {"client_id": "abc123", "client_uri": "", "logo_uri": "", "tos_uri": "", "policy_uri": "", ...} AnyHttpUrl rejects "", so handle_registration_response raises ValidationError and callers discard an otherwise valid registration — the server returned 201 with a real client_id. The servers are technically non-compliant (an empty string is not a valid URL), but the only reasonable interpretation of "" for an OPTIONAL field is "absent". This applies Postel's law at the deserialization boundary, matching the existing normalize_token_type validator on OAuthToken in the same file. Because OAuthClientInformationFull inherits from OAuthClientMetadata, the coercion applies to parsed DCR responses as well. How Has This Been Tested? New TestOAuthClientMetadataEmptyUrlCoercion covers per-field coercion (parametrized over all five fields), all-fields-together, valid-URL passthrough, inheritance through OAuthClientInformationFull (the DCR-response model), and a negative case confirming non-empty invalid URLs still raise. Breaking Changes None. This only widens accepted input — previously-rejected "" values now succeed as None. Valid URLs, None, and omitted keys are unchanged. Types of changes Bug fix (non-breaking change which fixes an issue) New feature (non-breaking change which adds functionality) Breaking change (fix or feature that would cause existing functionality to change) Documentation update Checklist I have read the MCP Documentation My code follows the repository's style guidelines New and existing tests pass locally I have added appropriate error handling I have added or updated documentation as needed Additional context RFC 7591 §2: https://datatracker.ietf.org/doc/html/rfc7591#section-2
Open Graph Description: Coerce "" to None for the five OPTIONAL URL fields on OAuthClientMetadata so that DCR responses from servers that echo omitted metadata as empty strings parse successfully. v1.x backport ...
X Description: Coerce "" to None for the five OPTIONAL URL fields on OAuthClientMetadata so that DCR responses from servers that echo omitted metadata as empty strings parse successfully. v1.x b...
Opengraph URL: https://github.com/modelcontextprotocol/python-sdk/pull/2405
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:6ae24d22-560b-77ed-2728-79c06dcfcec2 |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | EB54:4EA58:847982:BCF981:6A5F296D |
| html-safe-nonce | 0400b0601e94dd5ca7b4e3b5e5e621b8cc2d4a12f17b950c870a6e637977936f |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJFQjU0OjRFQTU4Ojg0Nzk4MjpCQ0Y5ODE6NkE1RjI5NkQiLCJ2aXNpdG9yX2lkIjoiODM3ODQ5NzQ2NDA4NjI0MzY5MyIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | c68190955d9fb91f9e42a9b4c0f82e117121766e950fddec46b25b542588803f |
| hovercard-subject-tag | pull_request:3504296149 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/modelcontextprotocol/python-sdk/pull/2405/files |
| twitter:image | https://avatars.githubusercontent.com/u/3823880?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/3823880?s=400&v=4 |
| og:image:alt | Coerce "" to None for the five OPTIONAL URL fields on OAuthClientMetadata so that DCR responses from servers that echo omitted metadata as empty strings parse successfully. v1.x backport ... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | af57322612a2efc3d6fee860947245ffbb465e4e3f9eb5dfefc0f770217aa0ca |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/modelcontextprotocol/python-sdk git https://github.com/modelcontextprotocol/python-sdk.git |
| octolytics-dimension-user_id | 182288589 |
| octolytics-dimension-user_login | modelcontextprotocol |
| octolytics-dimension-repository_id | 862584018 |
| octolytics-dimension-repository_nwo | modelcontextprotocol/python-sdk |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 862584018 |
| octolytics-dimension-repository_network_root_nwo | modelcontextprotocol/python-sdk |
| turbo-body-classes | logged-out env-production page-responsive full-width |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 93fabb5edc1b64676979fa226fed14ab56431f54 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width