Add interface abstractions for SchemaGenerator and Discoverer with DI improvements (#215)

* schema generator injection

* add interface for schema generator

* improve discoverer dependency

* remove some comments

* make some classes final

* simplify condition

* code review

* Extend changelog for covering interfaces

* Adopt SchemaGeneratorInterface after rebase and #153

---------

Co-authored-by: Christopher Hertel <mail@christopher-hertel.de>
This commit is contained in:
Antoine Bluchet 2026-01-11 22:03:43 +01:00 • committed by GitHub
commit 7cb29125bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -13,6 +13,7 @@ namespace Mcp\Capability\Discovery;
use Mcp\Capability\Attribute\McpTool;
use Mcp\Capability\Attribute\Schema;
use Mcp\Exception\BadMethodCallException;
use Mcp\Exception\InvalidArgumentException;
use Mcp\Server\RequestContext;
use phpDocumentor\Reflection\DocBlock\Tags\Param;
@ -57,7 +58,7 @@ use phpDocumentor\Reflection\DocBlock\Tags\Param;
*
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
*/
class SchemaGenerator
final class SchemaGenerator implements SchemaGeneratorInterface
{
public function __construct(
private readonly DocBlockParser $docBlockParser,
@ -65,12 +66,20 @@ class SchemaGenerator
}
/**
* Generates a JSON Schema object (as a PHP array) for a method's or function's parameters.
* Generates a JSON Schema object (as a PHP array) for parameters.
*
* @return array<string, mixed>
*/
public function generate(\ReflectionMethod|\ReflectionFunction $reflection): array
public function generate(\Reflector $reflection): array
{
if ($reflection instanceof \ReflectionClass) {
throw new BadMethodCallException('Schema generation from ReflectionClass is not implemented yet. Use ReflectionMethod or ReflectionFunction instead.');
}
if (!$reflection instanceof \ReflectionMethod && !$reflection instanceof \ReflectionFunction) {
throw new BadMethodCallException(\sprintf('Schema generation from %s is not supported. Use ReflectionMethod or ReflectionFunction instead.', $reflection::class));
}
$methodSchema = $this->extractMethodLevelSchema($reflection);
if ($methodSchema && isset($methodSchema['definition'])) {
@ -88,10 +97,18 @@ class SchemaGenerator
* Only returns an outputSchema if explicitly provided in the McpTool attribute.
* Per MCP spec, outputSchema should only be present when explicitly provided.
*
* @return array<string, mixed>|null
* @return ?array<string, mixed>
*/
public function generateOutputSchema(\ReflectionMethod|\ReflectionFunction $reflection): ?array
public function generateOutputSchema(\Reflector $reflection): ?array
{
if ($reflection instanceof \ReflectionClass) {
throw new BadMethodCallException('Schema generation from ReflectionClass is not implemented yet. Use ReflectionMethod or ReflectionFunction instead.');
}
if (!$reflection instanceof \ReflectionMethod && !$reflection instanceof \ReflectionFunction) {
throw new BadMethodCallException(\sprintf('Schema generation from %s is not supported. Use ReflectionMethod or ReflectionFunction instead.', $reflection::class));
}
// Only return outputSchema if explicitly provided in McpTool attribute
$mcpToolAttrs = $reflection->getAttributes(McpTool::class, \ReflectionAttribute::IS_INSTANCEOF);
if ($mcpToolAttrs) {
@ -108,7 +125,7 @@ class SchemaGenerator
*
* @return SchemaAttributeData
*/
private function extractMethodLevelSchema(\ReflectionMethod|\ReflectionFunction $reflection): ?array
private function extractMethodLevelSchema(\ReflectionFunctionAbstract $reflection): ?array
{
$schemaAttrs = $reflection->getAttributes(Schema::class, \ReflectionAttribute::IS_INSTANCEOF);
if (empty($schemaAttrs)) {