Title: Bind client credentials to their authorization server (SEP-2352) by Kludex · Pull Request #2933 · modelcontextprotocol/python-sdk · GitHub
Open Graph Title: Bind client credentials to their authorization server (SEP-2352) by Kludex · Pull Request #2933 · modelcontextprotocol/python-sdk
X Title: Bind client credentials to their authorization server (SEP-2352) by Kludex · Pull Request #2933 · modelcontextprotocol/python-sdk
Description: Implements SEP-2352 (the last client-side item of #2902): bind OAuth client credentials to their authorization server and re-register when it changes. What changed Persisted client credentials are now bound to the issuer that registered them. OAuthClientInformationFull gains an SDK-owned issuer field, set after DCR/CIMD. Before reusing stored credentials, the provider compares their recorded issuer to the authorization server discovered from protected resource metadata; on a mismatch it discards the credentials (and the old tokens) and re-registers, instead of presenting one server's client_id to another. CIMD (URL-based client IDs): portable across servers, always match - no re-registration. No recorded issuer (pre-registered, or stored before this change): no binding to enforce, left as-is. No TokenStorage protocol change - the issuer round-trips through the existing get_client_info/set_client_info, so the 8 in-repo implementations and downstream users are unaffected. Follows the Go SDK's approach (issuer field on the client-info, proactive comparison). The TS SDK takes a reactive invalidate-on-error path that doesn't fit our httpx auth-generator cleanly. Conformance The auth/authorization-server-migration scenario's no-reuse-on-as-change and no-cross-as-credential-reuse checks pass (the client never presents the old AS's client_id to the new AS). The reregister-on-as-change check is not yet reached: the scenario runs only at 2026-07-28, where client.py's 2025 stateful lifecycle is rejected (400 on initialize) before the migration 401 fires. The scenario therefore stays baselined in both expected-failures files, re-annotated to point at the stateless-lifecycle gap (the same gap blocking tools_call, scope-step-up, etc. on the 2026 leg) rather than SEP-2352. An in-process interaction test covers the migration end to end. Both conformance legs pass with no stale or unexpected entries. #2902 status This completes the implementation of all five client-side items (SEP-2207, SEP-2468, SEP-837, SEP-2350, SEP-2352). The AS-migration conformance scenario will go green once the 2026 stateless client lifecycle lands. AI Disclaimer This PR was developed with the assistance of either Claude or Codex. I've reviewed and verified the changes.
Open Graph Description: Implements SEP-2352 (the last client-side item of #2902): bind OAuth client credentials to their authorization server and re-register when it changes. What changed Persisted client credentials are ...
X Description: Implements SEP-2352 (the last client-side item of #2902): bind OAuth client credentials to their authorization server and re-register when it changes. What changed Persisted client credentials are ...
Opengraph URL: https://github.com/modelcontextprotocol/python-sdk/pull/2933
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:fbbe5519-0901-353d-99eb-5f0927ac6e9d |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | C04E:2AF61E:43D09:57662:6A5AAE17 |
| html-safe-nonce | 12267505bb162b80743dba1416f83219396ea5d027603f6614ded160959d46fc |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJDMDRFOjJBRjYxRTo0M0QwOTo1NzY2Mjo2QTVBQUUxNyIsInZpc2l0b3JfaWQiOiIyMDc3ODcwMTM3NTA1NzQzMSIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | 398a2203cd58845fa8aaa3f97ecafc1cd0ee83171c96d3b25cbccf03f9669820 |
| hovercard-subject-tag | pull_request:3904528849 |
| 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/2933/files |
| twitter:image | https://avatars.githubusercontent.com/u/7353520?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/7353520?s=400&v=4 |
| og:image:alt | Implements SEP-2352 (the last client-side item of #2902): bind OAuth client credentials to their authorization server and re-register when it changes. What changed Persisted client credentials are ... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | 21c53da6db0c40484841ba32fe19ecf485e7e734334faab487cd59d884d2bb33 |
| 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 | 6e85f20f54e1147d8162094880590864abefff6b |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width