feat: Add output schema support to MCP tools (#153)
This commit is contained in:
parent
4c3e5e61c7
commit
c0938a19bd
1 changed files with 605 additions and 55 deletions
|
|
@ -21,6 +21,7 @@ use Mcp\Schema\JsonRpc\Error;
|
|||
use Mcp\Schema\JsonRpc\Response;
|
||||
use Mcp\Schema\Request\CallToolRequest;
|
||||
use Mcp\Schema\Result\CallToolResult;
|
||||
use Mcp\Schema\Tool;
|
||||
use Mcp\Server\Handler\Request\CallToolHandler;
|
||||
use Mcp\Server\Session\SessionInterface;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
|
@ -59,7 +60,9 @@ class CallToolHandlerTest extends TestCase
|
|||
public function testHandleSuccessfulToolCall(): void
|
||||
{
|
||||
$request = $this->createCallToolRequest('greet_user', ['name' => 'John']);
|
||||
$toolReference = $this->createMock(ToolReference::class);
|
||||
$toolReference = $this->createToolReference('greet_user', function () {
|
||||
return 'Hello, John!';
|
||||
});
|
||||
$expectedResult = new CallToolResult([new TextContent('Hello, John!')]);
|
||||
|
||||
$this->registry
|
||||
|
|
@ -92,7 +95,9 @@ class CallToolHandlerTest extends TestCase
|
|||
public function testHandleToolCallWithEmptyArguments(): void
|
||||
{
|
||||
$request = $this->createCallToolRequest('simple_tool', []);
|
||||
$toolReference = $this->createMock(ToolReference::class);
|
||||
$toolReference = $this->createToolReference('greet_user', function () {
|
||||
return 'Hello, John!';
|
||||
});
|
||||
$expectedResult = new CallToolResult([new TextContent('Simple result')]);
|
||||
|
||||
$this->registry
|
||||
|
|
@ -129,7 +134,9 @@ class CallToolHandlerTest extends TestCase
|
|||
'null_param' => null,
|
||||
];
|
||||
$request = $this->createCallToolRequest('complex_tool', $arguments);
|
||||
$toolReference = $this->createMock(ToolReference::class);
|
||||
$toolReference = $this->createToolReference('greet_user', function () {
|
||||
return 'Hello, John!';
|
||||
});
|
||||
$expectedResult = new CallToolResult([new TextContent('Complex result')]);
|
||||
|
||||
$this->registry
|
||||
|
|
@ -182,7 +189,9 @@ class CallToolHandlerTest extends TestCase
|
|||
$request = $this->createCallToolRequest('failing_tool', ['param' => 'value']);
|
||||
$exception = new ToolCallException('Tool execution failed');
|
||||
|
||||
$toolReference = $this->createMock(ToolReference::class);
|
||||
$toolReference = $this->createToolReference('greet_user', function () {
|
||||
return 'Hello, John!';
|
||||
});
|
||||
$this->registry
|
||||
->expects($this->once())
|
||||
->method('getTool')
|
||||
|
|
@ -217,7 +226,9 @@ class CallToolHandlerTest extends TestCase
|
|||
$request = $this->createCallToolRequest('null_tool', []);
|
||||
$expectedResult = new CallToolResult([]);
|
||||
|
||||
$toolReference = $this->createMock(ToolReference::class);
|
||||
$toolReference = $this->createToolReference('greet_user', function () {
|
||||
return 'Hello, John!';
|
||||
});
|
||||
$this->registry
|
||||
->expects($this->once())
|
||||
->method('getTool')
|
||||
|
|
@ -254,7 +265,9 @@ class CallToolHandlerTest extends TestCase
|
|||
$request = $this->createCallToolRequest('test_tool', ['key1' => 'value1', 'key2' => 42]);
|
||||
$exception = new ToolCallException('Custom error message');
|
||||
|
||||
$toolReference = $this->createMock(ToolReference::class);
|
||||
$toolReference = $this->createToolReference('greet_user', function () {
|
||||
return 'Hello, John!';
|
||||
});
|
||||
$this->registry
|
||||
->expects($this->once())
|
||||
->method('getTool')
|
||||
|
|
@ -297,7 +310,9 @@ class CallToolHandlerTest extends TestCase
|
|||
$request = $this->createCallToolRequest('failing_tool', ['param' => 'value']);
|
||||
$exception = new \RuntimeException('Internal database connection failed');
|
||||
|
||||
$toolReference = $this->createMock(ToolReference::class);
|
||||
$toolReference = $this->createToolReference('greet_user', function () {
|
||||
return 'Hello, John!';
|
||||
});
|
||||
$this->registry
|
||||
->expects($this->once())
|
||||
->method('getTool')
|
||||
|
|
@ -324,7 +339,10 @@ class CallToolHandlerTest extends TestCase
|
|||
$request = $this->createCallToolRequest('tool-with_special.chars', []);
|
||||
$expectedResult = new CallToolResult([new TextContent('Special tool result')]);
|
||||
|
||||
$toolReference = $this->createMock(ToolReference::class);
|
||||
$toolReference = $this->createToolReference('greet_user', function () {
|
||||
return 'Hello, John!';
|
||||
});
|
||||
|
||||
$this->registry
|
||||
->expects($this->once())
|
||||
->method('getTool')
|
||||
|
|
@ -359,7 +377,9 @@ class CallToolHandlerTest extends TestCase
|
|||
$request = $this->createCallToolRequest('unicode_tool', $arguments);
|
||||
$expectedResult = new CallToolResult([new TextContent('Unicode handled')]);
|
||||
|
||||
$toolReference = $this->createMock(ToolReference::class);
|
||||
$toolReference = $this->createToolReference('greet_user', function () {
|
||||
return 'Hello, John!';
|
||||
});
|
||||
$this->registry
|
||||
->expects($this->once())
|
||||
->method('getTool')
|
||||
|
|
@ -387,7 +407,9 @@ class CallToolHandlerTest extends TestCase
|
|||
public function testHandleReturnsStructuredContentResult(): void
|
||||
{
|
||||
$request = $this->createCallToolRequest('structured_tool', ['query' => 'php']);
|
||||
$toolReference = $this->createMock(ToolReference::class);
|
||||
$toolReference = $this->createToolReference('greet_user', function () {
|
||||
return 'Hello, John!';
|
||||
});
|
||||
$structuredResult = new CallToolResult([new TextContent('Rendered results')], false, ['result' => 'Rendered results']);
|
||||
|
||||
$this->registry
|
||||
|
|
@ -416,7 +438,9 @@ class CallToolHandlerTest extends TestCase
|
|||
public function testHandleReturnsCallToolResult(): void
|
||||
{
|
||||
$request = $this->createCallToolRequest('result_tool', ['query' => 'php']);
|
||||
$toolReference = $this->createMock(ToolReference::class);
|
||||
$toolReference = $this->createToolReference('greet_user', function () {
|
||||
return 'Hello, John!';
|
||||
});
|
||||
$callToolResult = new CallToolResult([new TextContent('Error result')], true);
|
||||
|
||||
$this->registry
|
||||
|
|
@ -457,4 +481,22 @@ class CallToolHandlerTest extends TestCase
|
|||
],
|
||||
]);
|
||||
}
|
||||
|
||||
private function createToolReference(
|
||||
string $name,
|
||||
callable $handler,
|
||||
?array $outputSchema = null,
|
||||
array $methodsToMock = ['formatResult'],
|
||||
): ToolReference&MockObject {
|
||||
$tool = new Tool($name, ['type' => 'object', 'properties' => [], 'required' => null], null, null, null, null, $outputSchema);
|
||||
|
||||
$builder = $this->getMockBuilder(ToolReference::class)
|
||||
->setConstructorArgs([$tool, $handler]);
|
||||
|
||||
if (!empty($methodsToMock)) {
|
||||
$builder->onlyMethods($methodsToMock);
|
||||
}
|
||||
|
||||
return $builder->getMock();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue