Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/Capability/Attribute/McpPrompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@
final class McpPrompt
{
/**
* @param ?string $name overrides the prompt name (defaults to method name)
* @param ?string $description Optional description of the prompt. Defaults to method DocBlock summary.
* @param ?string $name overrides the prompt name (defaults to method name)
* @param ?string $description Optional description of the prompt. Defaults to method DocBlock summary.
* @param ?array<string, mixed> $meta Optional metadata
*/
public function __construct(
public ?string $name = null,
public ?string $description = null,
public ?array $meta = null,
) {
}
}
14 changes: 8 additions & 6 deletions src/Capability/Attribute/McpResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@
final class McpResource
{
/**
* @param string $uri The specific URI identifying this resource instance. Must be unique within the server.
* @param ?string $name A human-readable name for this resource. If null, a default might be generated from the method name.
* @param ?string $description An optional description of the resource. Defaults to class DocBlock summary.
* @param ?string $mimeType the MIME type, if known and constant for this resource
* @param ?int $size the size in bytes, if known and constant
* @param Annotations|null $annotations optional annotations describing the resource
* @param string $uri The specific URI identifying this resource instance. Must be unique within the server.
* @param ?string $name A human-readable name for this resource. If null, a default might be generated from the method name.
* @param ?string $description An optional description of the resource. Defaults to class DocBlock summary.
* @param ?string $mimeType the MIME type, if known and constant for this resource
* @param ?int $size the size in bytes, if known and constant
* @param Annotations|null $annotations optional annotations describing the resource
* @param ?array<string, mixed> $meta Optional metadata
*/
public function __construct(
public string $uri,
Expand All @@ -37,6 +38,7 @@ public function __construct(
public ?string $mimeType = null,
public ?int $size = null,
public ?Annotations $annotations = null,
public ?array $meta = null,
) {
}
}
12 changes: 7 additions & 5 deletions src/Capability/Attribute/McpResourceTemplate.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,20 @@
final class McpResourceTemplate
{
/**
* @param string $uriTemplate the URI template string (RFC 6570)
* @param ?string $name A human-readable name for the template type. If null, a default might be generated from the method name.
* @param ?string $description Optional description. Defaults to class DocBlock summary.
* @param ?string $mimeType optional default MIME type for matching resources
* @param ?Annotations $annotations optional annotations describing the resource template
* @param string $uriTemplate the URI template string (RFC 6570)
* @param ?string $name A human-readable name for the template type. If null, a default might be generated from the method name.
* @param ?string $description Optional description. Defaults to class DocBlock summary.
* @param ?string $mimeType optional default MIME type for matching resources
* @param ?Annotations $annotations optional annotations describing the resource template
* @param ?array<string, mixed> $meta Optional metadata
*/
public function __construct(
public string $uriTemplate,
public ?string $name = null,
public ?string $description = null,
public ?string $mimeType = null,
public ?Annotations $annotations = null,
public ?array $meta = null,
) {
}
}
8 changes: 5 additions & 3 deletions src/Capability/Attribute/McpTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@
class McpTool
{
/**
* @param string|null $name The name of the tool (defaults to the method name)
* @param string|null $description The description of the tool (defaults to the DocBlock/inferred)
* @param ToolAnnotations|null $annotations Optional annotations describing tool behavior
* @param string|null $name The name of the tool (defaults to the method name)
* @param string|null $description The description of the tool (defaults to the DocBlock/inferred)
* @param ToolAnnotations|null $annotations Optional annotations describing tool behavior
* @param ?array<string, mixed> $meta Optional metadata
*/
public function __construct(
public ?string $name = null,
public ?string $description = null,
public ?ToolAnnotations $annotations = null,
public ?array $meta = null,
) {
}
}
13 changes: 9 additions & 4 deletions src/Capability/Discovery/Discoverer.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ private function processMethod(\ReflectionMethod $method, array &$discoveredCoun
$name = $instance->name ?? ('__invoke' === $methodName ? $classShortName : $methodName);
$description = $instance->description ?? $this->docBlockParser->getSummary($docBlock) ?? null;
$inputSchema = $this->schemaGenerator->generate($method);
$tool = new Tool($name, $inputSchema, $description, $instance->annotations);
$meta = $instance->meta ?? null;
$tool = new Tool($name, $inputSchema, $description, $instance->annotations, $meta);
$tools[$name] = new ToolReference($tool, [$className, $methodName], false);
++$discoveredCount['tools'];
break;
Expand All @@ -234,8 +235,10 @@ private function processMethod(\ReflectionMethod $method, array &$discoveredCoun
$mimeType = $instance->mimeType;
$size = $instance->size;
$annotations = $instance->annotations;
$resource = new Resource($instance->uri, $name, $description, $mimeType, $annotations, $size);
$meta = $instance->meta;
$resource = new Resource($instance->uri, $name, $description, $mimeType, $annotations, $size, $meta);
$resources[$instance->uri] = new ResourceReference($resource, [$className, $methodName], false);

++$discoveredCount['resources'];
break;

Expand All @@ -253,7 +256,8 @@ private function processMethod(\ReflectionMethod $method, array &$discoveredCoun
$paramTag = $paramTags['$'.$param->getName()] ?? null;
$arguments[] = new PromptArgument($param->getName(), $paramTag ? trim((string) $paramTag->getDescription()) : null, !$param->isOptional() && !$param->isDefaultValueAvailable());
}
$prompt = new Prompt($name, $description, $arguments);
$meta = $instance->meta ?? null;
$prompt = new Prompt($name, $description, $arguments, $meta);
$completionProviders = $this->getCompletionProviders($method);
$prompts[$name] = new PromptReference($prompt, [$className, $methodName], false, $completionProviders);
++$discoveredCount['prompts'];
Expand All @@ -265,7 +269,8 @@ private function processMethod(\ReflectionMethod $method, array &$discoveredCoun
$description = $instance->description ?? $this->docBlockParser->getSummary($docBlock) ?? null;
$mimeType = $instance->mimeType;
$annotations = $instance->annotations;
$resourceTemplate = new ResourceTemplate($instance->uriTemplate, $name, $description, $mimeType, $annotations);
$meta = $instance->meta ?? null;
$resourceTemplate = new ResourceTemplate($instance->uriTemplate, $name, $description, $mimeType, $annotations, $meta);
$completionProviders = $this->getCompletionProviders($method);
$resourceTemplates[$instance->uriTemplate] = new ResourceTemplateReference($resourceTemplate, [$className, $methodName], false, $completionProviders);
++$discoveredCount['resourceTemplates'];
Expand Down
16 changes: 11 additions & 5 deletions src/Capability/Registry/Loader/ArrayLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ final class ArrayLoader implements LoaderInterface
* name: ?string,
* description: ?string,
* annotations: ?ToolAnnotations,
* meta: ?array<string, mixed>
* }[] $tools
* @param array{
* handler: Handler,
Expand All @@ -54,6 +55,7 @@ final class ArrayLoader implements LoaderInterface
* mimeType: ?string,
* size: int|null,
* annotations: ?Annotations,
* meta: ?array<string, mixed>
* }[] $resources
* @param array{
* handler: Handler,
Expand All @@ -62,11 +64,13 @@ final class ArrayLoader implements LoaderInterface
* description: ?string,
* mimeType: ?string,
* annotations: ?Annotations,
* meta: ?array<string, mixed>
* }[] $resourceTemplates
* @param array{
* handler: Handler,
* name: ?string,
* description: ?string,
* meta: ?array<string, mixed>
* }[] $prompts
*/
public function __construct(
Expand Down Expand Up @@ -102,7 +106,7 @@ public function load(ReferenceRegistryInterface $registry): void

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

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

$handlerDesc = $this->getHandlerDescription($data['handler']);
Expand Down Expand Up @@ -137,8 +141,9 @@ public function load(ReferenceRegistryInterface $registry): void
$mimeType = $data['mimeType'];
$size = $data['size'];
$annotations = $data['annotations'];
$meta = $data['meta'];

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

$handlerDesc = $this->getHandlerDescription($data['handler']);
Expand Down Expand Up @@ -172,8 +177,9 @@ public function load(ReferenceRegistryInterface $registry): void
$uriTemplate = $data['uriTemplate'];
$mimeType = $data['mimeType'];
$annotations = $data['annotations'];
$meta = $data['meta'];

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

Expand Down Expand Up @@ -224,8 +230,8 @@ public function load(ReferenceRegistryInterface $registry): void
!$param->isOptional() && !$param->isDefaultValueAvailable(),
);
}

$prompt = new Prompt($name, $description, $arguments);
$meta = $data['meta'];
$prompt = new Prompt($name, $description, $arguments, $meta);
$completionProviders = $this->getCompletionProviders($reflection);
$registry->registerPrompt($prompt, $data['handler'], $completionProviders, true);

Expand Down
21 changes: 12 additions & 9 deletions src/Capability/Registry/ResourceReference.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ public function formatResult(mixed $readResult, string $uri, ?string $mimeType =
return [$readResult->resource];
}

$meta = $this->schema->meta;

if (\is_array($readResult)) {
if (empty($readResult)) {
return [new TextResourceContents($uri, 'application/json', '[]')];
return [new TextResourceContents($uri, 'application/json', '[]', $meta)];
}

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

return [new TextResourceContents($uri, $mimeType, $readResult)];
return [new TextResourceContents($uri, $mimeType, $readResult, $meta)];
}

if (\is_resource($readResult) && 'stream' === get_resource_type($readResult)) {
$result = BlobResourceContents::fromStream(
$uri,
$readResult,
$mimeType ?? 'application/octet-stream'
$mimeType ?? 'application/octet-stream',
$meta
);

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

return [new BlobResourceContents($uri, $mimeType, $readResult['blob'])];
return [new BlobResourceContents($uri, $mimeType, $readResult['blob'], $meta)];
}

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

return [new TextResourceContents($uri, $mimeType, $readResult['text'])];
return [new TextResourceContents($uri, $mimeType, $readResult['text'], $meta)];
}

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

return [BlobResourceContents::fromSplFileInfo($uri, $readResult, $mimeType)];
return [BlobResourceContents::fromSplFileInfo($uri, $readResult, $mimeType, $meta)];
}

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

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

return [new TextResourceContents($uri, $mimeType, $jsonString)];
return [new TextResourceContents($uri, $mimeType, $jsonString, $meta)];
} catch (\JsonException $e) {
throw new RuntimeException(\sprintf('Failed to encode array as JSON for URI "%s": %s', $uri, $e->getMessage()));
}
Expand Down
21 changes: 12 additions & 9 deletions src/Capability/Registry/ResourceTemplateReference.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,11 @@ public function formatResult(mixed $readResult, string $uri, ?string $mimeType =
return [$readResult->resource];
}

$meta = $this->resourceTemplate->meta;

if (\is_array($readResult)) {
if (empty($readResult)) {
return [new TextResourceContents($uri, 'application/json', '[]')];
return [new TextResourceContents($uri, 'application/json', '[]', $meta)];
}

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

return [new TextResourceContents($uri, $mimeType, $readResult)];
return [new TextResourceContents($uri, $mimeType, $readResult, $meta)];
}

if (\is_resource($readResult) && 'stream' === get_resource_type($readResult)) {
$result = BlobResourceContents::fromStream(
$uri,
$readResult,
$mimeType ?? 'application/octet-stream'
$mimeType ?? 'application/octet-stream',
$meta
);

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

return [new BlobResourceContents($uri, $mimeType, $readResult['blob'])];
return [new BlobResourceContents($uri, $mimeType, $readResult['blob'], $meta)];
}

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

return [new TextResourceContents($uri, $mimeType, $readResult['text'])];
return [new TextResourceContents($uri, $mimeType, $readResult['text'], $meta)];
}

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

return [BlobResourceContents::fromSplFileInfo($uri, $readResult, $mimeType)];
return [BlobResourceContents::fromSplFileInfo($uri, $readResult, $mimeType, $meta)];
}

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

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

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