[Capability] Extract shared element name/description resolution (#481)

This commit is contained in:
Christopher Hertel 2026-08-25 00:46:21 +02:00 • committed by GitHub
commit ff0b5fadc8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 61 additions and 36 deletions

View file

@ -217,7 +217,6 @@ final class Discoverer implements DiscovererInterface
private function processMethod(\ReflectionMethod $method, array &$discoveredCount, \ReflectionAttribute $attribute, array &$tools, array &$resources, array &$prompts, array &$resourceTemplates): void
{
$className = $method->getDeclaringClass()->getName();
$classShortName = $method->getDeclaringClass()->getShortName();
$methodName = $method->getName();
$attributeClassName = $attribute->getName();
@ -226,9 +225,8 @@ final class Discoverer implements DiscovererInterface
switch ($attributeClassName) {
case McpTool::class:
$docBlock = $this->docBlockParser->parseDocBlock($method->getDocComment() ?? null);
$name = $instance->name ?? ('__invoke' === $methodName ? $classShortName : $methodName);
$description = $instance->description ?? $this->docBlockParser->getDescription($docBlock) ?? null;
$name = ElementMetadataResolver::resolveName($method, $instance->name);
$description = ElementMetadataResolver::resolveDescription($method, $instance->description, $this->docBlockParser);
$inputSchema = $this->schemaGenerator->generate($method);
$outputSchema = $this->schemaGenerator->generateOutputSchema($method);
$tool = new Tool(
@ -246,9 +244,8 @@ final class Discoverer implements DiscovererInterface
break;
case McpResource::class:
$docBlock = $this->docBlockParser->parseDocBlock($method->getDocComment() ?? null);
$name = $instance->name ?? ('__invoke' === $methodName ? $classShortName : $methodName);
$description = $instance->description ?? $this->docBlockParser->getDescription($docBlock) ?? null;
$name = ElementMetadataResolver::resolveName($method, $instance->name);
$description = ElementMetadataResolver::resolveDescription($method, $instance->description, $this->docBlockParser);
$resource = new ResourceDefinition(
$instance->uri,
$name,
@ -267,8 +264,8 @@ final class Discoverer implements DiscovererInterface
case McpPrompt::class:
$docBlock = $this->docBlockParser->parseDocBlock($method->getDocComment() ?? null);
$name = $instance->name ?? ('__invoke' === $methodName ? $classShortName : $methodName);
$description = $instance->description ?? $this->docBlockParser->getDescription($docBlock) ?? null;
$name = ElementMetadataResolver::resolveName($method, $instance->name);
$description = ElementMetadataResolver::resolveDescription($method, $instance->description, $this->docBlockParser);
$arguments = [];
$paramTags = $this->docBlockParser->getParamTags($docBlock);
foreach ($method->getParameters() as $param) {
@ -286,9 +283,8 @@ final class Discoverer implements DiscovererInterface
break;
case McpResourceTemplate::class:
$docBlock = $this->docBlockParser->parseDocBlock($method->getDocComment() ?? null);
$name = $instance->name ?? ('__invoke' === $methodName ? $classShortName : $methodName);
$description = $instance->description ?? $this->docBlockParser->getDescription($docBlock) ?? null;
$name = ElementMetadataResolver::resolveName($method, $instance->name);
$description = ElementMetadataResolver::resolveDescription($method, $instance->description, $this->docBlockParser);
$mimeType = $instance->mimeType;
$annotations = $instance->annotations;
$meta = $instance->meta ?? null;

View file

@ -0,0 +1,44 @@
<?php
/*
* This file is part of the official PHP MCP SDK.
*
* A collaboration between Symfony and the PHP Foundation.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Mcp\Capability\Discovery;
/**
* Derives an element's name and description from its handler method, used by
* both attribute discovery and reflection-based manual registration.
*
* @author Christopher Hertel <mail@christopher-hertel.de>
*/
final class ElementMetadataResolver
{
private function __construct()
{
}
/**
* Explicit names win; otherwise invokable classes are named after the class,
* regular handlers after the method.
*/
public static function resolveName(\ReflectionMethod $method, ?string $name): string
{
$methodName = $method->getName();
return $name ?? ('__invoke' === $methodName ? $method->getDeclaringClass()->getShortName() : $methodName);
}
/**
* Explicit descriptions win; otherwise the summary of the method's doc block is used.
*/
public static function resolveDescription(\ReflectionMethod $method, ?string $description, DocBlockParser $docBlockParser): ?string
{
return $description ?? $docBlockParser->getDescription($docBlockParser->parseDocBlock($method->getDocComment() ?? null));
}
}

View file

@ -16,6 +16,7 @@ use Mcp\Capability\Completion\EnumCompletionProvider;
use Mcp\Capability\Completion\ListCompletionProvider;
use Mcp\Capability\Completion\ProviderInterface;
use Mcp\Capability\Discovery\DocBlockParser;
use Mcp\Capability\Discovery\ElementMetadataResolver;
use Mcp\Capability\Discovery\HandlerResolver;
use Mcp\Capability\Discovery\SchemaGenerator;
use Mcp\Capability\Discovery\SchemaGeneratorInterface;
@ -106,12 +107,8 @@ final class ReflectedElementLoader implements LoaderInterface
$name = $data['name'] ?? 'closure_tool_'.spl_object_id($data['handler']);
$description = $data['description'] ?? null;
} else {
$classShortName = $reflection->getDeclaringClass()->getShortName();
$methodName = $reflection->getName();
$docBlock = $docBlockParser->parseDocBlock($reflection->getDocComment() ?? null);
$name = $data['name'] ?? ('__invoke' === $methodName ? $classShortName : $methodName);
$description = $data['description'] ?? $docBlockParser->getDescription($docBlock) ?? null;
$name = ElementMetadataResolver::resolveName($reflection, $data['name'] ?? null);
$description = ElementMetadataResolver::resolveDescription($reflection, $data['description'] ?? null, $docBlockParser);
}
$inputSchema = $data['inputSchema'] ?? $schemaGenerator->generate($reflection);
@ -148,12 +145,8 @@ final class ReflectedElementLoader implements LoaderInterface
$name = $data['name'] ?? 'closure_resource_'.spl_object_id($data['handler']);
$description = $data['description'] ?? null;
} else {
$classShortName = $reflection->getDeclaringClass()->getShortName();
$methodName = $reflection->getName();
$docBlock = $docBlockParser->parseDocBlock($reflection->getDocComment() ?? null);
$name = $data['name'] ?? ('__invoke' === $methodName ? $classShortName : $methodName);
$description = $data['description'] ?? $docBlockParser->getDescription($docBlock) ?? null;
$name = ElementMetadataResolver::resolveName($reflection, $data['name'] ?? null);
$description = ElementMetadataResolver::resolveDescription($reflection, $data['description'] ?? null, $docBlockParser);
}
$resource = new ResourceDefinition(
@ -189,12 +182,8 @@ final class ReflectedElementLoader implements LoaderInterface
$name = $data['name'] ?? 'closure_template_'.spl_object_id($data['handler']);
$description = $data['description'] ?? null;
} else {
$classShortName = $reflection->getDeclaringClass()->getShortName();
$methodName = $reflection->getName();
$docBlock = $docBlockParser->parseDocBlock($reflection->getDocComment() ?? null);
$name = $data['name'] ?? ('__invoke' === $methodName ? $classShortName : $methodName);
$description = $data['description'] ?? $docBlockParser->getDescription($docBlock) ?? null;
$name = ElementMetadataResolver::resolveName($reflection, $data['name'] ?? null);
$description = ElementMetadataResolver::resolveDescription($reflection, $data['description'] ?? null, $docBlockParser);
}
$template = new ResourceTemplate(
@ -229,12 +218,8 @@ final class ReflectedElementLoader implements LoaderInterface
$name = $data['name'] ?? 'closure_prompt_'.spl_object_id($data['handler']);
$description = $data['description'] ?? null;
} else {
$classShortName = $reflection->getDeclaringClass()->getShortName();
$methodName = $reflection->getName();
$docBlock = $docBlockParser->parseDocBlock($reflection->getDocComment() ?? null);
$name = $data['name'] ?? ('__invoke' === $methodName ? $classShortName : $methodName);
$description = $data['description'] ?? $docBlockParser->getDescription($docBlock) ?? null;
$name = ElementMetadataResolver::resolveName($reflection, $data['name'] ?? null);
$description = ElementMetadataResolver::resolveDescription($reflection, $data['description'] ?? null, $docBlockParser);
}
$arguments = [];