Skip to content

Commit 056c7b6

Browse files
chr-hertelCopilot
andauthored
[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>
1 parent 7a8fbe9 commit 056c7b6

File tree

12 files changed

+173
-39
lines changed

12 files changed

+173
-39
lines changed

docs/mcp-elements.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ class Calculator
7272
- **`name`** (optional): Tool identifier. Defaults to method name if not provided.
7373
- **`description`** (optional): Tool description. Defaults to docblock summary if not provided, otherwise uses method name.
7474
- **`annotations`** (optional): `ToolAnnotations` object for additional metadata.
75+
- **`icons`** (optional): Array of `Icon` objects for visual representation.
76+
- **`meta`** (optional): Arbitrary key-value pairs for custom metadata.
7577

7678
**Priority for name/description**: Attribute parameters → DocBlock content → Method name
7779

@@ -217,6 +219,9 @@ class ConfigProvider
217219
- **`description`** (optional): Resource description. Defaults to docblock summary if not provided.
218220
- **`mimeType`** (optional): MIME type of the resource content.
219221
- **`size`** (optional): Size in bytes if known.
222+
- **`annotations`** (optional): Additional metadata.
223+
- **`icons`** (optional): Array of `Icon` objects for visual representation.
224+
- **`meta`** (optional): Arbitrary key-value pairs for custom metadata.
220225

221226
**Standard Protocol URI Schemes**: `https://` (web resources), `file://` (filesystem), `git://` (version control).
222227
**Custom schemes**: `config://`, `data://`, `db://`, `api://` or any RFC 3986 compliant scheme.
@@ -399,6 +404,8 @@ class PromptGenerator
399404

400405
- **`name`** (optional): Prompt identifier. Defaults to method name if not provided.
401406
- **`description`** (optional): Prompt description. Defaults to docblock summary if not provided.
407+
- **`icons`** (optional): Array of `Icon` objects for visual representation.
408+
- **`meta`** (optional): Arbitrary key-value pairs for custom metadata.
402409

403410
### Prompt Return Values
404411

docs/server-builder.md

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,25 @@ final `Server` instance ready for use.
5252
Set the server's identity with name, version, and optional description:
5353

5454
```php
55+
use Mcp\Schema\Icon;
56+
use Mcp\Server;
57+
5558
$server = Server::builder()
56-
->setServerInfo('Calculator Server', '1.2.0', 'Advanced mathematical calculations');
59+
->setServerInfo(
60+
name: 'Calculator Server',
61+
version: '1.2.0',
62+
description: 'Advanced mathematical calculations',
63+
icons: [new Icon('https://example.com/icon.png', 'image/png', ['64x64'])],
64+
websiteUrl: 'https://example.com'
65+
');
5766
```
5867

5968
**Parameters:**
6069
- `$name` (string): The server name
6170
- `$version` (string): Version string (semantic versioning recommended)
6271
- `$description` (string|null): Optional description
72+
- `$icons` (Icon[]|null): Optional array of server icons
73+
- `$websiteUrl` (string|null): Optional server website URL
6374

6475
### Pagination Limit
6576

@@ -263,6 +274,16 @@ $server = Server::builder()
263274
);
264275
```
265276

277+
#### Parameters
278+
279+
- `handler` (callable|string): The tool handler
280+
- `name` (string|null): Optional tool name
281+
- `description` (string|null): Optional tool description
282+
- `annotations` (ToolAnnotations|null): Optional annotations for the tool
283+
- `inputSchema` (array|null): Optional input schema for the tool
284+
- `icons` (Icon[]|null): Optional array of icons for the tool
285+
- `meta` (array|null): Optional metadata for the tool
286+
266287
### Manual Resource Registration
267288

268289
Register static resources:
@@ -278,6 +299,18 @@ $server = Server::builder()
278299
);
279300
```
280301

302+
#### Parameters
303+
304+
- `handler` (callable|string): The resource handler
305+
- `uri` (string): The resource URI
306+
- `name` (string|null): Optional resource name
307+
- `description` (string|null): Optional resource description
308+
- `mimeType` (string|null): Optional MIME type of the resource
309+
- `size` (int|null): Optional size of the resource in bytes
310+
- `annotations` (Annotations|null): Optional annotations for the resource
311+
- `icons` (Icon[]|null): Optional array of icons for the resource
312+
- `meta` (array|null): Optional metadata for the resource
313+
281314
### Manual Resource Template Registration
282315

283316
Register dynamic resources with URI templates:
@@ -293,6 +326,15 @@ $server = Server::builder()
293326
);
294327
```
295328

329+
#### Parameters
330+
331+
- `handler` (callable|string): The resource template handler
332+
- `uriTemplate` (string): The resource URI template
333+
- `name` (string|null): Optional resource template name
334+
- `description` (string|null): Optional resource template description
335+
- `mimeType` (string|null): Optional MIME type of the resource
336+
- `annotations` (Annotations|null): Optional annotations for the resource template
337+
296338
### Manual Prompt Registration
297339

298340
Register prompt generators:
@@ -306,6 +348,13 @@ $server = Server::builder()
306348
);
307349
```
308350

351+
#### Parameters
352+
353+
- `handler` (callable|string): The prompt handler
354+
- `name` (string|null): Optional prompt name
355+
- `description` (string|null): Optional prompt description
356+
- `icons` (Icon[]|null): Optional array of icons for the prompt
357+
309358
**Note:** `name` and `description` are optional for all manual registrations. If not provided, they will be derived from
310359
the handler's method name and docblock.
311360

examples/discovery-calculator/McpElements.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Mcp\Capability\Attribute\McpResource;
1515
use Mcp\Capability\Attribute\McpTool;
1616
use Mcp\Exception\ToolCallException;
17+
use Mcp\Schema\Icon;
1718
use Psr\Log\LoggerInterface;
1819
use Psr\Log\NullLogger;
1920

@@ -47,7 +48,10 @@ public function __construct(
4748
*
4849
* @return float the result of the calculation
4950
*/
50-
#[McpTool(name: 'calculate')]
51+
#[McpTool(
52+
name: 'calculate',
53+
icons: [new Icon('https://www.svgrepo.com/show/530644/calculator.svg', 'image/svg+xml', ['any'])],
54+
)]
5155
public function calculate(float $a, float $b, string $operation): float
5256
{
5357
$this->logger->info(\sprintf('Calculating: %f %s %f', $a, $operation, $b));
@@ -92,6 +96,7 @@ public function calculate(float $a, float $b, string $operation): float
9296
name: 'calculator_config',
9397
description: 'Current settings for the calculator tool (precision, allow_negative).',
9498
mimeType: 'application/json',
99+
icons: [new Icon('https://www.svgrepo.com/show/529867/settings.svg', 'image/svg+xml', ['any'])],
95100
)]
96101
public function getConfiguration(): array
97102
{

src/Capability/Attribute/McpPrompt.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Mcp\Capability\Attribute;
1313

14+
use Mcp\Schema\Icon;
15+
1416
/**
1517
* Marks a PHP method as an MCP Prompt generator.
1618
* The method should return the prompt messages, potentially using arguments for templating.
@@ -23,11 +25,13 @@ final class McpPrompt
2325
/**
2426
* @param ?string $name overrides the prompt name (defaults to method name)
2527
* @param ?string $description Optional description of the prompt. Defaults to method DocBlock summary.
28+
* @param ?Icon[] $icons Optional list of icon URLs representing the prompt
2629
* @param ?array<string, mixed> $meta Optional metadata
2730
*/
2831
public function __construct(
2932
public ?string $name = null,
3033
public ?string $description = null,
34+
public ?array $icons = null,
3135
public ?array $meta = null,
3236
) {
3337
}

src/Capability/Attribute/McpResource.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Mcp\Capability\Attribute;
1313

1414
use Mcp\Schema\Annotations;
15+
use Mcp\Schema\Icon;
1516

1617
/**
1718
* Marks a PHP class as representing or handling a specific MCP Resource instance.
@@ -29,6 +30,7 @@ final class McpResource
2930
* @param ?string $mimeType the MIME type, if known and constant for this resource
3031
* @param ?int $size the size in bytes, if known and constant
3132
* @param Annotations|null $annotations optional annotations describing the resource
33+
* @param ?Icon[] $icons Optional list of icon URLs representing the resource
3234
* @param ?array<string, mixed> $meta Optional metadata
3335
*/
3436
public function __construct(
@@ -38,6 +40,7 @@ public function __construct(
3840
public ?string $mimeType = null,
3941
public ?int $size = null,
4042
public ?Annotations $annotations = null,
43+
public ?array $icons = null,
4144
public ?array $meta = null,
4245
) {
4346
}

src/Capability/Attribute/McpTool.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Mcp\Capability\Attribute;
1313

14+
use Mcp\Schema\Icon;
1415
use Mcp\Schema\ToolAnnotations;
1516

1617
/**
@@ -23,12 +24,14 @@ class McpTool
2324
* @param string|null $name The name of the tool (defaults to the method name)
2425
* @param string|null $description The description of the tool (defaults to the DocBlock/inferred)
2526
* @param ToolAnnotations|null $annotations Optional annotations describing tool behavior
27+
* @param ?Icon[] $icons Optional list of icon URLs representing the tool
2628
* @param ?array<string, mixed> $meta Optional metadata
2729
*/
2830
public function __construct(
2931
public ?string $name = null,
3032
public ?string $description = null,
3133
public ?ToolAnnotations $annotations = null,
34+
public ?array $icons = null,
3235
public ?array $meta = null,
3336
) {
3437
}

src/Capability/Discovery/Discoverer.php

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,14 @@ 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-
$meta = $instance->meta ?? null;
226-
$tool = new Tool($name, $inputSchema, $description, $instance->annotations, meta: $meta);
225+
$tool = new Tool(
226+
$name,
227+
$inputSchema,
228+
$description,
229+
$instance->annotations,
230+
$instance->icons,
231+
$instance->meta,
232+
);
227233
$tools[$name] = new ToolReference($tool, [$className, $methodName], false);
228234
++$discoveredCount['tools'];
229235
break;
@@ -232,11 +238,16 @@ private function processMethod(\ReflectionMethod $method, array &$discoveredCoun
232238
$docBlock = $this->docBlockParser->parseDocBlock($method->getDocComment() ?? null);
233239
$name = $instance->name ?? ('__invoke' === $methodName ? $classShortName : $methodName);
234240
$description = $instance->description ?? $this->docBlockParser->getSummary($docBlock) ?? null;
235-
$mimeType = $instance->mimeType;
236-
$size = $instance->size;
237-
$annotations = $instance->annotations;
238-
$meta = $instance->meta;
239-
$resource = new Resource($instance->uri, $name, $description, $mimeType, $annotations, $size, $meta);
241+
$resource = new Resource(
242+
$instance->uri,
243+
$name,
244+
$description,
245+
$instance->mimeType,
246+
$instance->annotations,
247+
$instance->size,
248+
$instance->icons,
249+
$instance->meta,
250+
);
240251
$resources[$instance->uri] = new ResourceReference($resource, [$className, $methodName], false);
241252

242253
++$discoveredCount['resources'];
@@ -256,8 +267,7 @@ private function processMethod(\ReflectionMethod $method, array &$discoveredCoun
256267
$paramTag = $paramTags['$'.$param->getName()] ?? null;
257268
$arguments[] = new PromptArgument($param->getName(), $paramTag ? trim((string) $paramTag->getDescription()) : null, !$param->isOptional() && !$param->isDefaultValueAvailable());
258269
}
259-
$meta = $instance->meta ?? null;
260-
$prompt = new Prompt($name, $description, $arguments, $meta);
270+
$prompt = new Prompt($name, $description, $arguments, $instance->icons, $instance->meta);
261271
$completionProviders = $this->getCompletionProviders($method);
262272
$prompts[$name] = new PromptReference($prompt, [$className, $methodName], false, $completionProviders);
263273
++$discoveredCount['prompts'];

src/Capability/Registry/Loader/ArrayLoader.php

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Mcp\Capability\Registry\ReferenceRegistryInterface;
2323
use Mcp\Exception\ConfigurationException;
2424
use Mcp\Schema\Annotations;
25+
use Mcp\Schema\Icon;
2526
use Mcp\Schema\Prompt;
2627
use Mcp\Schema\PromptArgument;
2728
use Mcp\Schema\Resource;
@@ -45,7 +46,7 @@ final class ArrayLoader implements LoaderInterface
4546
* name: ?string,
4647
* description: ?string,
4748
* annotations: ?ToolAnnotations,
48-
* icons: ?array<int, \Mcp\Schema\Icon>,
49+
* icons: ?Icon[],
4950
* meta: ?array<string, mixed>
5051
* }[] $tools
5152
* @param array{
@@ -56,6 +57,7 @@ final class ArrayLoader implements LoaderInterface
5657
* mimeType: ?string,
5758
* size: int|null,
5859
* annotations: ?Annotations,
60+
* icons: ?Icon[],
5961
* meta: ?array<string, mixed>
6062
* }[] $resources
6163
* @param array{
@@ -71,14 +73,15 @@ final class ArrayLoader implements LoaderInterface
7173
* handler: Handler,
7274
* name: ?string,
7375
* description: ?string,
76+
* icons: ?Icon[],
7477
* meta: ?array<string, mixed>
7578
* }[] $prompts
7679
*/
7780
public function __construct(
78-
private array $tools = [],
79-
private array $resources = [],
80-
private array $resourceTemplates = [],
81-
private array $prompts = [],
81+
private readonly array $tools = [],
82+
private readonly array $resources = [],
83+
private readonly array $resourceTemplates = [],
84+
private readonly array $prompts = [],
8285
private LoggerInterface $logger = new NullLogger(),
8386
) {
8487
}
@@ -145,13 +148,16 @@ public function load(ReferenceRegistryInterface $registry): void
145148
$description = $data['description'] ?? $docBlockParser->getSummary($docBlock) ?? null;
146149
}
147150

148-
$uri = $data['uri'];
149-
$mimeType = $data['mimeType'];
150-
$size = $data['size'];
151-
$annotations = $data['annotations'];
152-
$meta = $data['meta'];
153-
154-
$resource = new Resource($uri, $name, $description, $mimeType, $annotations, $size, $meta);
151+
$resource = new Resource(
152+
$data['uri'],
153+
$name,
154+
$description,
155+
$data['mimeType'],
156+
$data['annotations'],
157+
$data['size'],
158+
$data['icons'],
159+
$data['meta'],
160+
);
155161
$registry->registerResource($resource, $data['handler'], true);
156162

157163
$handlerDesc = $this->getHandlerDescription($data['handler']);
@@ -182,12 +188,14 @@ public function load(ReferenceRegistryInterface $registry): void
182188
$description = $data['description'] ?? $docBlockParser->getSummary($docBlock) ?? null;
183189
}
184190

185-
$uriTemplate = $data['uriTemplate'];
186-
$mimeType = $data['mimeType'];
187-
$annotations = $data['annotations'];
188-
$meta = $data['meta'];
189-
190-
$template = new ResourceTemplate($uriTemplate, $name, $description, $mimeType, $annotations, $meta);
191+
$template = new ResourceTemplate(
192+
$data['uriTemplate'],
193+
$name,
194+
$description,
195+
$data['mimeType'],
196+
$data['annotations'],
197+
$data['meta'],
198+
);
191199
$completionProviders = $this->getCompletionProviders($reflection);
192200
$registry->registerResourceTemplate($template, $data['handler'], $completionProviders, true);
193201

@@ -238,8 +246,7 @@ public function load(ReferenceRegistryInterface $registry): void
238246
!$param->isOptional() && !$param->isDefaultValueAvailable(),
239247
);
240248
}
241-
$meta = $data['meta'];
242-
$prompt = new Prompt($name, $description, $arguments, $meta);
249+
$prompt = new Prompt($name, $description, $arguments, $data['icons'], $data['meta']);
243250
$completionProviders = $this->getCompletionProviders($reflection);
244251
$registry->registerPrompt($prompt, $data['handler'], $completionProviders, true);
245252

0 commit comments

Comments
 (0)