## 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`. - The driver answers embedded requests with the handlers already registered through `on_elicitation`, `on_sampling`, and the new `on_roots`. There is no second registration path: a server can ask for input two ways, as a real request mid-call and as an entry in an `input_required` result, and one registration covers both. This follows the TypeScript driver, which dispatches "to the client's already-registered handlers (elicitation, sampling, roots - one generic engine, no per-feature API)". - `on_elicitation` and `on_sampling` no longer refuse a transport without `on_server_request`. They record the handler either way and wire it to the transport only when it can carry server-to-client requests. Refusing would have locked stdio clients, and every 2026-07-28 connection, out of the only route the spec leaves: the modern lifecycle forbids server-to-client requests, so an embedded request is how it asks. - `MCP::Client.new` gains `input_required_max_rounds:` (default 10, the TypeScript and Python default). - Once a handler is registered, `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 with no 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 `on_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 #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. Three tests cover the shared-registration design specifically: the driver resolving an embedded request on a transport that has no `on_server_request` at all, an `on_roots` handler answering an embedded `roots/list`, and `on_elicitation` and `on_sampling` accepting a registration on such a transport rather than raising, which replaces the two tests that pinned the old refusal. `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 `on_elicitation` and `on_sampling` no longer raise `ArgumentError` on a transport without `on_server_request`; they register the handler for the `input_required` route instead. Code that relied on the refusal to detect an unsuitable transport needs another check. Everything else is additive: the new keyword arguments default to `nil`, and with no handler registered every code path is identical to before.
6 lines
121 B
YAML
6 lines
121 B
YAML
server: []
|
|
client:
|
|
# Unimplemented authorization extensions.
|
|
- auth/dpop
|
|
- auth/dpop-nonce
|
|
- auth/wif-jwt-bearer
|