Pass InputRequiredResult through the MCPServer prompt and resource pipelines#3020
Conversation
…edResult through At 2026-07-28, prompts/get and resources/read may answer with an InputRequiredResult (SEP-2322 multi-round-trip requests), but only the tools/call pipeline passed one through from MCPServer. This closed the last waived server conformance scenario, input-required-result-non-tool-request. - Prompt.render and MCPServer.get_prompt pass an InputRequiredResult returned by an @mcp.prompt() function through unchanged; the retry's answers arrive on ctx.input_responses, with ctx.request_state carrying the echoed state. - ResourceTemplate.create_resource and MCPServer.read_resource do the same for @mcp.resource() template functions. Static resource functions can never read the retry (no Context), so FunctionResource.read now rejects an InputRequiredResult loudly instead of JSON-dumping it as content. - Context.read_resource keeps its narrow content type by default and grows ClientSession-style allow_input_required overloads so a handler can opt in and forward a template's InputRequiredResult as its own result. - Era handling is unchanged: the per-version result surfaces already reject the frame toward pre-2026 sessions with -32603, exactly as for tools. - The everything-server gains the test_input_required_result_prompt fixture the conformance scenario drives; both expected-failures baselines are now empty and all three server conformance legs pass with zero waivers. - Docs: a "Beyond tools" section in multi-round-trip.md with a runnable example, the low-level-server.md handler list, and a migration.md entry for the widened return types.
There was a problem hiding this comment.
I didn't find any bugs in this change — the pass-through logic mirrors the existing tools/call pattern and the new tests cover both pipelines well — but this is a feature PR with type-level breaking changes to the public MCPServer API, so it deserves a human look.
Extended reasoning...
Overview
This PR widens the MCPServer prompt and resource pipelines to pass InputRequiredResult through (SEP-2322 multi-round-trip requests), mirroring the existing tools/call pass-through. It touches Prompt.render, PromptManager.render_prompt, ResourceTemplate.create_resource, ResourceManager.get_resource, FunctionResource.read, MCPServer.get_prompt/read_resource/_handle_get_prompt/_handle_read_resource, and Context.read_resource (new allow_input_required overloads). It also removes the last server conformance waivers, adds an everything-server fixture, a docs tutorial, migration notes, and a substantial set of new tests.
Security risks
Low. The change passes a result object through unchanged rather than introducing new input handling, and the era gate (pre-2026 sessions rejecting the frame with -32603) is reused from the existing tools path and pinned by new tests. The static-resource case now raises loudly instead of JSON-dumping the object, which is a strictness improvement. No auth, crypto, or permission code is touched.
Level of scrutiny
Moderate. The implementation is consistent and well-tested (round-trip tests through an in-memory client, identity pass-through tests, legacy-session error tests, and the ctx.read_resource opt-in/raise arcs), but the PR changes the declared return types of several public API methods (get_prompt, read_resource, Prompt.render, ResourceTemplate.create_resource), which is a documented breaking change for direct callers. API-surface and protocol-conformance decisions like this should be confirmed by a maintainer rather than auto-approved.
Other factors
No bugs were found by the automated review. Test coverage of the new behavior is thorough, conformance baselines are now empty (CI will fail on stale entries, so that claim is self-checking), and the docs/migration guide are updated. The breadth (21 files) and the API/type changes are the main reasons to keep a human in the loop.
Drop the allow_input_required overloads: ctx.read_resource is a content reader, so an InputRequiredResult from a resource template always raises, with the error pointing at the forwarding path. A handler that wants to receive and forward the result as its own calls MCPServer.read_resource(uri, context) directly, which carries the union.
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
…resource pipelines tools/call already re-raises MCPError so a handler can answer with a typed protocol error (e.g. a missing-client-capability rejection with data.requiredCapabilities); the prompt and resource pipelines wrapped it into generic internal errors, destroying the code and data. Add the same carve-out at the five wrap sites (Prompt.render, MCPServer.get_prompt, ResourceTemplate.create_resource, FunctionResource.read, MCPServer.read_resource) — this matters now that prompt and template functions participate in the multi-round-trip flow and must be able to reject a request themselves. Also document that a tool whose dependencies elicit owns its request_state channel, so handlers should not forward another result's InputRequiredResult from such a tool.
There was a problem hiding this comment.
I didn't find any bugs, but this PR widens the return types of the core prompt/resource pipelines (a documented breaking change) and empties both conformance baselines, so it warrants a human review rather than auto-approval.
Extended reasoning...
Overview
The PR threads InputRequiredResult through the MCPServer prompt and resource pipelines: Prompt.render, PromptManager.render_prompt, ResourceTemplate.create_resource, ResourceManager.get_resource, MCPServer.get_prompt/read_resource, and the corresponding low-level handlers now return a union with InputRequiredResult. It also adds MCPError pass-through in the prompt/resource error handling paths, a static-resource rejection in FunctionResource.read, a RuntimeError guard in Context.read_resource, a new everything-server fixture, a new docs tutorial, and removes the last waived scenarios from both conformance expected-failures files.
Security risks
Low. No auth, crypto, or permission logic is touched. The changes are type-widening pass-throughs plus exception-class handling adjustments (except (ResourceError, MCPError): raise). The era gate that rejects InputRequiredResult toward pre-2026 sessions is unchanged and pinned by new tests, so there is no new data-exposure surface for legacy clients.
Level of scrutiny
This deserves a human pass: it is a feature PR with documented breaking changes to public method signatures (MCPServer.get_prompt/read_resource, Prompt.render, ResourceTemplate.create_resource), it changes exception-propagation behavior in the prompt and resource pipelines (handler-raised MCPError now surfaces with its original code/data instead of being wrapped), and it empties both conformance baselines — all of which affect downstream SDK users and CI posture, not just internals.
Other factors
The implementation closely mirrors the existing tools/call pass-through and is well-covered by new tests (round-trip via in-memory client, legacy-session rejection, static-resource rejection, ctx.read_resource guard, MCPError code/data propagation). The single reviewer comment (cubic) about the migration-doc call form was addressed in a follow-up commit. I found no correctness issues; the deferral is purely about the breadth and breaking nature of the change.
ctx.read_resource passed the handler's own Context into the nested resource template, so on the outer request's retry the template saw input_responses and request_state addressed to the outer handler — with a colliding key a write-once template would silently consume an answer to a different question. The continuation payload belongs to the wire request's target dispatch only, so nested invocations now use Context._nested_invocation(): same request infrastructure, no input_responses/request_state — a nested template always behaves as round one, making ctx.read_resource's raise-on-input-required contract deterministic across rounds. The explicit forwarding path (MCPServer.read_resource(uri, context)) is unchanged: there the caller deliberately re-addresses the payload.
There was a problem hiding this comment.
I didn't find any bugs in this change — the pass-through logic, era gating, and nested-context isolation all look consistent with the existing tools/call path and are well covered by the new tests. That said, this is a breaking type-level change to the core prompt/resource pipelines plus a behavior change in ctx.read_resource and MCPError propagation, so it deserves a human reviewer's sign-off.
Extended reasoning...
Overview
This PR passes InputRequiredResult through the MCPServer prompt and resource pipelines (SEP-2322 multi-round-trip), mirroring the existing tools/call support. It touches Prompt.render, PromptManager.render_prompt, ResourceTemplate.create_resource, ResourceManager.get_resource, MCPServer.get_prompt/read_resource/handlers, Context.read_resource (new _nested_invocation helper and a RuntimeError on InputRequiredResult), FunctionResource.read (rejects InputRequiredResult from static resources), MCPError pass-through in several try/except layers, conformance baselines, docs, the everything-server example, and a sizable batch of new tests.
Security risks
Low. No auth, crypto, or permission code is touched. The main protocol-surface consideration is that the nested-invocation context deliberately strips input_responses/request_state so a nested resource read cannot consume answers addressed to the outer handler — this is a hardening choice and is pinned by a dedicated test. The static-resource rejection of InputRequiredResult avoids accidentally serializing a request object as resource content. The era gate (-32603 toward pre-2026 sessions) reuses the existing tools-path mechanism with no new code.
Level of scrutiny
High. This is production SDK code on the main request-handling pipelines, includes a documented breaking change (return-type unions on MCPServer.get_prompt/read_resource, Prompt.render, ResourceTemplate.create_resource), and changes exception-propagation semantics (handler-raised MCPError now keeps its code/data instead of being wrapped). These are exactly the kinds of API-surface and behavior decisions a maintainer should weigh in on, even though the implementation closely mirrors the already-merged tools/call pattern.
Other factors
The bug hunting system found no bugs, and I didn't find any either: the isinstance pass-throughs are placed before validation/wrapping, MCPError re-raise clauses are added consistently at each wrapping layer, and the new tests cover both rounds of the flow, the legacy-session error posture, ctx.read_resource isolation, and static-resource rejection. The single prior reviewer comment (migration doc call-form) was addressed. Both conformance baselines are now empty, which the author reports verifying locally. Given the breadth and the breaking change, I'm deferring rather than approving.
Motivation and Context
input-required-result-non-tool-requestwas the last waived server conformance scenario: at 2026-07-28 a server MAY answerprompts/getandresources/readwith anInputRequiredResult(SEP-2322 multi-round-trip requests), but only the tools/call pipeline passed one through fromMCPServer— the prompt pipeline silently coerced it into a text message and the resource pipeline had no path for it. The low-levelServer, the per-version result surfaces, and the client auto-loop (#2998) already supported all three methods, so this closes the gap at the one remaining tier, mirroring the tools pass-through from #2974:Prompt.render/MCPServer.get_promptpass anInputRequiredResultreturned by an@mcp.prompt()function through unchanged; the retry's answers arrive onctx.input_responses, withctx.request_statecarrying the echoed state.ResourceTemplate.create_resource/MCPServer.read_resourcedo the same for@mcp.resource()template functions. Static resource functions can never read the retry (they take noContext), soFunctionResource.readnow rejects anInputRequiredResultloudly instead of JSON-dumping it as resource text.Context.read_resourcestays a pure content reader: anInputRequiredResultfrom a template raises a clearRuntimeErrorthere. A handler that wants to receive and forward one as its own result callsMCPServer.read_resource(uri, context)directly, which carries the union.-32603, exactly as for tools.The everything-server gains the
test_input_required_result_promptfixture the scenario drives, and the waiver is removed from both expected-failures files — both server conformance baselines are now empty.How Has This Been Tested?
Client, the seam pass-throughs (identity), bothctx.read_resourcearcs, the static-resource rejection, and the legacy-session-32603posture for both methods.mkdocs build --strictpasses with the new docs section and runnable example.Coverage note: the scenario exercises
prompts/getonly — no conformance scenario currently drivesresources/readreturningInputRequiredResult, so the resource side is proven by the SDK test suite alone. Worth adding a scenario in the conformance repo.Breaking Changes
Type-level, documented in
docs/migration.md:MCPServer.get_prompt()/read_resource()(andPrompt.render()/ResourceTemplate.create_resource()) now return... | InputRequiredResult; direct callers narrow withisinstance.ctx.read_resource()is unchanged — it still returns content, and now raises a clearRuntimeErrorif the resource requests input (forward viaMCPServer.read_resource(uri, context)instead).Types of changes
Checklist
Additional context
Part of #2891. With this change,
.github/actions/conformance/expected-failures.ymlandexpected-failures.2026-07-28.ymlare both fully empty — every scenario in the pinned conformance harness passes against the SDK with no waivers.AI Disclaimer