Skip to content

Commit 6da70b8

Browse files
committed
StructuredContent call tool result, added ability to return error from tool
1 parent 6100ffc commit 6da70b8

File tree

3 files changed

+74
-5
lines changed

3 files changed

+74
-5
lines changed

src/Schema/Result/CallToolResult.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@ class CallToolResult implements ResultInterface
3838
/**
3939
* Create a new CallToolResult.
4040
*
41-
* @param Content[] $content The content of the tool result
42-
* @param bool $isError Whether the tool execution resulted in an error. If not set, this is assumed to be false (the call was successful).
41+
* @param Content[] $content The content of the tool result
42+
* @param bool $isError Whether the tool execution resulted in an error. If not set, this is assumed to be false (the call was successful).
43+
* @param mixed[] $structuredContent JSON content for `structuredContent`
4344
*/
4445
public function __construct(
4546
public readonly array $content,
4647
public readonly bool $isError = false,
48+
public readonly ?array $structuredContent = null,
4749
) {
4850
foreach ($this->content as $item) {
4951
if (!$item instanceof Content) {
@@ -107,9 +109,15 @@ public static function fromArray(array $data): self
107109
*/
108110
public function jsonSerialize(): array
109111
{
110-
return [
112+
$result = [
111113
'content' => $this->content,
112114
'isError' => $this->isError,
113115
];
116+
117+
if ($this->structuredContent) {
118+
$result['structuredContent'] = $this->structuredContent;
119+
}
120+
121+
return $result;
114122
}
115123
}

src/Server/Handler/Request/CallToolHandler.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,17 @@ public function handle(Request $request, SessionInterface $session): Response|Er
5959
}
6060

6161
$result = $this->referenceHandler->handle($reference, $arguments);
62-
$formatted = $reference->formatResult($result);
62+
63+
if (!$result instanceof CallToolResult) {
64+
$result = new CallToolResult($reference->formatResult($result));
65+
}
6366

6467
$this->logger->debug('Tool executed successfully', [
6568
'name' => $toolName,
6669
'result_type' => \gettype($result),
6770
]);
6871

69-
return new Response($request->getId(), new CallToolResult($formatted));
72+
return new Response($request->getId(), $result);
7073
} catch (ToolNotFoundException $e) {
7174
$this->logger->error('Tool not found', ['name' => $toolName]);
7275

tests/Unit/Server/Handler/Request/CallToolHandlerTest.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,64 @@ public function testHandleWithSpecialCharactersInArguments(): void
342342
$this->assertEquals($expectedResult, $response->result);
343343
}
344344

345+
public function testHandleReturnsStructuredContentResult(): void
346+
{
347+
$request = $this->createCallToolRequest('structured_tool', ['query' => 'php']);
348+
$toolReference = $this->createMock(ToolReference::class);
349+
$structuredResult = new CallToolResult([new TextContent('Rendered results')], false, ['result' => 'Rendered results']);
350+
351+
$this->referenceProvider
352+
->expects($this->once())
353+
->method('getTool')
354+
->with('structured_tool')
355+
->willReturn($toolReference);
356+
357+
$this->referenceHandler
358+
->expects($this->once())
359+
->method('handle')
360+
->with($toolReference, ['query' => 'php'])
361+
->willReturn($structuredResult);
362+
363+
$toolReference
364+
->expects($this->never())
365+
->method('formatResult');
366+
367+
$response = $this->handler->handle($request, $this->session);
368+
369+
$this->assertInstanceOf(Response::class, $response);
370+
$this->assertSame($structuredResult, $response->result);
371+
$this->assertEquals(['result' => 'Rendered results'], $response->result->jsonSerialize()['structuredContent'] ?? []);
372+
}
373+
374+
public function testHandleReturnsCallToolResult(): void
375+
{
376+
$request = $this->createCallToolRequest('result_tool', ['query' => 'php']);
377+
$toolReference = $this->createMock(ToolReference::class);
378+
$callToolResult = new CallToolResult([new TextContent('Error result')], true);
379+
380+
$this->referenceProvider
381+
->expects($this->once())
382+
->method('getTool')
383+
->with('result_tool')
384+
->willReturn($toolReference);
385+
386+
$this->referenceHandler
387+
->expects($this->once())
388+
->method('handle')
389+
->with($toolReference, ['query' => 'php'])
390+
->willReturn($callToolResult);
391+
392+
$toolReference
393+
->expects($this->never())
394+
->method('formatResult');
395+
396+
$response = $this->handler->handle($request, $this->session);
397+
398+
$this->assertInstanceOf(Response::class, $response);
399+
$this->assertSame($callToolResult, $response->result);
400+
$this->assertArrayNotHasKey('structuredContent', $response->result->jsonSerialize());
401+
}
402+
345403
/**
346404
* @param array<string, mixed> $arguments
347405
*/

0 commit comments

Comments
 (0)