message}\n"; return new Response($request->getId(), new ElicitResult(ElicitAction::Accept, ['name' => 'Ada'])); } }; $client = Client::builder() ->setClientInfo('stateless-example-client', '1.0.0') // The only line that selects the modern lifecycle. ->setProtocolVersion(ProtocolVersion::V2026_07_28) // Declared in the envelope of every request, so the server knows what it // may ask for before it decides how to answer. ->setCapabilities(new ClientCapabilities(elicitation: true)) ->addRequestHandler($answerWithAName) ->build(); $client->connect(new HttpTransport('http://127.0.0.1:8000/')); printf("Connected to %s (revision %s)\n\n", $client->getServerInfo()?->name, $client->getProtocolVersion()?->value); echo "Tools:\n"; foreach ($client->listTools()->tools as $tool) { printf(" %-12s %s\n", $tool->name, $tool->description ?? ''); } echo "\nA plain call:\n"; echo ' '.text($client->callTool('get_weather', ['city' => 'Munich']))."\n"; echo "\nA call the server cannot finish in one round:\n"; // One call from here. Two on the wire: the server returns its question, the // handler above answers it, and the client retries carrying both the answer and // the server's sealed `requestState`. echo ' '.text($client->callTool('greet', []))."\n"; $client->disconnect(); /** The first block of text in a tool result. */ function text(CallToolResult $result): string { $first = $result->content[0] ?? null; return $first instanceof TextContent ? $first->text : '(no text)'; }