diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e82b677..febbef45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ All notable changes to `mcp/sdk` will be documented in this file. * [BC Break] `RegistryInterface::registerTool()`, `registerResource()`, `registerResourceTemplate()`, `registerPrompt()` lost their trailing `bool $isManual = false` parameter. Callers using positional arguments must drop the flag. * [BC Break] Removed `RegistryInterface::clear()`, `getDiscoveryState()`, `setDiscoveryState()`. Rediscovery now goes through `DiscoveryLoader::load()` directly. * `Registry::register*()` semantics changed to plain last-write-wins (overwrites silently) and the methods now return the stored `*Reference`. The previous "discovered registration is ignored when a manual one already exists" precedence rule still applies, but is now enforced by `DiscoveryLoader` via reference-identity tracking — and still emits a debug log when a discovery is skipped due to a conflicting registration. +* Add optional `title` parameter to `Builder::addResource()` and `Builder::addResourceTemplate()` for MCP spec compliance +* [BC Break] `Builder::addResource()` signature changed — `$title` parameter added between `$name` and `$description`. Callers using positional arguments must switch to named arguments. +* [BC Break] `Builder::addResourceTemplate()` signature changed — `$title` parameter added between `$name` and `$description`. Callers using positional arguments must switch to named arguments. 0.5.0 ----- diff --git a/src/Capability/Registry/Loader/ArrayLoader.php b/src/Capability/Registry/Loader/ArrayLoader.php index bce3adce..8865b0ff 100644 --- a/src/Capability/Registry/Loader/ArrayLoader.php +++ b/src/Capability/Registry/Loader/ArrayLoader.php @@ -56,6 +56,7 @@ final class ArrayLoader implements LoaderInterface * handler: Handler, * uri: string, * name: ?string, + * title: ?string, * description: ?string, * mimeType: ?string, * size: int|null, @@ -67,6 +68,7 @@ final class ArrayLoader implements LoaderInterface * handler: Handler, * uriTemplate: string, * name: ?string, + * title: ?string, * description: ?string, * mimeType: ?string, * annotations: ?Annotations, @@ -157,6 +159,7 @@ public function load(RegistryInterface $registry): void $resource = new Resource( uri: $data['uri'], name: $name, + title: $data['title'] ?? null, description: $description, mimeType: $data['mimeType'] ?? null, annotations: $data['annotations'] ?? null, @@ -197,6 +200,7 @@ public function load(RegistryInterface $registry): void $template = new ResourceTemplate( uriTemplate: $data['uriTemplate'], name: $name, + title: $data['title'] ?? null, description: $description, mimeType: $data['mimeType'] ?? null, annotations: $data['annotations'] ?? null, diff --git a/src/Server/Builder.php b/src/Server/Builder.php index 532f4fed..ba26d23f 100644 --- a/src/Server/Builder.php +++ b/src/Server/Builder.php @@ -119,6 +119,7 @@ final class Builder * handler: Handler, * uri: string, * name: ?string, + * title: ?string, * description: ?string, * mimeType: ?string, * size: int|null, @@ -134,6 +135,7 @@ final class Builder * handler: Handler, * uriTemplate: string, * name: ?string, + * title: ?string, * description: ?string, * mimeType: ?string, * annotations: ?Annotations, @@ -433,6 +435,7 @@ public function addTool( * Manually registers a resource handler. * * @param Handler $handler + * @param ?string $title Optional human-readable title for display in UI * @param ?Icon[] $icons * @param array|null $meta */ @@ -440,6 +443,7 @@ public function addResource( \Closure|array|string $handler, string $uri, ?string $name = null, + ?string $title = null, ?string $description = null, ?string $mimeType = null, ?int $size = null, @@ -451,6 +455,7 @@ public function addResource( 'handler', 'uri', 'name', + 'title', 'description', 'mimeType', 'size', @@ -466,12 +471,14 @@ public function addResource( * Manually registers a resource template handler. * * @param Handler $handler + * @param ?string $title Optional human-readable title for display in UI * @param array|null $meta */ public function addResourceTemplate( \Closure|array|string $handler, string $uriTemplate, ?string $name = null, + ?string $title = null, ?string $description = null, ?string $mimeType = null, ?Annotations $annotations = null, @@ -481,6 +488,7 @@ public function addResourceTemplate( 'handler', 'uriTemplate', 'name', + 'title', 'description', 'mimeType', 'annotations', diff --git a/tests/Unit/Capability/Registry/Loader/ArrayLoaderResourceTitleTest.php b/tests/Unit/Capability/Registry/Loader/ArrayLoaderResourceTitleTest.php new file mode 100644 index 00000000..bd01207a --- /dev/null +++ b/tests/Unit/Capability/Registry/Loader/ArrayLoaderResourceTitleTest.php @@ -0,0 +1,69 @@ + static fn (): string => 'ok', + 'uri' => 'config://app/settings', + 'name' => 'app_settings', + 'title' => 'Application Settings', + 'description' => null, + 'mimeType' => null, + 'size' => null, + 'annotations' => null, + 'icons' => null, + 'meta' => null, + ], + ]; + + $loader = new ArrayLoader([], $resources); + $registry = new Registry(); + + $loader->load($registry); + + $resourceRef = $registry->getResource('config://app/settings'); + $this->assertSame('Application Settings', $resourceRef->resource->title); + } + + public function testLoadPropagatesResourceTemplateTitleToRegisteredTemplate(): void + { + $resourceTemplates = [ + [ + 'handler' => static fn (): string => 'ok', + 'uriTemplate' => 'user://{userId}/profile', + 'name' => 'user_profile', + 'title' => 'User Profile', + 'description' => null, + 'mimeType' => null, + 'annotations' => null, + 'meta' => null, + ], + ]; + + $loader = new ArrayLoader([], [], $resourceTemplates); + $registry = new Registry(); + + $loader->load($registry); + + $templateRef = $registry->getResourceTemplate('user://{userId}/profile'); + $this->assertSame('User Profile', $templateRef->resourceTemplate->title); + } +}