Skip to content

Commit bfcef49

Browse files
committed
feature #224 [MCP SDK] Update Tool schema to match 2025-06-18 and introduce ToolAnnotationsInterface (xavierleune)
This PR was merged into the main branch. Discussion ---------- [MCP SDK] Update Tool schema to match 2025-06-18 and introduce `ToolAnnotationsInterface` | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Docs? | no | Issues | -- | License | MIT The current Tool definition lacks a few fields, this PR covers it. Commits ------- 98c3704 [MCP SDK] Update Tool schema to match 2025-06-18 and introduce ToolAnnotationsInterface
2 parents 3e0fedc + 98c3704 commit bfcef49

File tree

7 files changed

+173
-4
lines changed

7 files changed

+173
-4
lines changed

demo/src/MCP/Tools/CurrentTimeTool.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace App\MCP\Tools;
1313

1414
use Symfony\AI\McpSdk\Capability\Tool\MetadataInterface;
15+
use Symfony\AI\McpSdk\Capability\Tool\ToolAnnotationsInterface;
1516
use Symfony\AI\McpSdk\Capability\Tool\ToolCall;
1617
use Symfony\AI\McpSdk\Capability\Tool\ToolCallResult;
1718
use Symfony\AI\McpSdk\Capability\Tool\ToolExecutorInterface;
@@ -54,4 +55,19 @@ public function getInputSchema(): array
5455
'required' => ['format'],
5556
];
5657
}
58+
59+
public function getOutputSchema(): ?array
60+
{
61+
return null;
62+
}
63+
64+
public function getTitle(): ?string
65+
{
66+
return null;
67+
}
68+
69+
public function getAnnotations(): ?ToolAnnotationsInterface
70+
{
71+
return null;
72+
}
5773
}

src/mcp-sdk/examples/cli/src/ExampleTool.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace App;
1313

1414
use Symfony\AI\McpSdk\Capability\Tool\MetadataInterface;
15+
use Symfony\AI\McpSdk\Capability\Tool\ToolAnnotationsInterface;
1516
use Symfony\AI\McpSdk\Capability\Tool\ToolCall;
1617
use Symfony\AI\McpSdk\Capability\Tool\ToolCallResult;
1718
use Symfony\AI\McpSdk\Capability\Tool\ToolExecutorInterface;
@@ -51,4 +52,19 @@ public function getInputSchema(): array
5152
'required' => [],
5253
];
5354
}
55+
56+
public function getOutputSchema(): ?array
57+
{
58+
return null;
59+
}
60+
61+
public function getTitle(): ?string
62+
{
63+
return null;
64+
}
65+
66+
public function getAnnotations(): ?ToolAnnotationsInterface
67+
{
68+
return null;
69+
}
5470
}

src/mcp-sdk/src/Capability/Tool/IdentifierInterface.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,8 @@
1313

1414
interface IdentifierInterface
1515
{
16+
/**
17+
* @return string intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn’t present)
18+
*/
1619
public function getName(): string;
1720
}

src/mcp-sdk/src/Capability/Tool/MetadataInterface.php

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,64 @@
1111

1212
namespace Symfony\AI\McpSdk\Capability\Tool;
1313

14+
/**
15+
* @see {https://modelcontextprotocol.io/specification/2025-06-18/schema#tool}
16+
*/
1417
interface MetadataInterface extends IdentifierInterface
1518
{
16-
public function getDescription(): string;
19+
/**
20+
* @return string|null A human-readable description of the tool.
21+
* 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
22+
*
23+
* @see https://modelcontextprotocol.io/specification/2025-06-18/schema#tool-description
24+
*/
25+
public function getDescription(): ?string;
1726

1827
/**
1928
* @return array{
20-
* type?: string,
29+
* type?: 'object',
2130
* required?: list<string>,
2231
* properties?: array<string, array{
2332
* type: string,
2433
* description?: string,
2534
* }>,
2635
* }
36+
*
37+
* @see https://modelcontextprotocol.io/specification/2025-06-18/schema#tool-inputschema
2738
*/
2839
public function getInputSchema(): array;
40+
41+
/**
42+
* @return array{
43+
* type?: 'object',
44+
* required?: list<string>,
45+
* properties?: array<string, array{
46+
* type: string,
47+
* description?: string,
48+
* }>,
49+
* }|null
50+
*
51+
* @see https://modelcontextprotocol.io/specification/2025-06-18/schema#tool-outputschema
52+
*/
53+
public function getOutputSchema(): ?array;
54+
55+
/**
56+
* @return string|null Intended for UI and end-user contexts — optimized to be human-readable and easily understood, even by those unfamiliar with domain-specific terminology.
57+
*
58+
* If not provided, the name should be used for display (except for Tool, where annotations.title should be given precedence over using name, if present).
59+
*
60+
* @see https://modelcontextprotocol.io/specification/2025-06-18/schema#tool-title
61+
*/
62+
public function getTitle(): ?string;
63+
64+
/**
65+
* @return ToolAnnotationsInterface|null Additional properties describing a Tool to clients.
66+
*
67+
* NOTE: all properties in ToolAnnotations are hints. They are not guaranteed to provide a faithful description of tool behavior (including descriptive properties like title).
68+
*
69+
* Clients should never make tool use decisions based on ToolAnnotations received from untrusted servers.
70+
*
71+
* @see https://modelcontextprotocol.io/specification/2025-06-18/schema#tool-annotations
72+
*/
73+
public function getAnnotations(): ?ToolAnnotationsInterface;
2974
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\AI\McpSdk\Capability\Tool;
13+
14+
interface ToolAnnotationsInterface
15+
{
16+
/**
17+
* @return bool|null If true, the tool may perform destructive updates to its environment. If false, the tool performs only additive updates.
18+
*
19+
* (This property is meaningful only when readOnlyHint == false)
20+
*
21+
* Default: true
22+
*
23+
* @see https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-destructivehint
24+
*/
25+
public function getDestructiveHint(): ?bool;
26+
27+
/**
28+
* @return bool|null If true, calling the tool repeatedly with the same arguments will have no additional effect on the its environment.
29+
*
30+
* (This property is meaningful only when readOnlyHint == false)
31+
*
32+
* Default: false
33+
*
34+
* @see https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-idempotenthint
35+
*/
36+
public function getIdempotentHint(): ?bool;
37+
38+
/**
39+
* @return bool|null If true, this tool may interact with an “open world” of external entities. If false, the tool’s domain of interaction is closed. For example, the world of a web search tool is open, whereas that of a memory tool is not.
40+
*
41+
* Default: true
42+
*
43+
* @see https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-openworldhint
44+
*/
45+
public function getOpenWorldHint(): ?bool;
46+
47+
/**
48+
* @return bool|null If true, the tool does not modify its environment.
49+
*
50+
* Default: false
51+
*
52+
* @see https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-readonlyhint
53+
*/
54+
public function getReadOnlyHint(): ?bool;
55+
56+
/**
57+
* @return string|null A human-readable title for the tool
58+
*
59+
* @see https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations-title
60+
*/
61+
public function getTitle(): ?string;
62+
}

src/mcp-sdk/src/Server/RequestHandler/ToolListHandler.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,25 @@ public function createResponse(Request $message): Response
3636
foreach ($metadataList as $tool) {
3737
$nextCursor = $tool->getName();
3838
$inputSchema = $tool->getInputSchema();
39-
$tools[] = [
39+
$annotations = null === $tool->getAnnotations() ? [] : array_filter([
40+
'title' => $tool->getAnnotations()->getTitle(),
41+
'destructiveHint' => $tool->getAnnotations()->getDestructiveHint(),
42+
'idempotentHint' => $tool->getAnnotations()->getIdempotentHint(),
43+
'openWorldHint' => $tool->getAnnotations()->getOpenWorldHint(),
44+
'readOnlyHint' => $tool->getAnnotations()->getReadOnlyHint(),
45+
], static fn ($value) => null !== $value);
46+
47+
$tools[] = array_filter([
4048
'name' => $tool->getName(),
4149
'description' => $tool->getDescription(),
4250
'inputSchema' => [] === $inputSchema ? [
4351
'type' => 'object',
4452
'$schema' => 'http://json-schema.org/draft-07/schema#',
4553
] : $inputSchema,
46-
];
54+
'title' => $tool->getTitle(),
55+
'outputSchema' => $tool->getOutputSchema(),
56+
'annotations' => (object) $annotations,
57+
], static fn ($value) => null !== $value);
4758
}
4859

4960
$result = [

src/mcp-sdk/tests/Server/RequestHandler/ToolListHandlerTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use PHPUnit\Framework\TestCase;
1818
use Symfony\AI\McpSdk\Capability\Tool\CollectionInterface;
1919
use Symfony\AI\McpSdk\Capability\Tool\MetadataInterface;
20+
use Symfony\AI\McpSdk\Capability\Tool\ToolAnnotationsInterface;
2021
use Symfony\AI\McpSdk\Message\Request;
2122
use Symfony\AI\McpSdk\Server\RequestHandler\ToolListHandler;
2223

@@ -102,6 +103,21 @@ public function getInputSchema(): array
102103
{
103104
return ['type' => 'object'];
104105
}
106+
107+
public function getOutputSchema(): ?array
108+
{
109+
return null;
110+
}
111+
112+
public function getTitle(): string
113+
{
114+
return 'Test tool';
115+
}
116+
117+
public function getAnnotations(): ?ToolAnnotationsInterface
118+
{
119+
return null;
120+
}
105121
};
106122
}
107123
}

0 commit comments

Comments
 (0)