[Server] Add support for icons on explicit builder registration and attributes (#142)

* Add support for icons on explicit builder registration and attributes

* Update docs/server-builder.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Christopher Hertel 2025-11-13 22:39:18 +01:00 • committed by GitHub
commit 056c7b6103
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 173 additions and 39 deletions

View file

@ -11,6 +11,8 @@
namespace Mcp\Capability\Attribute;
use Mcp\Schema\Icon;
/**
* Marks a PHP method as an MCP Prompt generator.
* The method should return the prompt messages, potentially using arguments for templating.
@ -23,11 +25,13 @@ 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 ?Icon[] $icons Optional list of icon URLs representing the prompt
* @param ?array<string, mixed> $meta Optional metadata
*/
public function __construct(
public ?string $name = null,
public ?string $description = null,
public ?array $icons = null,
public ?array $meta = null,
) {
}

View file

@ -12,6 +12,7 @@
namespace Mcp\Capability\Attribute;
use Mcp\Schema\Annotations;
use Mcp\Schema\Icon;
/**
* Marks a PHP class as representing or handling a specific MCP Resource instance.
@ -29,6 +30,7 @@ final class McpResource
* @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 ?Icon[] $icons Optional list of icon URLs representing the resource
* @param ?array<string, mixed> $meta Optional metadata
*/
public function __construct(
@ -38,6 +40,7 @@ final class McpResource
public ?string $mimeType = null,
public ?int $size = null,
public ?Annotations $annotations = null,
public ?array $icons = null,
public ?array $meta = null,
) {
}

View file

@ -11,6 +11,7 @@
namespace Mcp\Capability\Attribute;
use Mcp\Schema\Icon;
use Mcp\Schema\ToolAnnotations;
/**
@ -23,12 +24,14 @@ 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 ?Icon[] $icons Optional list of icon URLs representing the tool
* @param ?array<string, mixed> $meta Optional metadata
*/
public function __construct(
public ?string $name = null,
public ?string $description = null,
public ?ToolAnnotations $annotations = null,
public ?array $icons = null,
public ?array $meta = null,
) {
}

View file

@ -222,8 +222,14 @@ class Discoverer
$name = $instance->name ?? ('__invoke' === $methodName ? $classShortName : $methodName);
$description = $instance->description ?? $this->docBlockParser->getSummary($docBlock) ?? null;
$inputSchema = $this->schemaGenerator->generate($method);
$meta = $instance->meta ?? null;
$tool = new Tool($name, $inputSchema, $description, $instance->annotations, meta: $meta);
$tool = new Tool(
$name,
$inputSchema,
$description,
$instance->annotations,
$instance->icons,
$instance->meta,
);
$tools[$name] = new ToolReference($tool, [$className, $methodName], false);
++$discoveredCount['tools'];
break;
@ -232,11 +238,16 @@ class Discoverer
$docBlock = $this->docBlockParser->parseDocBlock($method->getDocComment() ?? null);
$name = $instance->name ?? ('__invoke' === $methodName ? $classShortName : $methodName);
$description = $instance->description ?? $this->docBlockParser->getSummary($docBlock) ?? null;
$mimeType = $instance->mimeType;
$size = $instance->size;
$annotations = $instance->annotations;
$meta = $instance->meta;
$resource = new Resource($instance->uri, $name, $description, $mimeType, $annotations, $size, $meta);
$resource = new Resource(
$instance->uri,
$name,
$description,
$instance->mimeType,
$instance->annotations,
$instance->size,
$instance->icons,
$instance->meta,
);
$resources[$instance->uri] = new ResourceReference($resource, [$className, $methodName], false);
++$discoveredCount['resources'];
@ -256,8 +267,7 @@ class Discoverer
$paramTag = $paramTags['$'.$param->getName()] ?? null;
$arguments[] = new PromptArgument($param->getName(), $paramTag ? trim((string) $paramTag->getDescription()) : null, !$param->isOptional() && !$param->isDefaultValueAvailable());
}
$meta = $instance->meta ?? null;
$prompt = new Prompt($name, $description, $arguments, $meta);
$prompt = new Prompt($name, $description, $arguments, $instance->icons, $instance->meta);
$completionProviders = $this->getCompletionProviders($method);
$prompts[$name] = new PromptReference($prompt, [$className, $methodName], false, $completionProviders);
++$discoveredCount['prompts'];

View file

@ -22,6 +22,7 @@ use Mcp\Capability\Registry\ElementReference;
use Mcp\Capability\Registry\ReferenceRegistryInterface;
use Mcp\Exception\ConfigurationException;
use Mcp\Schema\Annotations;
use Mcp\Schema\Icon;
use Mcp\Schema\Prompt;
use Mcp\Schema\PromptArgument;
use Mcp\Schema\Resource;
@ -45,7 +46,7 @@ final class ArrayLoader implements LoaderInterface
* name: ?string,
* description: ?string,
* annotations: ?ToolAnnotations,
* icons: ?array<int, \Mcp\Schema\Icon>,
* icons: ?Icon[],
* meta: ?array<string, mixed>
* }[] $tools
* @param array{
@ -56,6 +57,7 @@ final class ArrayLoader implements LoaderInterface
* mimeType: ?string,
* size: int|null,
* annotations: ?Annotations,
* icons: ?Icon[],
* meta: ?array<string, mixed>
* }[] $resources
* @param array{
@ -71,14 +73,15 @@ final class ArrayLoader implements LoaderInterface
* handler: Handler,
* name: ?string,
* description: ?string,
* icons: ?Icon[],
* meta: ?array<string, mixed>
* }[] $prompts
*/
public function __construct(
private array $tools = [],
private array $resources = [],
private array $resourceTemplates = [],
private array $prompts = [],
private readonly array $tools = [],
private readonly array $resources = [],
private readonly array $resourceTemplates = [],
private readonly array $prompts = [],
private LoggerInterface $logger = new NullLogger(),
) {
}
@ -145,13 +148,16 @@ final class ArrayLoader implements LoaderInterface
$description = $data['description'] ?? $docBlockParser->getSummary($docBlock) ?? null;
}
$uri = $data['uri'];
$mimeType = $data['mimeType'];
$size = $data['size'];
$annotations = $data['annotations'];
$meta = $data['meta'];
$resource = new Resource($uri, $name, $description, $mimeType, $annotations, $size, $meta);
$resource = new Resource(
$data['uri'],
$name,
$description,
$data['mimeType'],
$data['annotations'],
$data['size'],
$data['icons'],
$data['meta'],
);
$registry->registerResource($resource, $data['handler'], true);
$handlerDesc = $this->getHandlerDescription($data['handler']);
@ -182,12 +188,14 @@ final class ArrayLoader implements LoaderInterface
$description = $data['description'] ?? $docBlockParser->getSummary($docBlock) ?? null;
}
$uriTemplate = $data['uriTemplate'];
$mimeType = $data['mimeType'];
$annotations = $data['annotations'];
$meta = $data['meta'];
$template = new ResourceTemplate($uriTemplate, $name, $description, $mimeType, $annotations, $meta);
$template = new ResourceTemplate(
$data['uriTemplate'],
$name,
$description,
$data['mimeType'],
$data['annotations'],
$data['meta'],
);
$completionProviders = $this->getCompletionProviders($reflection);
$registry->registerResourceTemplate($template, $data['handler'], $completionProviders, true);
@ -238,8 +246,7 @@ final class ArrayLoader implements LoaderInterface
!$param->isOptional() && !$param->isDefaultValueAvailable(),
);
}
$meta = $data['meta'];
$prompt = new Prompt($name, $description, $arguments, $meta);
$prompt = new Prompt($name, $description, $arguments, $data['icons'], $data['meta']);
$completionProviders = $this->getCompletionProviders($reflection);
$registry->registerPrompt($prompt, $data['handler'], $completionProviders, true);

View file

@ -83,7 +83,7 @@ final class Builder
* name: ?string,
* description: ?string,
* annotations: ?ToolAnnotations,
* icons: ?array<int, Icon>,
* icons: ?Icon[],
* meta: ?array<string, mixed>
* }[]
*/
@ -98,6 +98,7 @@ final class Builder
* mimeType: ?string,
* size: int|null,
* annotations: ?Annotations,
* icons: ?Icon[],
* meta: ?array<string, mixed>
* }[]
*/
@ -121,6 +122,7 @@ final class Builder
* handler: Handler,
* name: ?string,
* description: ?string,
* icons: ?Icon[],
* meta: ?array<string, mixed>
* }[]
*/
@ -316,7 +318,7 @@ final class Builder
*
* @param Handler $handler
* @param array<string, mixed>|null $inputSchema
* @param Icon[]|null $icons
* @param ?Icon[] $icons
* @param array<string, mixed>|null $meta
*/
public function addTool(
@ -328,7 +330,15 @@ final class Builder
?array $icons = null,
?array $meta = null,
): self {
$this->tools[] = compact('handler', 'name', 'description', 'annotations', 'inputSchema', 'icons', 'meta');
$this->tools[] = compact(
'handler',
'name',
'description',
'annotations',
'inputSchema',
'icons',
'meta',
);
return $this;
}
@ -337,6 +347,7 @@ final class Builder
* Manually registers a resource handler.
*
* @param Handler $handler
* @param ?Icon[] $icons
* @param array<string, mixed>|null $meta
*/
public function addResource(
@ -347,9 +358,20 @@ final class Builder
?string $mimeType = null,
?int $size = null,
?Annotations $annotations = null,
?array $icons = null,
?array $meta = null,
): self {
$this->resources[] = compact('handler', 'uri', 'name', 'description', 'mimeType', 'size', 'annotations', 'meta');
$this->resources[] = compact(
'handler',
'uri',
'name',
'description',
'mimeType',
'size',
'annotations',
'icons',
'meta',
);
return $this;
}
@ -383,10 +405,15 @@ final class Builder
* Manually registers a prompt handler.
*
* @param Handler $handler
* @param ?Icon[] $icons
*/
public function addPrompt(\Closure|array|string $handler, ?string $name = null, ?string $description = null): self
{
$this->prompts[] = compact('handler', 'name', 'description');
public function addPrompt(
\Closure|array|string $handler,
?string $name = null,
?string $description = null,
?array $icons = null,
): self {
$this->prompts[] = compact('handler', 'name', 'description', 'icons');
return $this;
}