Skip to content

Commit d3a0791

Browse files
authored
Roll out meta properties in attributes and more schema classes (#129)
1 parent 09596c0 commit d3a0791

File tree

16 files changed

+178
-88
lines changed

16 files changed

+178
-88
lines changed

src/Capability/Attribute/McpPrompt.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@
2121
final class McpPrompt
2222
{
2323
/**
24-
* @param ?string $name overrides the prompt name (defaults to method name)
25-
* @param ?string $description Optional description of the prompt. Defaults to method DocBlock summary.
24+
* @param ?string $name overrides the prompt name (defaults to method name)
25+
* @param ?string $description Optional description of the prompt. Defaults to method DocBlock summary.
26+
* @param ?array<string, mixed> $meta Optional metadata
2627
*/
2728
public function __construct(
2829
public ?string $name = null,
2930
public ?string $description = null,
31+
public ?array $meta = null,
3032
) {
3133
}
3234
}

src/Capability/Attribute/McpResource.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@
2323
final class McpResource
2424
{
2525
/**
26-
* @param string $uri The specific URI identifying this resource instance. Must be unique within the server.
27-
* @param ?string $name A human-readable name for this resource. If null, a default might be generated from the method name.
28-
* @param ?string $description An optional description of the resource. Defaults to class DocBlock summary.
29-
* @param ?string $mimeType the MIME type, if known and constant for this resource
30-
* @param ?int $size the size in bytes, if known and constant
31-
* @param Annotations|null $annotations optional annotations describing the resource
26+
* @param string $uri The specific URI identifying this resource instance. Must be unique within the server.
27+
* @param ?string $name A human-readable name for this resource. If null, a default might be generated from the method name.
28+
* @param ?string $description An optional description of the resource. Defaults to class DocBlock summary.
29+
* @param ?string $mimeType the MIME type, if known and constant for this resource
30+
* @param ?int $size the size in bytes, if known and constant
31+
* @param Annotations|null $annotations optional annotations describing the resource
32+
* @param ?array<string, mixed> $meta Optional metadata
3233
*/
3334
public function __construct(
3435
public string $uri,
@@ -37,6 +38,7 @@ public function __construct(
3738
public ?string $mimeType = null,
3839
public ?int $size = null,
3940
public ?Annotations $annotations = null,
41+
public ?array $meta = null,
4042
) {
4143
}
4244
}

src/Capability/Attribute/McpResourceTemplate.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,20 @@
2323
final class McpResourceTemplate
2424
{
2525
/**
26-
* @param string $uriTemplate the URI template string (RFC 6570)
27-
* @param ?string $name A human-readable name for the template type. If null, a default might be generated from the method name.
28-
* @param ?string $description Optional description. Defaults to class DocBlock summary.
29-
* @param ?string $mimeType optional default MIME type for matching resources
30-
* @param ?Annotations $annotations optional annotations describing the resource template
26+
* @param string $uriTemplate the URI template string (RFC 6570)
27+
* @param ?string $name A human-readable name for the template type. If null, a default might be generated from the method name.
28+
* @param ?string $description Optional description. Defaults to class DocBlock summary.
29+
* @param ?string $mimeType optional default MIME type for matching resources
30+
* @param ?Annotations $annotations optional annotations describing the resource template
31+
* @param ?array<string, mixed> $meta Optional metadata
3132
*/
3233
public function __construct(
3334
public string $uriTemplate,
3435
public ?string $name = null,
3536
public ?string $description = null,
3637
public ?string $mimeType = null,
3738
public ?Annotations $annotations = null,
39+
public ?array $meta = null,
3840
) {
3941
}
4042
}

src/Capability/Attribute/McpTool.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@
2020
class McpTool
2121
{
2222
/**
23-
* @param string|null $name The name of the tool (defaults to the method name)
24-
* @param string|null $description The description of the tool (defaults to the DocBlock/inferred)
25-
* @param ToolAnnotations|null $annotations Optional annotations describing tool behavior
23+
* @param string|null $name The name of the tool (defaults to the method name)
24+
* @param string|null $description The description of the tool (defaults to the DocBlock/inferred)
25+
* @param ToolAnnotations|null $annotations Optional annotations describing tool behavior
26+
* @param ?array<string, mixed> $meta Optional metadata
2627
*/
2728
public function __construct(
2829
public ?string $name = null,
2930
public ?string $description = null,
3031
public ?ToolAnnotations $annotations = null,
32+
public ?array $meta = null,
3133
) {
3234
}
3335
}

src/Capability/Discovery/Discoverer.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,8 @@ private function processMethod(\ReflectionMethod $method, array &$discoveredCoun
222222
$name = $instance->name ?? ('__invoke' === $methodName ? $classShortName : $methodName);
223223
$description = $instance->description ?? $this->docBlockParser->getSummary($docBlock) ?? null;
224224
$inputSchema = $this->schemaGenerator->generate($method);
225-
$tool = new Tool($name, $inputSchema, $description, $instance->annotations);
225+
$meta = $instance->meta ?? null;
226+
$tool = new Tool($name, $inputSchema, $description, $instance->annotations, $meta);
226227
$tools[$name] = new ToolReference($tool, [$className, $methodName], false);
227228
++$discoveredCount['tools'];
228229
break;
@@ -234,8 +235,10 @@ private function processMethod(\ReflectionMethod $method, array &$discoveredCoun
234235
$mimeType = $instance->mimeType;
235236
$size = $instance->size;
236237
$annotations = $instance->annotations;
237-
$resource = new Resource($instance->uri, $name, $description, $mimeType, $annotations, $size);
238+
$meta = $instance->meta;
239+
$resource = new Resource($instance->uri, $name, $description, $mimeType, $annotations, $size, $meta);
238240
$resources[$instance->uri] = new ResourceReference($resource, [$className, $methodName], false);
241+
239242
++$discoveredCount['resources'];
240243
break;
241244

@@ -253,7 +256,8 @@ private function processMethod(\ReflectionMethod $method, array &$discoveredCoun
253256
$paramTag = $paramTags['$'.$param->getName()] ?? null;
254257
$arguments[] = new PromptArgument($param->getName(), $paramTag ? trim((string) $paramTag->getDescription()) : null, !$param->isOptional() && !$param->isDefaultValueAvailable());
255258
}
256-
$prompt = new Prompt($name, $description, $arguments);
259+
$meta = $instance->meta ?? null;
260+
$prompt = new Prompt($name, $description, $arguments, $meta);
257261
$completionProviders = $this->getCompletionProviders($method);
258262
$prompts[$name] = new PromptReference($prompt, [$className, $methodName], false, $completionProviders);
259263
++$discoveredCount['prompts'];
@@ -265,7 +269,8 @@ private function processMethod(\ReflectionMethod $method, array &$discoveredCoun
265269
$description = $instance->description ?? $this->docBlockParser->getSummary($docBlock) ?? null;
266270
$mimeType = $instance->mimeType;
267271
$annotations = $instance->annotations;
268-
$resourceTemplate = new ResourceTemplate($instance->uriTemplate, $name, $description, $mimeType, $annotations);
272+
$meta = $instance->meta ?? null;
273+
$resourceTemplate = new ResourceTemplate($instance->uriTemplate, $name, $description, $mimeType, $annotations, $meta);
269274
$completionProviders = $this->getCompletionProviders($method);
270275
$resourceTemplates[$instance->uriTemplate] = new ResourceTemplateReference($resourceTemplate, [$className, $methodName], false, $completionProviders);
271276
++$discoveredCount['resourceTemplates'];

src/Capability/Registry/Loader/ArrayLoader.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ final class ArrayLoader implements LoaderInterface
4545
* name: ?string,
4646
* description: ?string,
4747
* annotations: ?ToolAnnotations,
48+
* meta: ?array<string, mixed>
4849
* }[] $tools
4950
* @param array{
5051
* handler: Handler,
@@ -54,6 +55,7 @@ final class ArrayLoader implements LoaderInterface
5455
* mimeType: ?string,
5556
* size: int|null,
5657
* annotations: ?Annotations,
58+
* meta: ?array<string, mixed>
5759
* }[] $resources
5860
* @param array{
5961
* handler: Handler,
@@ -62,11 +64,13 @@ final class ArrayLoader implements LoaderInterface
6264
* description: ?string,
6365
* mimeType: ?string,
6466
* annotations: ?Annotations,
67+
* meta: ?array<string, mixed>
6568
* }[] $resourceTemplates
6669
* @param array{
6770
* handler: Handler,
6871
* name: ?string,
6972
* description: ?string,
73+
* meta: ?array<string, mixed>
7074
* }[] $prompts
7175
*/
7276
public function __construct(
@@ -102,7 +106,7 @@ public function load(ReferenceRegistryInterface $registry): void
102106

103107
$inputSchema = $data['inputSchema'] ?? $schemaGenerator->generate($reflection);
104108

105-
$tool = new Tool($name, $inputSchema, $description, $data['annotations']);
109+
$tool = new Tool($name, $inputSchema, $description, $data['annotations'], $data['meta'] ?? null);
106110
$registry->registerTool($tool, $data['handler'], true);
107111

108112
$handlerDesc = $this->getHandlerDescription($data['handler']);
@@ -137,8 +141,9 @@ public function load(ReferenceRegistryInterface $registry): void
137141
$mimeType = $data['mimeType'];
138142
$size = $data['size'];
139143
$annotations = $data['annotations'];
144+
$meta = $data['meta'];
140145

141-
$resource = new Resource($uri, $name, $description, $mimeType, $annotations, $size);
146+
$resource = new Resource($uri, $name, $description, $mimeType, $annotations, $size, $meta);
142147
$registry->registerResource($resource, $data['handler'], true);
143148

144149
$handlerDesc = $this->getHandlerDescription($data['handler']);
@@ -172,8 +177,9 @@ public function load(ReferenceRegistryInterface $registry): void
172177
$uriTemplate = $data['uriTemplate'];
173178
$mimeType = $data['mimeType'];
174179
$annotations = $data['annotations'];
180+
$meta = $data['meta'];
175181

176-
$template = new ResourceTemplate($uriTemplate, $name, $description, $mimeType, $annotations);
182+
$template = new ResourceTemplate($uriTemplate, $name, $description, $mimeType, $annotations, $meta);
177183
$completionProviders = $this->getCompletionProviders($reflection);
178184
$registry->registerResourceTemplate($template, $data['handler'], $completionProviders, true);
179185

@@ -224,8 +230,8 @@ public function load(ReferenceRegistryInterface $registry): void
224230
!$param->isOptional() && !$param->isDefaultValueAvailable(),
225231
);
226232
}
227-
228-
$prompt = new Prompt($name, $description, $arguments);
233+
$meta = $data['meta'];
234+
$prompt = new Prompt($name, $description, $arguments, $meta);
229235
$completionProviders = $this->getCompletionProviders($reflection);
230236
$registry->registerPrompt($prompt, $data['handler'], $completionProviders, true);
231237

src/Capability/Registry/ResourceReference.php

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,11 @@ public function formatResult(mixed $readResult, string $uri, ?string $mimeType =
6868
return [$readResult->resource];
6969
}
7070

71+
$meta = $this->schema->meta;
72+
7173
if (\is_array($readResult)) {
7274
if (empty($readResult)) {
73-
return [new TextResourceContents($uri, 'application/json', '[]')];
75+
return [new TextResourceContents($uri, 'application/json', '[]', $meta)];
7476
}
7577

7678
$allAreResourceContents = true;
@@ -118,14 +120,15 @@ public function formatResult(mixed $readResult, string $uri, ?string $mimeType =
118120
if (\is_string($readResult)) {
119121
$mimeType = $mimeType ?? $this->guessMimeTypeFromString($readResult);
120122

121-
return [new TextResourceContents($uri, $mimeType, $readResult)];
123+
return [new TextResourceContents($uri, $mimeType, $readResult, $meta)];
122124
}
123125

124126
if (\is_resource($readResult) && 'stream' === get_resource_type($readResult)) {
125127
$result = BlobResourceContents::fromStream(
126128
$uri,
127129
$readResult,
128-
$mimeType ?? 'application/octet-stream'
130+
$mimeType ?? 'application/octet-stream',
131+
$meta
129132
);
130133

131134
@fclose($readResult);
@@ -136,21 +139,21 @@ public function formatResult(mixed $readResult, string $uri, ?string $mimeType =
136139
if (\is_array($readResult) && isset($readResult['blob']) && \is_string($readResult['blob'])) {
137140
$mimeType = $readResult['mimeType'] ?? $mimeType ?? 'application/octet-stream';
138141

139-
return [new BlobResourceContents($uri, $mimeType, $readResult['blob'])];
142+
return [new BlobResourceContents($uri, $mimeType, $readResult['blob'], $meta)];
140143
}
141144

142145
if (\is_array($readResult) && isset($readResult['text']) && \is_string($readResult['text'])) {
143146
$mimeType = $readResult['mimeType'] ?? $mimeType ?? 'text/plain';
144147

145-
return [new TextResourceContents($uri, $mimeType, $readResult['text'])];
148+
return [new TextResourceContents($uri, $mimeType, $readResult['text'], $meta)];
146149
}
147150

148151
if ($readResult instanceof \SplFileInfo && $readResult->isFile() && $readResult->isReadable()) {
149152
if ($mimeType && str_contains(strtolower($mimeType), 'text')) {
150-
return [new TextResourceContents($uri, $mimeType, file_get_contents($readResult->getPathname()))];
153+
return [new TextResourceContents($uri, $mimeType, file_get_contents($readResult->getPathname()), $meta)];
151154
}
152155

153-
return [BlobResourceContents::fromSplFileInfo($uri, $readResult, $mimeType)];
156+
return [BlobResourceContents::fromSplFileInfo($uri, $readResult, $mimeType, $meta)];
154157
}
155158

156159
if (\is_array($readResult)) {
@@ -159,7 +162,7 @@ public function formatResult(mixed $readResult, string $uri, ?string $mimeType =
159162
try {
160163
$jsonString = json_encode($readResult, \JSON_THROW_ON_ERROR | \JSON_PRETTY_PRINT);
161164

162-
return [new TextResourceContents($uri, $mimeType, $jsonString)];
165+
return [new TextResourceContents($uri, $mimeType, $jsonString, $meta)];
163166
} catch (\JsonException $e) {
164167
throw new RuntimeException(\sprintf('Failed to encode array as JSON for URI "%s": %s', $uri, $e->getMessage()));
165168
}
@@ -169,7 +172,7 @@ public function formatResult(mixed $readResult, string $uri, ?string $mimeType =
169172
$jsonString = json_encode($readResult, \JSON_THROW_ON_ERROR | \JSON_PRETTY_PRINT);
170173
$mimeType = $mimeType ?? 'application/json';
171174

172-
return [new TextResourceContents($uri, $mimeType, $jsonString)];
175+
return [new TextResourceContents($uri, $mimeType, $jsonString, $meta)];
173176
} catch (\JsonException $e) {
174177
throw new RuntimeException(\sprintf('Failed to encode array as JSON for URI "%s": %s', $uri, $e->getMessage()));
175178
}

src/Capability/Registry/ResourceTemplateReference.php

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,11 @@ public function formatResult(mixed $readResult, string $uri, ?string $mimeType =
101101
return [$readResult->resource];
102102
}
103103

104+
$meta = $this->resourceTemplate->meta;
105+
104106
if (\is_array($readResult)) {
105107
if (empty($readResult)) {
106-
return [new TextResourceContents($uri, 'application/json', '[]')];
108+
return [new TextResourceContents($uri, 'application/json', '[]', $meta)];
107109
}
108110

109111
$allAreResourceContents = true;
@@ -151,14 +153,15 @@ public function formatResult(mixed $readResult, string $uri, ?string $mimeType =
151153
if (\is_string($readResult)) {
152154
$mimeType = $mimeType ?? $this->guessMimeTypeFromString($readResult);
153155

154-
return [new TextResourceContents($uri, $mimeType, $readResult)];
156+
return [new TextResourceContents($uri, $mimeType, $readResult, $meta)];
155157
}
156158

157159
if (\is_resource($readResult) && 'stream' === get_resource_type($readResult)) {
158160
$result = BlobResourceContents::fromStream(
159161
$uri,
160162
$readResult,
161-
$mimeType ?? 'application/octet-stream'
163+
$mimeType ?? 'application/octet-stream',
164+
$meta
162165
);
163166

164167
@fclose($readResult);
@@ -169,21 +172,21 @@ public function formatResult(mixed $readResult, string $uri, ?string $mimeType =
169172
if (\is_array($readResult) && isset($readResult['blob']) && \is_string($readResult['blob'])) {
170173
$mimeType = $readResult['mimeType'] ?? $mimeType ?? 'application/octet-stream';
171174

172-
return [new BlobResourceContents($uri, $mimeType, $readResult['blob'])];
175+
return [new BlobResourceContents($uri, $mimeType, $readResult['blob'], $meta)];
173176
}
174177

175178
if (\is_array($readResult) && isset($readResult['text']) && \is_string($readResult['text'])) {
176179
$mimeType = $readResult['mimeType'] ?? $mimeType ?? 'text/plain';
177180

178-
return [new TextResourceContents($uri, $mimeType, $readResult['text'])];
181+
return [new TextResourceContents($uri, $mimeType, $readResult['text'], $meta)];
179182
}
180183

181184
if ($readResult instanceof \SplFileInfo && $readResult->isFile() && $readResult->isReadable()) {
182185
if ($mimeType && str_contains(strtolower($mimeType), 'text')) {
183-
return [new TextResourceContents($uri, $mimeType, file_get_contents($readResult->getPathname()))];
186+
return [new TextResourceContents($uri, $mimeType, file_get_contents($readResult->getPathname()), $meta)];
184187
}
185188

186-
return [BlobResourceContents::fromSplFileInfo($uri, $readResult, $mimeType)];
189+
return [BlobResourceContents::fromSplFileInfo($uri, $readResult, $mimeType, $meta)];
187190
}
188191

189192
if (\is_array($readResult)) {
@@ -192,7 +195,7 @@ public function formatResult(mixed $readResult, string $uri, ?string $mimeType =
192195
try {
193196
$jsonString = json_encode($readResult, \JSON_THROW_ON_ERROR | \JSON_PRETTY_PRINT);
194197

195-
return [new TextResourceContents($uri, $mimeType, $jsonString)];
198+
return [new TextResourceContents($uri, $mimeType, $jsonString, $meta)];
196199
} catch (\JsonException $e) {
197200
throw new RuntimeException("Failed to encode array as JSON for URI '{$uri}': {$e->getMessage()}");
198201
}
@@ -202,7 +205,7 @@ public function formatResult(mixed $readResult, string $uri, ?string $mimeType =
202205
$jsonString = json_encode($readResult, \JSON_THROW_ON_ERROR | \JSON_PRETTY_PRINT);
203206
$mimeType = $mimeType ?? 'application/json';
204207

205-
return [new TextResourceContents($uri, $mimeType, $jsonString)];
208+
return [new TextResourceContents($uri, $mimeType, $jsonString, $meta)];
206209
} catch (\JsonException $e) {
207210
throw new RuntimeException("Failed to encode array as JSON for URI '{$uri}': {$e->getMessage()}");
208211
}

0 commit comments

Comments
 (0)