diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aa16144..67ae3929 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ All notable changes to `mcp/sdk` will be documented in this file. * Add OAuth 2.0 Dynamic Client Registration middleware (RFC 7591) * Add optional `title` field to `Prompt` and `McpPrompt` for MCP spec compliance * [BC Break] `Builder::addPrompt()` signature changed — `$title` parameter added between `$name` and `$description`. Callers using positional arguments for `$description` must switch to named arguments. +* Add optional `title` field to `Tool` and `McpTool` for MCP spec compliance +* [BC Break] `Tool::__construct()` signature changed — `$title` parameter added between `$name` and `$inputSchema`. Callers using positional arguments must switch to named arguments or pass `null` for `$title`. +* [BC Break] `McpTool` attribute signature changed — `$title` parameter added between `$name` and `$description`. Callers using positional arguments for `$description` must switch to named arguments. +* [BC Break] `Builder::addTool()` signature changed — `$title` parameter added between `$name` and `$description`. Callers using positional arguments for `$description` must switch to named arguments. 0.4.0 ----- diff --git a/docs/server-builder.md b/docs/server-builder.md index 3ec22ebb..1a8e4663 100644 --- a/docs/server-builder.md +++ b/docs/server-builder.md @@ -278,6 +278,7 @@ $server = Server::builder() - `handler` (callable|string): The tool handler - `name` (string|null): Optional tool name +- `title` (string|null): Optional human-readable title for display in UI - `description` (string|null): Optional tool description - `annotations` (ToolAnnotations|null): Optional annotations for the tool - `inputSchema` (array|null): Optional input schema for the tool @@ -578,7 +579,7 @@ $server = Server::builder() | `addRequestHandlers()` | handlers | Prepend multiple custom request handlers | | `addNotificationHandler()` | handler | Prepend a single custom notification handler | | `addNotificationHandlers()` | handlers | Prepend multiple custom notification handlers | -| `addTool()` | handler, name?, description?, annotations?, inputSchema? | Register tool | +| `addTool()` | handler, name?, title?, description?, annotations?, inputSchema?, ... | Register tool | | `addResource()` | handler, uri, name?, description?, mimeType?, size?, annotations? | Register resource | | `addResourceTemplate()` | handler, uriTemplate, name?, description?, mimeType?, annotations? | Register resource template | | `addPrompt()` | handler, name?, description? | Register prompt | diff --git a/docs/server-client-communication.md b/docs/server-client-communication.md index 5122d0be..b37ff63b 100644 --- a/docs/server-client-communication.md +++ b/docs/server-client-communication.md @@ -24,7 +24,7 @@ use Mcp\Server\RequestContext; class MyService { - #[McpTool('my_tool', 'My Tool Description')] + #[McpTool(name: 'my_tool', description: 'My Tool Description')] public function myTool(RequestContext $context): string { $context->getClientGateway()->log(...); diff --git a/examples/server/client-communication/ClientAwareService.php b/examples/server/client-communication/ClientAwareService.php index b33f4a75..2a3d8342 100644 --- a/examples/server/client-communication/ClientAwareService.php +++ b/examples/server/client-communication/ClientAwareService.php @@ -28,7 +28,7 @@ public function __construct( /** * @return array{incident: string, recommended_actions: string, model: string} */ - #[McpTool('coordinate_incident_response', 'Coordinate an incident response with logging, progress, and sampling.')] + #[McpTool(name: 'coordinate_incident_response', description: 'Coordinate an incident response with logging, progress, and sampling.')] public function coordinateIncident(RequestContext $context, string $incidentTitle): array { $clientGateway = $context->getClientGateway(); diff --git a/examples/server/custom-method-handlers/server.php b/examples/server/custom-method-handlers/server.php index 23bb6b76..461d9d96 100644 --- a/examples/server/custom-method-handlers/server.php +++ b/examples/server/custom-method-handlers/server.php @@ -25,6 +25,7 @@ $toolDefinitions = [ 'say_hello' => new Tool( name: 'say_hello', + title: null, inputSchema: [ 'type' => 'object', 'properties' => [ @@ -37,6 +38,7 @@ ), 'sum' => new Tool( name: 'sum', + title: null, inputSchema: [ 'type' => 'object', 'properties' => [ diff --git a/examples/server/elicitation/ElicitationHandlers.php b/examples/server/elicitation/ElicitationHandlers.php index 0803dd9c..d94b1ef6 100644 --- a/examples/server/elicitation/ElicitationHandlers.php +++ b/examples/server/elicitation/ElicitationHandlers.php @@ -47,7 +47,7 @@ public function __construct( * * @return array{status: string, message: string, booking?: array{party_size: int, date: string, dietary: string}} */ - #[McpTool('book_restaurant', 'Book a restaurant reservation, collecting details via elicitation.')] + #[McpTool(name: 'book_restaurant', description: 'Book a restaurant reservation, collecting details via elicitation.')] public function bookRestaurant(RequestContext $context, string $restaurantName): array { if (!$context->getClientGateway()->supportsElicitation()) { @@ -158,7 +158,7 @@ enumNames: ['None', 'Vegetarian', 'Vegan', 'Gluten-Free', 'Halal', 'Kosher'], * * @return array{status: string, message: string} */ - #[McpTool('confirm_action', 'Request user confirmation before proceeding with an action.')] + #[McpTool(name: 'confirm_action', description: 'Request user confirmation before proceeding with an action.')] public function confirmAction(RequestContext $context, string $actionDescription): array { if (!$context->getClientGateway()->supportsElicitation()) { @@ -226,7 +226,7 @@ public function confirmAction(RequestContext $context, string $actionDescription * * @return array{status: string, message: string, feedback?: array{rating: string, comments: string}} */ - #[McpTool('collect_feedback', 'Collect user feedback via elicitation form.')] + #[McpTool(name: 'collect_feedback', description: 'Collect user feedback via elicitation form.')] public function collectFeedback(RequestContext $context, string $topic): array { if (!$context->getClientGateway()->supportsElicitation()) { diff --git a/src/Capability/Attribute/McpTool.php b/src/Capability/Attribute/McpTool.php index fe754e90..1c71e2cd 100644 --- a/src/Capability/Attribute/McpTool.php +++ b/src/Capability/Attribute/McpTool.php @@ -22,6 +22,7 @@ class McpTool { /** * @param string|null $name The name of the tool (defaults to the method name) + * @param string|null $title Optional human-readable title for display in UI * @param string|null $description The description of the tool (defaults to the DocBlock/inferred) * @param ToolAnnotations|null $annotations Optional annotations describing tool behavior * @param ?Icon[] $icons Optional list of icon URLs representing the tool @@ -30,6 +31,7 @@ class McpTool */ public function __construct( public ?string $name = null, + public ?string $title = null, public ?string $description = null, public ?ToolAnnotations $annotations = null, public ?array $icons = null, diff --git a/src/Capability/Discovery/Discoverer.php b/src/Capability/Discovery/Discoverer.php index 586b711d..2554a30d 100644 --- a/src/Capability/Discovery/Discoverer.php +++ b/src/Capability/Discovery/Discoverer.php @@ -231,13 +231,14 @@ private function processMethod(\ReflectionMethod $method, array &$discoveredCoun $inputSchema = $this->schemaGenerator->generate($method); $outputSchema = $this->schemaGenerator->generateOutputSchema($method); $tool = new Tool( - $name, - $inputSchema, - $description, - $instance->annotations, - $instance->icons, - $instance->meta, - $outputSchema, + name: $name, + title: $instance->title, + inputSchema: $inputSchema, + description: $description, + annotations: $instance->annotations, + icons: $instance->icons, + meta: $instance->meta, + outputSchema: $outputSchema, ); $tools[$name] = new ToolReference($tool, [$className, $methodName], false); ++$discoveredCount['tools']; diff --git a/src/Capability/Registry/Loader/ArrayLoader.php b/src/Capability/Registry/Loader/ArrayLoader.php index d9a337e0..34055a2d 100644 --- a/src/Capability/Registry/Loader/ArrayLoader.php +++ b/src/Capability/Registry/Loader/ArrayLoader.php @@ -45,6 +45,7 @@ final class ArrayLoader implements LoaderInterface * @param array{ * handler: Handler, * name: ?string, + * title: ?string, * description: ?string, * annotations: ?ToolAnnotations, * icons: ?Icon[], @@ -115,6 +116,7 @@ public function load(RegistryInterface $registry): void $tool = new Tool( name: $name, + title: $data['title'] ?? null, inputSchema: $inputSchema, description: $description, annotations: $data['annotations'] ?? null, diff --git a/src/Schema/Tool.php b/src/Schema/Tool.php index 6255701d..18178044 100644 --- a/src/Schema/Tool.php +++ b/src/Schema/Tool.php @@ -33,6 +33,7 @@ * } * @phpstan-type ToolData array{ * name: string, + * title?: string, * inputSchema: ToolInputSchema, * description?: string|null, * annotations?: ToolAnnotationsData, @@ -47,10 +48,11 @@ class Tool implements \JsonSerializable { /** * @param string $name the name of the tool + * @param ?string $title Optional human-readable title for display in UI + * @param ToolInputSchema $inputSchema a JSON Schema object (as a PHP array) defining the expected 'arguments' for the tool * @param ?string $description A human-readable description of the tool. * This can be used by clients to improve the LLM's understanding of * available tools. It can be thought of like a "hint" to the model. - * @param ToolInputSchema $inputSchema a JSON Schema object (as a PHP array) defining the expected 'arguments' for the tool * @param ?ToolAnnotations $annotations optional additional tool information * @param ?Icon[] $icons optional icons representing the tool * @param ?array $meta Optional metadata @@ -58,6 +60,7 @@ class Tool implements \JsonSerializable */ public function __construct( public readonly string $name, + public readonly ?string $title, public readonly array $inputSchema, public readonly ?string $description, public readonly ?ToolAnnotations $annotations, @@ -95,19 +98,21 @@ public static function fromArray(array $data): self } return new self( - $data['name'], - $inputSchema, - isset($data['description']) && \is_string($data['description']) ? $data['description'] : null, - isset($data['annotations']) && \is_array($data['annotations']) ? ToolAnnotations::fromArray($data['annotations']) : null, - isset($data['icons']) && \is_array($data['icons']) ? array_map(Icon::fromArray(...), $data['icons']) : null, - isset($data['_meta']) && \is_array($data['_meta']) ? $data['_meta'] : null, - $outputSchema, + name: $data['name'], + title: isset($data['title']) && \is_string($data['title']) ? $data['title'] : null, + inputSchema: $inputSchema, + description: isset($data['description']) && \is_string($data['description']) ? $data['description'] : null, + annotations: isset($data['annotations']) && \is_array($data['annotations']) ? ToolAnnotations::fromArray($data['annotations']) : null, + icons: isset($data['icons']) && \is_array($data['icons']) ? array_map(Icon::fromArray(...), $data['icons']) : null, + meta: isset($data['_meta']) && \is_array($data['_meta']) ? $data['_meta'] : null, + outputSchema: $outputSchema, ); } /** * @return array{ * name: string, + * title?: string, * inputSchema: ToolInputSchema, * description?: string, * annotations?: ToolAnnotations, @@ -118,10 +123,11 @@ public static function fromArray(array $data): self */ public function jsonSerialize(): array { - $data = [ - 'name' => $this->name, - 'inputSchema' => $this->inputSchema, - ]; + $data = ['name' => $this->name]; + if (null !== $this->title) { + $data['title'] = $this->title; + } + $data['inputSchema'] = $this->inputSchema; if (null !== $this->description) { $data['description'] = $this->description; } diff --git a/src/Schema/ToolAnnotations.php b/src/Schema/ToolAnnotations.php index 60e457f8..783d9599 100644 --- a/src/Schema/ToolAnnotations.php +++ b/src/Schema/ToolAnnotations.php @@ -28,7 +28,7 @@ class ToolAnnotations implements \JsonSerializable { /** - * @param ?string $title a human-readable title for the tool + * @param ?string $title a human-readable title for the tool — deprecated for display in favor of `Mcp\Schema\Tool::$title` per MCP spec revision 2025-06-18; retained for backward compatibility * @param ?bool $readOnlyHint if true, the tool does not modify its environment * @param ?bool $destructiveHint If true, the tool may perform destructive updates to its environment. If false, the tool performs only additive updates. * @param ?bool $idempotentHint If true, calling the tool repeatedly with the same arguments will have no additional effect on the its environment. (This property is meaningful only when `readOnlyHint == false`) diff --git a/src/Server/Builder.php b/src/Server/Builder.php index f1c5c053..953c4c2c 100644 --- a/src/Server/Builder.php +++ b/src/Server/Builder.php @@ -99,6 +99,7 @@ final class Builder * @var array{ * handler: Handler, * name: ?string, + * title: ?string, * description: ?string, * annotations: ?ToolAnnotations, * icons: ?Icon[], @@ -373,6 +374,7 @@ public function setProtocolVersion(ProtocolVersion $protocolVersion): self * Manually registers a tool handler. * * @param Handler $handler + * @param ?string $title Optional human-readable title for display in UI * @param array|null $inputSchema * @param ?Icon[] $icons * @param array|null $meta @@ -381,6 +383,7 @@ public function setProtocolVersion(ProtocolVersion $protocolVersion): self public function addTool( callable|array|string $handler, ?string $name = null, + ?string $title = null, ?string $description = null, ?ToolAnnotations $annotations = null, ?array $inputSchema = null, @@ -391,6 +394,7 @@ public function addTool( $this->tools[] = compact( 'handler', 'name', + 'title', 'description', 'annotations', 'inputSchema', diff --git a/tests/Conformance/server.php b/tests/Conformance/server.php index 471d5d83..02c78c2a 100644 --- a/tests/Conformance/server.php +++ b/tests/Conformance/server.php @@ -40,18 +40,18 @@ ->setSession(new FileSessionStore(__DIR__.'/sessions')) ->setLogger($logger) // Tools - ->addTool(static fn () => 'This is a simple text response for testing.', 'test_simple_text', 'Tests simple text content response') - ->addTool(static fn () => new ImageContent(Elements::TEST_IMAGE_BASE64, 'image/png'), 'test_image_content', 'Tests image content response') - ->addTool(static fn () => new AudioContent(Elements::TEST_AUDIO_BASE64, 'audio/wav'), 'test_audio_content', 'Tests audio content response') - ->addTool(static fn () => EmbeddedResource::fromText('test://embedded-resource', 'This is an embedded resource content.'), 'test_embedded_resource', 'Tests embedded resource content response') - ->addTool([Elements::class, 'toolMultipleTypes'], 'test_multiple_content_types', 'Tests response with multiple content types') - ->addTool([Elements::class, 'toolWithLogging'], 'test_tool_with_logging', 'Tests tool that emits log messages') - ->addTool([Elements::class, 'toolWithProgress'], 'test_tool_with_progress', 'Tests tool that reports progress notifications') - ->addTool([Elements::class, 'toolWithSampling'], 'test_sampling', 'Tests server-initiated sampling') - ->addTool(static fn () => CallToolResult::error([new TextContent('This tool intentionally returns an error for testing')]), 'test_error_handling', 'Tests error response handling') - ->addTool([Elements::class, 'toolWithElicitation'], 'test_elicitation', 'Tests server-initiated elicitation') - ->addTool([Elements::class, 'toolWithElicitationDefaults'], 'test_elicitation_sep1034_defaults', 'Tests elicitation with default values') - ->addTool([Elements::class, 'toolWithElicitationEnums'], 'test_elicitation_sep1330_enums', 'Tests elicitation with enum schemas') + ->addTool(static fn () => 'This is a simple text response for testing.', name: 'test_simple_text', description: 'Tests simple text content response') + ->addTool(static fn () => new ImageContent(Elements::TEST_IMAGE_BASE64, 'image/png'), name: 'test_image_content', description: 'Tests image content response') + ->addTool(static fn () => new AudioContent(Elements::TEST_AUDIO_BASE64, 'audio/wav'), name: 'test_audio_content', description: 'Tests audio content response') + ->addTool(static fn () => EmbeddedResource::fromText('test://embedded-resource', 'This is an embedded resource content.'), name: 'test_embedded_resource', description: 'Tests embedded resource content response') + ->addTool([Elements::class, 'toolMultipleTypes'], name: 'test_multiple_content_types', description: 'Tests response with multiple content types') + ->addTool([Elements::class, 'toolWithLogging'], name: 'test_tool_with_logging', description: 'Tests tool that emits log messages') + ->addTool([Elements::class, 'toolWithProgress'], name: 'test_tool_with_progress', description: 'Tests tool that reports progress notifications') + ->addTool([Elements::class, 'toolWithSampling'], name: 'test_sampling', description: 'Tests server-initiated sampling') + ->addTool(static fn () => CallToolResult::error([new TextContent('This tool intentionally returns an error for testing')]), name: 'test_error_handling', description: 'Tests error response handling') + ->addTool([Elements::class, 'toolWithElicitation'], name: 'test_elicitation', description: 'Tests server-initiated elicitation') + ->addTool([Elements::class, 'toolWithElicitationDefaults'], name: 'test_elicitation_sep1034_defaults', description: 'Tests elicitation with default values') + ->addTool([Elements::class, 'toolWithElicitationEnums'], name: 'test_elicitation_sep1330_enums', description: 'Tests elicitation with enum schemas') // Resources ->addResource(static fn () => 'This is the content of the static text resource.', 'test://static-text', 'static-text', 'A static text resource for testing') ->addResource(static fn () => fopen('data://image/png;base64,'.Elements::TEST_IMAGE_BASE64, 'r'), 'test://static-binary', 'static-binary', 'A static binary resource (image) for testing') diff --git a/tests/Unit/Capability/Attribute/McpToolTest.php b/tests/Unit/Capability/Attribute/McpToolTest.php index b6ab86d5..e6314581 100644 --- a/tests/Unit/Capability/Attribute/McpToolTest.php +++ b/tests/Unit/Capability/Attribute/McpToolTest.php @@ -52,6 +52,21 @@ public function testInstantiatesWithMissingOptionalArguments(): void $this->assertNull($attribute->outputSchema); } + public function testInstantiatesWithTitle(): void + { + $attribute = new McpTool(name: 'n', title: 'T'); + + $this->assertSame('n', $attribute->name); + $this->assertSame('T', $attribute->title); + } + + public function testDefaultTitleIsNull(): void + { + $attribute = new McpTool(); + + $this->assertNull($attribute->title); + } + public function testInstantiatesWithOutputSchema(): void { // Arrange diff --git a/tests/Unit/Capability/Discovery/DiscovererToolTitleTest.php b/tests/Unit/Capability/Discovery/DiscovererToolTitleTest.php new file mode 100644 index 00000000..93754256 --- /dev/null +++ b/tests/Unit/Capability/Discovery/DiscovererToolTitleTest.php @@ -0,0 +1,33 @@ +discover(__DIR__, ['Fixtures']); + + $tools = $discovery->getTools(); + + $this->assertArrayHasKey('greet_user', $tools); + $toolRef = $tools['greet_user']; + $this->assertInstanceOf(ToolReference::class, $toolRef); + $this->assertSame('Greet User', $toolRef->tool->title); + } +} diff --git a/tests/Unit/Capability/Discovery/Fixtures/DiscoverableToolHandler.php b/tests/Unit/Capability/Discovery/Fixtures/DiscoverableToolHandler.php index 1c6bc394..f20cda58 100644 --- a/tests/Unit/Capability/Discovery/Fixtures/DiscoverableToolHandler.php +++ b/tests/Unit/Capability/Discovery/Fixtures/DiscoverableToolHandler.php @@ -24,7 +24,7 @@ class DiscoverableToolHandler * * @return string the greeting */ - #[McpTool(name: 'greet_user', description: 'Greets a user by name.')] + #[McpTool(name: 'greet_user', description: 'Greets a user by name.', title: 'Greet User')] public function greet(string $name): string { return "Hello, {$name}!"; diff --git a/tests/Unit/Capability/Registry/Loader/ArrayLoaderToolTitleTest.php b/tests/Unit/Capability/Registry/Loader/ArrayLoaderToolTitleTest.php new file mode 100644 index 00000000..68a1bfb7 --- /dev/null +++ b/tests/Unit/Capability/Registry/Loader/ArrayLoaderToolTitleTest.php @@ -0,0 +1,48 @@ + static fn (): string => 'ok', + 'name' => 'weather_lookup', + 'title' => 'Weather Lookup', + 'description' => null, + 'annotations' => null, + 'inputSchema' => [ + 'type' => 'object', + 'properties' => new \stdClass(), + 'required' => null, + ], + 'icons' => null, + 'meta' => null, + 'outputSchema' => null, + ], + ]; + + $loader = new ArrayLoader($tools); + $registry = new Registry(); + + $loader->load($registry); + + $toolRef = $registry->getTool('weather_lookup'); + $this->assertSame('Weather Lookup', $toolRef->tool->title); + } +} diff --git a/tests/Unit/Capability/RegistryTest.php b/tests/Unit/Capability/RegistryTest.php index e8b19585..90ac8879 100644 --- a/tests/Unit/Capability/RegistryTest.php +++ b/tests/Unit/Capability/RegistryTest.php @@ -615,6 +615,7 @@ private function createValidTool(string $name, ?array $outputSchema = null): Too { return new Tool( name: $name, + title: null, inputSchema: [ 'type' => 'object', 'properties' => [ diff --git a/tests/Unit/Schema/ToolTest.php b/tests/Unit/Schema/ToolTest.php new file mode 100644 index 00000000..dd6861c3 --- /dev/null +++ b/tests/Unit/Schema/ToolTest.php @@ -0,0 +1,100 @@ +, required: string[]|null} + */ + private static function validInputSchema(): array + { + return [ + 'type' => 'object', + 'properties' => ['q' => ['type' => 'string']], + 'required' => null, + ]; + } + + private static function makeTool(?string $title, ?string $description = null): Tool + { + return new Tool( + name: 'x', + inputSchema: self::validInputSchema(), + description: $description, + annotations: null, + title: $title, + ); + } + + /** + * @return iterable}> + */ + public static function serializationKeyOrderProvider(): iterable + { + yield 'with title' => ['Friendly Title', ['name', 'title', 'inputSchema']]; + yield 'without title' => [null, ['name', 'inputSchema']]; + } + + /** + * @param list $expectedKeys + */ + #[DataProvider('serializationKeyOrderProvider')] + public function testSerializationPlacesTitleBetweenNameAndInputSchema(?string $title, array $expectedKeys): void + { + $serialized = self::makeTool($title)->jsonSerialize(); + + $this->assertSame($expectedKeys, array_keys($serialized)); + if (null !== $title) { + $this->assertSame($title, $serialized['title']); + } else { + $this->assertArrayNotHasKey('title', $serialized); + } + } + + /** + * @return iterable, ?string}> + */ + public static function fromArrayTitleProvider(): iterable + { + yield 'title present' => [['title' => 'Friendly Title'], 'Friendly Title']; + yield 'title missing' => [[], null]; + } + + /** + * @param array $extra + */ + #[DataProvider('fromArrayTitleProvider')] + public function testFromArrayReadsTitle(array $extra, ?string $expectedTitle): void + { + $tool = Tool::fromArray(['name' => 'x', 'inputSchema' => self::validInputSchema()] + $extra); + + $this->assertSame($expectedTitle, $tool->title); + } + + public function testRoundTripPreservesTitle(): void + { + $original = self::makeTool('Friendly Title', 'desc'); + + /** @var array{name: string, title?: string, inputSchema: array{type: 'object', properties: array, required: string[]|null}, description?: string|null} $serialized */ + $serialized = $original->jsonSerialize(); + $restored = Tool::fromArray($serialized); + + $this->assertSame('Friendly Title', $restored->title); + $this->assertSame($original->name, $restored->name); + $this->assertSame($original->description, $restored->description); + } +} diff --git a/tests/Unit/Server/BuilderTest.php b/tests/Unit/Server/BuilderTest.php index e8e54af6..8a92d599 100644 --- a/tests/Unit/Server/BuilderTest.php +++ b/tests/Unit/Server/BuilderTest.php @@ -71,7 +71,7 @@ public function testCustomReferenceHandlerIsUsedForToolCalls(): void $server = Server::builder() ->setServerInfo('test', '1.0.0') ->setReferenceHandler($referenceHandler) - ->addTool(static fn (): string => 'original', 'test_tool', 'A test tool') + ->addTool(static fn (): string => 'original', name: 'test_tool', description: 'A test tool') ->build(); $result = $this->callTool($server, 'test_tool'); diff --git a/tests/Unit/Server/Handler/Request/CallToolHandlerTest.php b/tests/Unit/Server/Handler/Request/CallToolHandlerTest.php index b8ec53e1..cad7af15 100644 --- a/tests/Unit/Server/Handler/Request/CallToolHandlerTest.php +++ b/tests/Unit/Server/Handler/Request/CallToolHandlerTest.php @@ -483,7 +483,7 @@ public function testValidationError(): void $request = $this->createCallToolRequest('result_tool', ['query' => 'php']); $toolReference = $this->getMockBuilder(ToolReference::class) - ->setConstructorArgs([new Tool('simple_tool', $schema, null, null), static function () {}]) + ->setConstructorArgs([new Tool('simple_tool', null, $schema, null, null), static function () {}]) ->getMock(); $this->registry @@ -535,7 +535,7 @@ private function createToolReference( ], 'required' => [], ]; - $tool = new Tool($name, $schema, null, null, null, null, $outputSchema); + $tool = new Tool($name, null, $schema, null, null, null, null, $outputSchema); $builder = $this->getMockBuilder(ToolReference::class) ->setConstructorArgs([$tool, $handler]); diff --git a/tests/Unit/Server/Handler/Request/ListToolsHandlerTest.php b/tests/Unit/Server/Handler/Request/ListToolsHandlerTest.php index 24536301..b97185ad 100644 --- a/tests/Unit/Server/Handler/Request/ListToolsHandlerTest.php +++ b/tests/Unit/Server/Handler/Request/ListToolsHandlerTest.php @@ -289,6 +289,7 @@ private function addToolsToRegistry(int $count): void for ($i = 0; $i < $count; ++$i) { $tool = new Tool( name: "tool_$i", + title: null, inputSchema: [ 'type' => 'object', 'properties' => [],