Drive multi round-trip input_required results on the client per SEP-2322 - #500
Open
koic wants to merge 1 commit into
Open
Drive multi round-trip input_required results on the client per SEP-2322#500koic wants to merge 1 commit into
input_required results on the client per SEP-2322#500koic wants to merge 1 commit into
Conversation
…-2322 ## Motivation and Context Final step of SEP-2322 (modelcontextprotocol/modelcontextprotocol#2322) for the 2026-07-28 MCP spec release. The client so far only recognized `input_required` results by raising `InputRequiredError`; this adds the resume loop, mirroring the Python SDK's high-level `Client` driver and the TypeScript SDK's `runInputRequiredDriver`. - `MCP::Client.new` gains `elicitation_handler:`, `sampling_handler:`, and `roots_handler:` callbacks plus `input_required_max_rounds:` (default 10, the TypeScript and Python default). Handlers receive the embedded request's params and return the corresponding result shape; a decline/cancel elicitation result is a valid answer and flows back to the server as-is. - With at least one handler configured, `call_tool`, `get_prompt`, and `read_resource` resume automatically: each `inputRequests` entry is fulfilled by the matching handler and the ORIGINAL request is re-issued with `inputResponses` under the same keys plus the byte-exact echoed `requestState`, on a fresh JSON-RPC id per leg (modern envelope stamping happens in the transports per send, so every leg carries the SEP-2575 triple). A `requestState`-only result (load shedding) retries after an exponential backoff from 50ms to a 250ms cap, matching the Python SDK; every leg counts against the round cap, whose exhaustion raises `InputRequiredError` carrying the last result. - A requested kind without a matching handler falls back to the manual path by raising `InputRequiredError`, and clients without any handlers keep the exact pre-existing behavior. For manual driving, `call_tool` / `get_prompt` / `read_resource` gain `input_responses:` and `request_state:` keyword arguments that ride at the params top level. Non-MRTR methods such as `tools/list` keep raising unconditionally. The conformance client rides the same change: it derives its lifecycle from the wire version the harness names per run (`MCP_CONFORMANCE_PROTOCOL_VERSION`), connecting with `mode: :modern` for the 2026 draft and keeping the pinned legacy handshake through 2025-11-25, retrying a modern connect once when the server rejects the first request with `-32022` naming its supported versions. New scenario branches drive `sep-2322-client-request-state` through this driver (an accepting `elicitation_handler` plus the four `test_mrtr_*` tools), `request-metadata`, both SEP-2243 header scenarios, and `json-schema-ref-no-deref`. The expected-failures baseline drops the six scenarios this stack resolves, leaving only the not-scored extensions and the post-anchor `json-schema-2020-12-preservation`. Verified end-to-end over stdio: a modern connection (`connect(mode: :modern)` with declared form-elicitation capability) against a server whose tool returns `InputRequiredResult` under `RequestStateSecurity` sealing completes in two legs with the handler supplying the answer, and a handler-less client receives `InputRequiredError` with the sealed opaque state. Part of modelcontextprotocol#382. ## How Has This Been Tested? New tests in `test/mcp/client_test.rb` (Mocha sequenced transports) cover: the two-leg happy path asserting preserved original params, key-matched `inputResponses`, byte-exact `requestState` echo, and a fresh id per leg; the exponential backoff sequence for `requestState`-only legs (stubbed `sleep`); round-cap exhaustion raising `InputRequiredError` with the last state; the unhandled-kind fallback; the manual `input_responses:`/`request_state:` keyword arguments; and the `prompts/get` driver path. The existing SEP-2322 recognition tests pin the no-handler behavior unchanged. `bundle exec rake` (tests, RuboCop, and conformance baseline) passes, plus the stdio end-to-end script described above. With the separate SEP-2243 mirroring change applied alongside, every scored scenario of the frozen 2026-07-28 client leg passes (`sep-2322-client-request-state` 5/5, `request-metadata` 5/5 with no version-retry warning, `http-custom-headers` 18/18, `http-invalid-tool-headers` 11/11), and the `--requirements` client legs at both revisions report failures only in not-scored scenarios. The full conformance task passes its baseline check on both legs. ## Breaking Changes None. All new constructor and method keyword arguments default to `nil`, and without configured handlers every code path is identical to before.
koic
force-pushed
the
client_input_required_driver
branch
from
August 10, 2026 03:39
2d30e14 to
54fcff7
Compare
input_required Results on the Legacy Wire per SEP-2322input_required results on the client per SEP-2322
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation and Context
Final step of SEP-2322 (modelcontextprotocol/modelcontextprotocol#2322) for the 2026-07-28 MCP spec release. The client so far only recognized
input_requiredresults by raisingInputRequiredError; this adds the resume loop, mirroring the Python SDK's high-levelClientdriver and the TypeScript SDK'srunInputRequiredDriver.MCP::Client.newgainselicitation_handler:,sampling_handler:, androots_handler:callbacks plusinput_required_max_rounds:(default 10, the TypeScript and Python default). Handlers receive the embedded request's params and return the corresponding result shape; a decline/cancel elicitation result is a valid answer and flows back to the server as-is.call_tool,get_prompt, andread_resourceresume automatically: eachinputRequestsentry is fulfilled by the matching handler and the ORIGINAL request is re-issued withinputResponsesunder the same keys plus the byte-exact echoedrequestState, on a fresh JSON-RPC id per leg (modern envelope stamping happens in the transports per send, so every leg carries the SEP-2575 triple). ArequestState-only result (load shedding) retries after an exponential backoff from 50ms to a 250ms cap, matching the Python SDK; every leg counts against the round cap, whose exhaustion raisesInputRequiredErrorcarrying the last result.InputRequiredError, and clients without any handlers keep the exact pre-existing behavior. For manual driving,call_tool/get_prompt/read_resourcegaininput_responses:andrequest_state:keyword arguments that ride at the params top level. Non-MRTR methods such astools/listkeep raising unconditionally.The conformance client rides the same change: it derives its lifecycle from the wire version the harness names per run
(
MCP_CONFORMANCE_PROTOCOL_VERSION), connecting withmode: :modernfor the 2026 draft and keeping the pinned legacy handshake through 2025-11-25, retrying a modern connect once when the server rejects the first request with-32022naming its supported versions. New scenario branches drivesep-2322-client-request-statethrough this driver (an acceptingelicitation_handlerplus the fourtest_mrtr_*tools),request-metadata, both SEP-2243 header scenarios, andjson-schema-ref-no-deref. The expected-failures baseline drops the six scenarios this stack resolves, leaving only the not-scored extensions and the post-anchorjson-schema-2020-12-preservation.Verified end-to-end over stdio: a modern connection (
connect(mode: :modern)with declared form-elicitation capability) against a server whose tool returnsInputRequiredResultunderRequestStateSecuritysealing completes in two legs with the handler supplying the answer, and a handler-less client receivesInputRequiredErrorwith the sealed opaque state.Part of #382.
How Has This Been Tested?
New tests in
test/mcp/client_test.rb(Mocha sequenced transports) cover: the two-leg happy path asserting preserved original params, key-matchedinputResponses, byte-exactrequestStateecho, and a fresh id per leg; the exponential backoff sequence forrequestState-only legs (stubbedsleep); round-cap exhaustion raisingInputRequiredErrorwith the last state; the unhandled-kind fallback; the manualinput_responses:/request_state:keyword arguments; and theprompts/getdriver path. The existing SEP-2322 recognition tests pin the no-handler behavior unchanged.bundle exec rake(tests, RuboCop, and conformance baseline) passes, plus the stdio end-to-end script described above.With the separate SEP-2243 mirroring change applied alongside, every scored scenario of the frozen 2026-07-28 client leg passes (
sep-2322-client-request-state5/5,request-metadata5/5 with no version-retry warning,http-custom-headers18/18,http-invalid-tool-headers11/11), and the--requirementsclient legs at both revisions report failures only in not-scored scenarios. The full conformance task passes its baseline check on both legs.Breaking Changes
None. All new constructor and method keyword arguments default to
nil, and without configured handlers every code path is identical to before.