[Server][Tests] Complete the multi round-trip request flow (SEP-2322) (#446)

The revision has no way for a server to put a request on a response stream,
so a handler that needs input from the user says so in its result instead: a
handler returning InputRequiredResult comes back as
resultType: "input_required" with the asks and an opaque requestState, and
the client retries the same request carrying inputResponses.

RequestContext::getInputContext() is what the handler reads them through.
RequestStateCodec signs and time-bounds the state, so a tampered or expired
one is refused rather than trusted - the state travels through the client,
which is not a place to keep something the server later believes.
This commit is contained in:
Christopher Hertel 2026-08-18 22:45:35 +02:00 • committed by GitHub
commit ef0c1c2808
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -30,8 +30,8 @@ use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
/**
* A tools/call can answer with the tool's output or, under MRTR, with a request
* for the input the tool still needs — hence the two result types.
* A tools/call answers with the tool's output or, under MRTR, with a request
* for the input it still needs.
*
* @implements RequestHandlerInterface<CallToolResult|InputRequiredResult>
*
@ -103,7 +103,7 @@ final class CallToolHandler implements RequestHandlerInterface
try {
$result = $this->referenceHandler->handle($reference, $arguments);
// Tool asking for more input as response
// An ask is a result in its own right, not tool output.
if ($result instanceof InputRequiredResult) {
return new Response($request->getId(), $result);
}
@ -146,6 +146,8 @@ final class CallToolHandler implements RequestHandlerInterface
return new Response($request->getId(), $result);
} catch (MissingRequiredClientCapabilityException $e) {
// Not a tool failure — the request was unservable, and the client
// needs to retry declaring the capability. Rendered as -32021.
throw $e;
} catch (ToolCallException $e) {
$this->logger->error(\sprintf('Error while executing tool "%s": "%s".', $toolName, $e->getMessage()), [