Title: draft: MRTR (SEP-2322) lowlevel plumbing + handler-shape comparison by maxisbey · Pull Request #2322 · modelcontextprotocol/python-sdk · GitHub
Open Graph Title: draft: MRTR (SEP-2322) lowlevel plumbing + handler-shape comparison by maxisbey · Pull Request #2322 · modelcontextprotocol/python-sdk
X Title: draft: MRTR (SEP-2322) lowlevel plumbing + handler-shape comparison by maxisbey · Pull Request #2322 · modelcontextprotocol/python-sdk
Description: Draft implementation of Multi Round-Trip Requests (SEP-2322) for the Python SDK. Two commits: lowlevel plumbing, then the handler-shape comparison deck. Counterpart to typescript-sdk#1701 — same weather-lookup tool throughout, so the diff between option files is the argument. Unlike the TS demos, the lowlevel plumbing here is real (not smuggled through JSON text blocks); every option round-trips IncompleteResult through the actual wire protocol. Commit 1: types + lowlevel + client retry loop Where Shape Types src/mcp/types/_types.py IncompleteResult (discriminated by result_type), InputRequest/InputResponse unions, input_responses+request_state folded into RequestParams Server src/mcp/server/lowlevel/server.py on_call_tool return widened to CallToolResult | IncompleteResult | CreateTaskResult Shared src/mcp/shared/session.py send_request accepts TypeAdapter via overload — enables union result parsing Session src/mcp/client/session.py call_tool_mrtr() returns the union; call_tool() stays narrow, raises clearly on IncompleteResult Client src/mcp/client/client.py call_tool() drives the retry loop internally — dispatches embedded input requests to elicitation_callback/sampling_callback/list_roots_callback, retries with collected responses + echoed request_state. max_mrtr_rounds=8 bound. The client-side delta from today's code is zero: elicitation_callback is the same function whether it fires from SSE push or MRTR retry. Commit 2: handler-shape comparison SDK primitives in src/mcp/server/experimental/mrtr.py: MrtrCtx.once(key, fn) — idempotency guard tracked in request_state (Option F) ToolBuilder — incomplete_step(...).end_step(...).build(); end_step runs exactly once regardless of round count (Option G) input_response(params, key) — sugar for the guard-first pattern sse_retry_shim() + dispatch_by_version() — comparison artifacts for A/D Option examples in examples/servers/mrtr-options/: Author writes SDK does Hidden re-entry Old client gets E MRTR-native only Nothing No Result w/ default, or error A MRTR-native only Retry-loop over SSE Yes, safe Full elicitation B await elicit() Exception → IncompleteResult Yes, unsafe Full elicitation C One handler, if version branch Version accessor No Full elicitation D Two handlers Picks by version No Full elicitation F MRTR-native + ctx.once wraps once() guard in request_state No (same as E) G Step functions + .build() Step-tracking in request_state No (same as E) Testing tests/experimental/test_mrtr.py parametrises E/F/G against the same Client + callback to prove identical wire behaviour — the server's internal choice doesn't leak. The footgun test measures audit_log count: naive handler fires twice for one tool call, F and G fire once. tests/client/test_client.py has 8 new E2E tests covering the retry loop (single-round elicitation, multi-round with request_state accumulation, sampling/roots dispatch, round-limit, missing-callback error paths). Not in scope Persistent/Tasks workflow — ServerTaskContext already does input_required; MRTR integration is a separate PR mrtrOnly client flag — trivial to add, not demoed requestState HMAC signing — called out in code comments; demos use plain base64-JSON High-level MCPServer integration (@server.tool decorator shape) — lowlevel-first, this PR stops at Server Exploratory — not intended to merge as-is. Open questions: which of F/G (or both) to ship as SDK primitives, whether to keep call_tool_mrtr as public or fold the union into call_tool once SEP finalises, whether sse_retry_shim belongs in the SDK at all vs docs-only. 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
Open Graph Description: Draft implementation of Multi Round-Trip Requests (SEP-2322) for the Python SDK. Two commits: lowlevel plumbing, then the handler-shape comparison deck. Counterpart to typescript-sdk#1701 — same we...
X Description: Draft implementation of Multi Round-Trip Requests (SEP-2322) for the Python SDK. Two commits: lowlevel plumbing, then the handler-shape comparison deck. Counterpart to typescript-sdk#1701 — same we...
Opengraph URL: https://github.com/modelcontextprotocol/python-sdk/pull/2322
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:68b7a632-2b29-b75e-865a-4a0fb74fc436 |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | 99CA:227DF9:84594B:BC9DF6:6A5A0B61 |
| html-safe-nonce | 7ac3c3b13cf3f098c28b2c67151afdb61667a2df3c5d03381b62e9f886a39c33 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5OUNBOjIyN0RGOTo4NDU5NEI6QkM5REY2OjZBNUEwQjYxIiwidmlzaXRvcl9pZCI6IjQwOTcwMDYzNTgxMjE1NDg2NDEiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | f980bc633cb7474edb969b85e45401d5cf1435e11d2b52c60afaba4a3c25f476 |
| hovercard-subject-tag | pull_request:3425717281 |
| 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/2322/files |
| twitter:image | https://avatars.githubusercontent.com/u/224885523?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/224885523?s=400&v=4 |
| og:image:alt | Draft implementation of Multi Round-Trip Requests (SEP-2322) for the Python SDK. Two commits: lowlevel plumbing, then the handler-shape comparison deck. Counterpart to typescript-sdk#1701 — same we... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | 8c2fe7351e4e987cb8f75e2425a0b895d807779bc4c2d3c312be0a54082219f7 |
| 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 | 491710c7a8eecca9d0a06bd157646f2685d8c87b |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width