diff --git a/src/Schema/Result/CallToolResult.php b/src/Schema/Result/CallToolResult.php index 8a01bbb2..da3be4f6 100644 --- a/src/Schema/Result/CallToolResult.php +++ b/src/Schema/Result/CallToolResult.php @@ -38,12 +38,14 @@ class CallToolResult implements ResultInterface /** * Create a new CallToolResult. * - * @param Content[] $content The content of the tool result - * @param bool $isError Whether the tool execution resulted in an error. If not set, this is assumed to be false (the call was successful). + * @param Content[] $content The content of the tool result + * @param bool $isError Whether the tool execution resulted in an error. If not set, this is assumed to be false (the call was successful). + * @param mixed[] $structuredContent JSON content for `structuredContent` */ public function __construct( public readonly array $content, public readonly bool $isError = false, + public readonly ?array $structuredContent = null, ) { foreach ($this->content as $item) { if (!$item instanceof Content) { @@ -107,9 +109,15 @@ public static function fromArray(array $data): self */ public function jsonSerialize(): array { - return [ + $result = [ 'content' => $this->content, 'isError' => $this->isError, ]; + + if ($this->structuredContent) { + $result['structuredContent'] = $this->structuredContent; + } + + return $result; } } diff --git a/src/Server/Handler/Request/CallToolHandler.php b/src/Server/Handler/Request/CallToolHandler.php index dea2725b..c1f10b9d 100644 --- a/src/Server/Handler/Request/CallToolHandler.php +++ b/src/Server/Handler/Request/CallToolHandler.php @@ -59,14 +59,17 @@ public function handle(Request $request, SessionInterface $session): Response|Er } $result = $this->referenceHandler->handle($reference, $arguments); - $formatted = $reference->formatResult($result); + + if (!$result instanceof CallToolResult) { + $result = new CallToolResult($reference->formatResult($result)); + } $this->logger->debug('Tool executed successfully', [ 'name' => $toolName, 'result_type' => \gettype($result), ]); - return new Response($request->getId(), new CallToolResult($formatted)); + return new Response($request->getId(), $result); } catch (ToolNotFoundException $e) { $this->logger->error('Tool not found', ['name' => $toolName]); diff --git a/tests/Unit/Server/Handler/Request/CallToolHandlerTest.php b/tests/Unit/Server/Handler/Request/CallToolHandlerTest.php index 4423cbd6..11b799bf 100644 --- a/tests/Unit/Server/Handler/Request/CallToolHandlerTest.php +++ b/tests/Unit/Server/Handler/Request/CallToolHandlerTest.php @@ -342,6 +342,64 @@ public function testHandleWithSpecialCharactersInArguments(): void $this->assertEquals($expectedResult, $response->result); } + public function testHandleReturnsStructuredContentResult(): void + { + $request = $this->createCallToolRequest('structured_tool', ['query' => 'php']); + $toolReference = $this->createMock(ToolReference::class); + $structuredResult = new CallToolResult([new TextContent('Rendered results')], false, ['result' => 'Rendered results']); + + $this->referenceProvider + ->expects($this->once()) + ->method('getTool') + ->with('structured_tool') + ->willReturn($toolReference); + + $this->referenceHandler + ->expects($this->once()) + ->method('handle') + ->with($toolReference, ['query' => 'php']) + ->willReturn($structuredResult); + + $toolReference + ->expects($this->never()) + ->method('formatResult'); + + $response = $this->handler->handle($request, $this->session); + + $this->assertInstanceOf(Response::class, $response); + $this->assertSame($structuredResult, $response->result); + $this->assertEquals(['result' => 'Rendered results'], $response->result->jsonSerialize()['structuredContent'] ?? []); + } + + public function testHandleReturnsCallToolResult(): void + { + $request = $this->createCallToolRequest('result_tool', ['query' => 'php']); + $toolReference = $this->createMock(ToolReference::class); + $callToolResult = new CallToolResult([new TextContent('Error result')], true); + + $this->referenceProvider + ->expects($this->once()) + ->method('getTool') + ->with('result_tool') + ->willReturn($toolReference); + + $this->referenceHandler + ->expects($this->once()) + ->method('handle') + ->with($toolReference, ['query' => 'php']) + ->willReturn($callToolResult); + + $toolReference + ->expects($this->never()) + ->method('formatResult'); + + $response = $this->handler->handle($request, $this->session); + + $this->assertInstanceOf(Response::class, $response); + $this->assertSame($callToolResult, $response->result); + $this->assertArrayNotHasKey('structuredContent', $response->result->jsonSerialize()); + } + /** * @param array $arguments */