Skip to content

Commit 5aa8874

Browse files
chr-hertelclaude
andcommitted
Add title field to Prompt for MCP spec compliance
The MCP 2025-11-25 specification defines an optional `title` property on Prompt for human-readable display in UI contexts, distinct from the programmatic `name` identifier. This was missing from the SDK. Closes #276 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 85fddf5 commit 5aa8874

File tree

5 files changed

+14
-2
lines changed

5 files changed

+14
-2
lines changed

src/Capability/Attribute/McpPrompt.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ class McpPrompt
2424
{
2525
/**
2626
* @param ?string $name overrides the prompt name (defaults to method name)
27+
* @param ?string $title Optional human-readable title for display in UI
2728
* @param ?string $description Optional description of the prompt. Defaults to method DocBlock summary.
2829
* @param ?Icon[] $icons Optional list of icon URLs representing the prompt
2930
* @param ?array<string, mixed> $meta Optional metadata
3031
*/
3132
public function __construct(
3233
public ?string $name = null,
34+
public ?string $title = null,
3335
public ?string $description = null,
3436
public ?array $icons = null,
3537
public ?array $meta = null,

src/Capability/Discovery/Discoverer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ private function processMethod(\ReflectionMethod $method, array &$discoveredCoun
271271
$paramTag = $paramTags['$'.$param->getName()] ?? null;
272272
$arguments[] = new PromptArgument($param->getName(), $paramTag ? trim((string) $paramTag->getDescription()) : null, !$param->isOptional() && !$param->isDefaultValueAvailable());
273273
}
274-
$prompt = new Prompt($name, $description, $arguments, $instance->icons, $instance->meta);
274+
$prompt = new Prompt($name, $instance->title, $description, $arguments, $instance->icons, $instance->meta);
275275
$completionProviders = $this->getCompletionProviders($method);
276276
$prompts[$name] = new PromptReference($prompt, [$className, $methodName], false, $completionProviders);
277277
++$discoveredCount['prompts'];

src/Capability/Registry/Loader/ArrayLoader.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ public function load(RegistryInterface $registry): void
252252
}
253253
$prompt = new Prompt(
254254
name: $name,
255+
title: $data['title'] ?? null,
255256
description: $description,
256257
arguments: $arguments,
257258
icons: $data['icons'] ?? null,

src/Schema/Prompt.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
*
2222
* @phpstan-type PromptData array{
2323
* name: string,
24+
* title?: string,
2425
* description?: string,
2526
* arguments?: PromptArgumentData[],
2627
* icons?: IconData[],
@@ -33,13 +34,15 @@ class Prompt implements \JsonSerializable
3334
{
3435
/**
3536
* @param string $name the name of the prompt or prompt template
37+
* @param ?string $title Optional human-readable title for display in UI
3638
* @param ?string $description an optional description of what this prompt provides
3739
* @param ?PromptArgument[] $arguments A list of arguments for templating. Null if not a template.
3840
* @param ?Icon[] $icons optional icons representing the prompt
3941
* @param ?array<string, mixed> $meta Optional metadata
4042
*/
4143
public function __construct(
4244
public readonly string $name,
45+
public readonly ?string $title = null,
4346
public readonly ?string $description = null,
4447
public readonly ?array $arguments = null,
4548
public readonly ?array $icons = null,
@@ -73,6 +76,7 @@ public static function fromArray(array $data): self
7376

7477
return new self(
7578
name: $data['name'],
79+
title: $data['title'] ?? null,
7680
description: $data['description'] ?? null,
7781
arguments: $arguments,
7882
icons: isset($data['icons']) && \is_array($data['icons']) ? array_map(Icon::fromArray(...), $data['icons']) : null,
@@ -83,6 +87,7 @@ public static function fromArray(array $data): self
8387
/**
8488
* @return array{
8589
* name: string,
90+
* title?: string,
8691
* description?: string,
8792
* arguments?: array<PromptArgument>,
8893
* icons?: Icon[],
@@ -92,6 +97,9 @@ public static function fromArray(array $data): self
9297
public function jsonSerialize(): array
9398
{
9499
$data = ['name' => $this->name];
100+
if (null !== $this->title) {
101+
$data['title'] = $this->title;
102+
}
95103
if (null !== $this->description) {
96104
$data['description'] = $this->description;
97105
}

src/Server/Builder.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,11 +472,12 @@ public function addResourceTemplate(
472472
public function addPrompt(
473473
\Closure|array|string $handler,
474474
?string $name = null,
475+
?string $title = null,
475476
?string $description = null,
476477
?array $icons = null,
477478
?array $meta = null,
478479
): self {
479-
$this->prompts[] = compact('handler', 'name', 'description', 'icons', 'meta');
480+
$this->prompts[] = compact('handler', 'name', 'title', 'description', 'icons', 'meta');
480481

481482
return $this;
482483
}

0 commit comments

Comments
 (0)