[Server] Serve ClientGateway::elicit() under the modern lifecycle - #466
Merged
chr-hertel merged 1 commit intoAug 19, 2026
Merged
Conversation
chr-hertel
requested review from
CodeWithKyrian,
Nyholm and
soyuka
as code owners
August 19, 2026 22:20
One handler asks the user something on any revision now. Where the client can be asked mid-request it still is; where it cannot, the ask becomes the input_required result that revision carries and the same call returns the answer once the client re-sends it. Costs one handler entry per ask, so side effects belong after the last question. Answers from earlier rounds travel in the requestState, which is why asking more than once needs Builder::setRequestState(). sample() and listRoots() still raise a LogicException there: that revision removed them outright.
chr-hertel
force-pushed
the
unify-elicitations
branch
from
August 19, 2026 23:02
8feafd8 to
2db0d04
Compare
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.
Building a server against the SDK meant picking an era:
ClientGateway::elicit()on the handshake leg, or returning anInputRequiredResultand readingRequestContext::getInputContext()on the 2026-07-28 leg — whereelicit()threw outright. Portable handlers had to be written in the second shape and carry the fork.elicit()andelicitUrl()now serve both. Same signature, same return type:Under 2026-07-28 the gateway's fiber suspension is fulfilled from MRTR state instead of a server-initiated request: answered already → the fiber resumes and the handler runs on; not yet → the ask becomes the
input_requiredresult and the fiber is abandoned (unwound, sofinallyruns), and the same call returns the answer when the client re-sends it. NewMcp\Server\Stateless\ElicitationReplayowns the keying, lookup and sealing;ClientGatewaygains only the optional$key.What it costs:
InputRequiredResultstays the explicit form for batching several asks into one round, carrying your own state, or asking for anything else.requestState— the client only echoes the round it just answered — so a handler asking twice or more needsBuilder::setRequestState(). Without it: a logged internal error, not silent data loss. Single-ask handlers need nothing.key:names an ask across rounds; unnamed asks are keyed by position.sample()andlistRoots()still raise aLogicExceptionthere — that revision removed them outright, and the message now says so instead of pointing atInputRequiredResult.examples/server/elicitationlost its fork entirely: the privateask()is oneelicit()call, and every tool dropped its|InputRequiredResultreturn type andinstanceofbranch. The pre-existingDualEraElicitationTestdrives that example from both eras and passes untouched.Tests:
ElicitationReplayTestplus round-trip, three-round-with-state, missing-key, malformed-answer, undeclared-capability and mid-stream cases inStatelessProtocolTest.