Rework registry to combine provider and registry interface, move capabilities out and enable Registry injection in Builder (#150)
This commit is contained in:
parent
7a58ab3f5a
commit
7cd281d0c7
22 changed files with 567 additions and 736 deletions
|
|
@ -13,8 +13,6 @@ namespace Mcp\Capability;
|
||||||
|
|
||||||
use Mcp\Capability\Discovery\DiscoveryState;
|
use Mcp\Capability\Discovery\DiscoveryState;
|
||||||
use Mcp\Capability\Registry\PromptReference;
|
use Mcp\Capability\Registry\PromptReference;
|
||||||
use Mcp\Capability\Registry\ReferenceProviderInterface;
|
|
||||||
use Mcp\Capability\Registry\ReferenceRegistryInterface;
|
|
||||||
use Mcp\Capability\Registry\ResourceReference;
|
use Mcp\Capability\Registry\ResourceReference;
|
||||||
use Mcp\Capability\Registry\ResourceTemplateReference;
|
use Mcp\Capability\Registry\ResourceTemplateReference;
|
||||||
use Mcp\Capability\Registry\ToolReference;
|
use Mcp\Capability\Registry\ToolReference;
|
||||||
|
|
@ -31,7 +29,6 @@ use Mcp\Schema\Page;
|
||||||
use Mcp\Schema\Prompt;
|
use Mcp\Schema\Prompt;
|
||||||
use Mcp\Schema\Resource;
|
use Mcp\Schema\Resource;
|
||||||
use Mcp\Schema\ResourceTemplate;
|
use Mcp\Schema\ResourceTemplate;
|
||||||
use Mcp\Schema\ServerCapabilities;
|
|
||||||
use Mcp\Schema\Tool;
|
use Mcp\Schema\Tool;
|
||||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
@ -39,13 +36,10 @@ use Psr\Log\NullLogger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registry implementation that manages MCP element registration and access.
|
* Registry implementation that manages MCP element registration and access.
|
||||||
* Implements both ReferenceProvider (for access) and ReferenceRegistry (for registration)
|
|
||||||
* following the Interface Segregation Principle.
|
|
||||||
*
|
*
|
||||||
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
|
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
|
||||||
* @author Pavel Buchnev <butschster@gmail.com>
|
|
||||||
*/
|
*/
|
||||||
final class Registry implements ReferenceProviderInterface, ReferenceRegistryInterface
|
final class Registry implements RegistryInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var array<string, ToolReference>
|
* @var array<string, ToolReference>
|
||||||
|
|
@ -67,8 +61,6 @@ final class Registry implements ReferenceProviderInterface, ReferenceRegistryInt
|
||||||
*/
|
*/
|
||||||
private array $resourceTemplates = [];
|
private array $resourceTemplates = [];
|
||||||
|
|
||||||
private ServerCapabilities $serverCapabilities;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ?EventDispatcherInterface $eventDispatcher = null,
|
private readonly ?EventDispatcherInterface $eventDispatcher = null,
|
||||||
private readonly LoggerInterface $logger = new NullLogger(),
|
private readonly LoggerInterface $logger = new NullLogger(),
|
||||||
|
|
@ -76,25 +68,6 @@ final class Registry implements ReferenceProviderInterface, ReferenceRegistryInt
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCapabilities(): ServerCapabilities
|
|
||||||
{
|
|
||||||
if (!$this->hasElements()) {
|
|
||||||
$this->logger->info('No capabilities registered on server.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->serverCapabilities ?? new ServerCapabilities(
|
|
||||||
tools: [] !== $this->tools,
|
|
||||||
toolsListChanged: $this->eventDispatcher instanceof EventDispatcherInterface,
|
|
||||||
resources: [] !== $this->resources || [] !== $this->resourceTemplates,
|
|
||||||
resourcesSubscribe: false,
|
|
||||||
resourcesListChanged: $this->eventDispatcher instanceof EventDispatcherInterface,
|
|
||||||
prompts: [] !== $this->prompts,
|
|
||||||
promptsListChanged: $this->eventDispatcher instanceof EventDispatcherInterface,
|
|
||||||
logging: false,
|
|
||||||
completions: true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function registerTool(Tool $tool, callable|array|string $handler, bool $isManual = false): void
|
public function registerTool(Tool $tool, callable|array|string $handler, bool $isManual = false): void
|
||||||
{
|
{
|
||||||
$toolName = $tool->name;
|
$toolName = $tool->name;
|
||||||
|
|
@ -220,41 +193,9 @@ final class Registry implements ReferenceProviderInterface, ReferenceRegistryInt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTool(string $name): ToolReference
|
public function hasTools(): bool
|
||||||
{
|
{
|
||||||
return $this->tools[$name] ?? throw new ToolNotFoundException($name);
|
return [] !== $this->tools;
|
||||||
}
|
|
||||||
|
|
||||||
public function getResource(
|
|
||||||
string $uri,
|
|
||||||
bool $includeTemplates = true,
|
|
||||||
): ResourceReference|ResourceTemplateReference {
|
|
||||||
$registration = $this->resources[$uri] ?? null;
|
|
||||||
if ($registration) {
|
|
||||||
return $registration;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($includeTemplates) {
|
|
||||||
foreach ($this->resourceTemplates as $template) {
|
|
||||||
if ($template->matches($uri)) {
|
|
||||||
return $template;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->logger->debug('No resource matched URI.', ['uri' => $uri]);
|
|
||||||
|
|
||||||
throw new ResourceNotFoundException($uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getResourceTemplate(string $uriTemplate): ResourceTemplateReference
|
|
||||||
{
|
|
||||||
return $this->resourceTemplates[$uriTemplate] ?? throw new ResourceNotFoundException($uriTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPrompt(string $name): PromptReference
|
|
||||||
{
|
|
||||||
return $this->prompts[$name] ?? throw new PromptNotFoundException($name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTools(?int $limit = null, ?string $cursor = null): Page
|
public function getTools(?int $limit = null, ?string $cursor = null): Page
|
||||||
|
|
@ -279,6 +220,16 @@ final class Registry implements ReferenceProviderInterface, ReferenceRegistryInt
|
||||||
return new Page($paginatedTools, $nextCursor);
|
return new Page($paginatedTools, $nextCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getTool(string $name): ToolReference
|
||||||
|
{
|
||||||
|
return $this->tools[$name] ?? throw new ToolNotFoundException($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasResources(): bool
|
||||||
|
{
|
||||||
|
return [] !== $this->resources;
|
||||||
|
}
|
||||||
|
|
||||||
public function getResources(?int $limit = null, ?string $cursor = null): Page
|
public function getResources(?int $limit = null, ?string $cursor = null): Page
|
||||||
{
|
{
|
||||||
$resources = [];
|
$resources = [];
|
||||||
|
|
@ -301,26 +252,31 @@ final class Registry implements ReferenceProviderInterface, ReferenceRegistryInt
|
||||||
return new Page($paginatedResources, $nextCursor);
|
return new Page($paginatedResources, $nextCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPrompts(?int $limit = null, ?string $cursor = null): Page
|
public function getResource(
|
||||||
|
string $uri,
|
||||||
|
bool $includeTemplates = true,
|
||||||
|
): ResourceReference|ResourceTemplateReference {
|
||||||
|
$registration = $this->resources[$uri] ?? null;
|
||||||
|
if ($registration) {
|
||||||
|
return $registration;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($includeTemplates) {
|
||||||
|
foreach ($this->resourceTemplates as $template) {
|
||||||
|
if ($template->matches($uri)) {
|
||||||
|
return $template;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logger->debug('No resource matched URI.', ['uri' => $uri]);
|
||||||
|
|
||||||
|
throw new ResourceNotFoundException($uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasResourceTemplates(): bool
|
||||||
{
|
{
|
||||||
$prompts = [];
|
return [] !== $this->resourceTemplates;
|
||||||
foreach ($this->prompts as $promptReference) {
|
|
||||||
$prompts[$promptReference->prompt->name] = $promptReference->prompt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null === $limit) {
|
|
||||||
return new Page($prompts, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
$paginatedPrompts = $this->paginateResults($prompts, $limit, $cursor);
|
|
||||||
|
|
||||||
$nextCursor = $this->calculateNextCursor(
|
|
||||||
\count($prompts),
|
|
||||||
$cursor,
|
|
||||||
$limit
|
|
||||||
);
|
|
||||||
|
|
||||||
return new Page($paginatedPrompts, $nextCursor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getResourceTemplates(?int $limit = null, ?string $cursor = null): Page
|
public function getResourceTemplates(?int $limit = null, ?string $cursor = null): Page
|
||||||
|
|
@ -345,12 +301,41 @@ final class Registry implements ReferenceProviderInterface, ReferenceRegistryInt
|
||||||
return new Page($paginatedTemplates, $nextCursor);
|
return new Page($paginatedTemplates, $nextCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasElements(): bool
|
public function getResourceTemplate(string $uriTemplate): ResourceTemplateReference
|
||||||
{
|
{
|
||||||
return !empty($this->tools)
|
return $this->resourceTemplates[$uriTemplate] ?? throw new ResourceNotFoundException($uriTemplate);
|
||||||
|| !empty($this->resources)
|
}
|
||||||
|| !empty($this->prompts)
|
|
||||||
|| !empty($this->resourceTemplates);
|
public function hasPrompts(): bool
|
||||||
|
{
|
||||||
|
return [] !== $this->prompts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrompts(?int $limit = null, ?string $cursor = null): Page
|
||||||
|
{
|
||||||
|
$prompts = [];
|
||||||
|
foreach ($this->prompts as $promptReference) {
|
||||||
|
$prompts[$promptReference->prompt->name] = $promptReference->prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $limit) {
|
||||||
|
return new Page($prompts, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
$paginatedPrompts = $this->paginateResults($prompts, $limit, $cursor);
|
||||||
|
|
||||||
|
$nextCursor = $this->calculateNextCursor(
|
||||||
|
\count($prompts),
|
||||||
|
$cursor,
|
||||||
|
$limit
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Page($paginatedPrompts, $nextCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrompt(string $name): PromptReference
|
||||||
|
{
|
||||||
|
return $this->prompts[$name] ?? throw new PromptNotFoundException($name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -464,9 +449,4 @@ final class Registry implements ReferenceProviderInterface, ReferenceRegistryInt
|
||||||
|
|
||||||
return array_values(\array_slice($items, $offset, $limit));
|
return array_values(\array_slice($items, $offset, $limit));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setServerCapabilities(ServerCapabilities $serverCapabilities): void
|
|
||||||
{
|
|
||||||
$this->serverCapabilities = $serverCapabilities;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ use Mcp\Capability\Discovery\DocBlockParser;
|
||||||
use Mcp\Capability\Discovery\HandlerResolver;
|
use Mcp\Capability\Discovery\HandlerResolver;
|
||||||
use Mcp\Capability\Discovery\SchemaGenerator;
|
use Mcp\Capability\Discovery\SchemaGenerator;
|
||||||
use Mcp\Capability\Registry\ElementReference;
|
use Mcp\Capability\Registry\ElementReference;
|
||||||
use Mcp\Capability\Registry\ReferenceRegistryInterface;
|
use Mcp\Capability\RegistryInterface;
|
||||||
use Mcp\Exception\ConfigurationException;
|
use Mcp\Exception\ConfigurationException;
|
||||||
use Mcp\Schema\Annotations;
|
use Mcp\Schema\Annotations;
|
||||||
use Mcp\Schema\Icon;
|
use Mcp\Schema\Icon;
|
||||||
|
|
@ -86,7 +86,7 @@ final class ArrayLoader implements LoaderInterface
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function load(ReferenceRegistryInterface $registry): void
|
public function load(RegistryInterface $registry): void
|
||||||
{
|
{
|
||||||
$docBlockParser = new DocBlockParser(logger: $this->logger);
|
$docBlockParser = new DocBlockParser(logger: $this->logger);
|
||||||
$schemaGenerator = new SchemaGenerator($docBlockParser);
|
$schemaGenerator = new SchemaGenerator($docBlockParser);
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ namespace Mcp\Capability\Registry\Loader;
|
||||||
|
|
||||||
use Mcp\Capability\Discovery\CachedDiscoverer;
|
use Mcp\Capability\Discovery\CachedDiscoverer;
|
||||||
use Mcp\Capability\Discovery\Discoverer;
|
use Mcp\Capability\Discovery\Discoverer;
|
||||||
use Mcp\Capability\Registry\ReferenceRegistryInterface;
|
use Mcp\Capability\RegistryInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Psr\SimpleCache\CacheInterface;
|
use Psr\SimpleCache\CacheInterface;
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@ final class DiscoveryLoader implements LoaderInterface
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function load(ReferenceRegistryInterface $registry): void
|
public function load(RegistryInterface $registry): void
|
||||||
{
|
{
|
||||||
// This now encapsulates the discovery process
|
// This now encapsulates the discovery process
|
||||||
$discoverer = new Discoverer($this->logger);
|
$discoverer = new Discoverer($this->logger);
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,12 @@
|
||||||
|
|
||||||
namespace Mcp\Capability\Registry\Loader;
|
namespace Mcp\Capability\Registry\Loader;
|
||||||
|
|
||||||
use Mcp\Capability\Registry\ReferenceRegistryInterface;
|
use Mcp\Capability\RegistryInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Antoine Bluchet <soyuka@gmail.com>
|
* @author Antoine Bluchet <soyuka@gmail.com>
|
||||||
*/
|
*/
|
||||||
interface LoaderInterface
|
interface LoaderInterface
|
||||||
{
|
{
|
||||||
public function load(ReferenceRegistryInterface $registry): void;
|
public function load(RegistryInterface $registry): void;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@
|
||||||
|
|
||||||
namespace Mcp\Capability\Registry;
|
namespace Mcp\Capability\Registry;
|
||||||
|
|
||||||
|
use Mcp\Exception\InvalidArgumentException;
|
||||||
|
use Mcp\Exception\RegistryException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for handling execution of MCP elements.
|
* Interface for handling execution of MCP elements.
|
||||||
* Allows custom implementations of element execution logic.
|
* Allows custom implementations of element execution logic.
|
||||||
|
|
@ -27,8 +30,8 @@ interface ReferenceHandlerInterface
|
||||||
*
|
*
|
||||||
* @return mixed the result of the element execution
|
* @return mixed the result of the element execution
|
||||||
*
|
*
|
||||||
* @throws \Mcp\Exception\InvalidArgumentException if the handler is invalid
|
* @throws InvalidArgumentException if the handler is invalid
|
||||||
* @throws \Mcp\Exception\RegistryException if execution fails
|
* @throws RegistryException if execution fails
|
||||||
*/
|
*/
|
||||||
public function handle(ElementReference $reference, array $arguments): mixed;
|
public function handle(ElementReference $reference, array $arguments): mixed;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
<?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\Registry;
|
|
||||||
|
|
||||||
use Mcp\Exception\PromptNotFoundException;
|
|
||||||
use Mcp\Exception\ResourceNotFoundException;
|
|
||||||
use Mcp\Exception\ToolNotFoundException;
|
|
||||||
use Mcp\Schema\Page;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for providing access to registered MCP elements.
|
|
||||||
* Separates the concern of accessing elements from registering them.
|
|
||||||
*
|
|
||||||
* @author Pavel Buchnev <butschster@gmail.com>
|
|
||||||
*/
|
|
||||||
interface ReferenceProviderInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Gets a tool reference by name.
|
|
||||||
*
|
|
||||||
* @throws ToolNotFoundException
|
|
||||||
*/
|
|
||||||
public function getTool(string $name): ToolReference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a resource reference by URI (includes template matching if enabled).
|
|
||||||
*
|
|
||||||
* @throws ResourceNotFoundException
|
|
||||||
*/
|
|
||||||
public function getResource(string $uri, bool $includeTemplates = true): ResourceReference|ResourceTemplateReference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a resource template reference by URI template.
|
|
||||||
*
|
|
||||||
* @throws ResourceNotFoundException
|
|
||||||
*/
|
|
||||||
public function getResourceTemplate(string $uriTemplate): ResourceTemplateReference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a prompt reference by name.
|
|
||||||
*
|
|
||||||
* @throws PromptNotFoundException
|
|
||||||
*/
|
|
||||||
public function getPrompt(string $name): PromptReference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all registered tools.
|
|
||||||
*/
|
|
||||||
public function getTools(?int $limit = null, ?string $cursor = null): Page;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all registered resources.
|
|
||||||
*/
|
|
||||||
public function getResources(?int $limit = null, ?string $cursor = null): Page;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all registered prompts.
|
|
||||||
*/
|
|
||||||
public function getPrompts(?int $limit = null, ?string $cursor = null): Page;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all registered resource templates.
|
|
||||||
*/
|
|
||||||
public function getResourceTemplates(?int $limit = null, ?string $cursor = null): Page;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if any elements (manual or discovered) are currently registered.
|
|
||||||
*/
|
|
||||||
public function hasElements(): bool;
|
|
||||||
}
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
<?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\Registry;
|
|
||||||
|
|
||||||
use Mcp\Capability\Discovery\DiscoveryState;
|
|
||||||
use Mcp\Schema\Prompt;
|
|
||||||
use Mcp\Schema\Resource;
|
|
||||||
use Mcp\Schema\ResourceTemplate;
|
|
||||||
use Mcp\Schema\ServerCapabilities;
|
|
||||||
use Mcp\Schema\Tool;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @phpstan-import-type Handler from ElementReference
|
|
||||||
*
|
|
||||||
* Interface for registering MCP elements.
|
|
||||||
* Separates the concern of registering elements from accessing them.
|
|
||||||
*
|
|
||||||
* @author Pavel Buchnev <butschster@gmail.com>
|
|
||||||
*/
|
|
||||||
interface ReferenceRegistryInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Gets server capabilities based on registered elements.
|
|
||||||
*/
|
|
||||||
public function getCapabilities(): ServerCapabilities;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a tool with its handler.
|
|
||||||
*
|
|
||||||
* @param Handler $handler
|
|
||||||
*/
|
|
||||||
public function registerTool(Tool $tool, callable|array|string $handler, bool $isManual = false): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a resource with its handler.
|
|
||||||
*
|
|
||||||
* @param Handler $handler
|
|
||||||
*/
|
|
||||||
public function registerResource(Resource $resource, callable|array|string $handler, bool $isManual = false): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a resource template with its handler and completion providers.
|
|
||||||
*
|
|
||||||
* @param Handler $handler
|
|
||||||
* @param array<string, class-string|object> $completionProviders
|
|
||||||
*/
|
|
||||||
public function registerResourceTemplate(
|
|
||||||
ResourceTemplate $template,
|
|
||||||
callable|array|string $handler,
|
|
||||||
array $completionProviders = [],
|
|
||||||
bool $isManual = false,
|
|
||||||
): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a prompt with its handler and completion providers.
|
|
||||||
*
|
|
||||||
* @param Handler $handler
|
|
||||||
* @param array<string, class-string|object> $completionProviders
|
|
||||||
*/
|
|
||||||
public function registerPrompt(
|
|
||||||
Prompt $prompt,
|
|
||||||
callable|array|string $handler,
|
|
||||||
array $completionProviders = [],
|
|
||||||
bool $isManual = false,
|
|
||||||
): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear discovered elements from registry.
|
|
||||||
*/
|
|
||||||
public function clear(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current discovery state (only discovered elements, not manual ones).
|
|
||||||
*/
|
|
||||||
public function getDiscoveryState(): DiscoveryState;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set discovery state, replacing all discovered elements.
|
|
||||||
* Manual elements are preserved.
|
|
||||||
*/
|
|
||||||
public function setDiscoveryState(DiscoveryState $state): void;
|
|
||||||
}
|
|
||||||
160
src/Capability/RegistryInterface.php
Normal file
160
src/Capability/RegistryInterface.php
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
<?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;
|
||||||
|
|
||||||
|
use Mcp\Capability\Discovery\DiscoveryState;
|
||||||
|
use Mcp\Capability\Registry\ElementReference;
|
||||||
|
use Mcp\Capability\Registry\PromptReference;
|
||||||
|
use Mcp\Capability\Registry\ResourceReference;
|
||||||
|
use Mcp\Capability\Registry\ResourceTemplateReference;
|
||||||
|
use Mcp\Capability\Registry\ToolReference;
|
||||||
|
use Mcp\Exception\PromptNotFoundException;
|
||||||
|
use Mcp\Exception\ResourceNotFoundException;
|
||||||
|
use Mcp\Exception\ToolNotFoundException;
|
||||||
|
use Mcp\Schema\Page;
|
||||||
|
use Mcp\Schema\Prompt;
|
||||||
|
use Mcp\Schema\Resource;
|
||||||
|
use Mcp\Schema\ResourceTemplate;
|
||||||
|
use Mcp\Schema\Tool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-import-type Handler from ElementReference
|
||||||
|
*
|
||||||
|
* @author Kyrian Obikwelu <koshnawaza@gmail.com>
|
||||||
|
* @author Christopher Hertel <mail@christopher-hertel.de>
|
||||||
|
*/
|
||||||
|
interface RegistryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Registers a tool with its handler.
|
||||||
|
*
|
||||||
|
* @param Handler $handler
|
||||||
|
*/
|
||||||
|
public function registerTool(Tool $tool, callable|array|string $handler, bool $isManual = false): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a resource with its handler.
|
||||||
|
*
|
||||||
|
* @param Handler $handler
|
||||||
|
*/
|
||||||
|
public function registerResource(Resource $resource, callable|array|string $handler, bool $isManual = false): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a resource template with its handler and completion providers.
|
||||||
|
*
|
||||||
|
* @param Handler $handler
|
||||||
|
* @param array<string, class-string|object> $completionProviders
|
||||||
|
*/
|
||||||
|
public function registerResourceTemplate(
|
||||||
|
ResourceTemplate $template,
|
||||||
|
callable|array|string $handler,
|
||||||
|
array $completionProviders = [],
|
||||||
|
bool $isManual = false,
|
||||||
|
): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a prompt with its handler and completion providers.
|
||||||
|
*
|
||||||
|
* @param Handler $handler
|
||||||
|
* @param array<string, class-string|object> $completionProviders
|
||||||
|
*/
|
||||||
|
public function registerPrompt(
|
||||||
|
Prompt $prompt,
|
||||||
|
callable|array|string $handler,
|
||||||
|
array $completionProviders = [],
|
||||||
|
bool $isManual = false,
|
||||||
|
): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear discovered elements from registry.
|
||||||
|
*/
|
||||||
|
public function clear(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current discovery state (only discovered elements, not manual ones).
|
||||||
|
*/
|
||||||
|
public function getDiscoveryState(): DiscoveryState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set discovery state, replacing all discovered elements.
|
||||||
|
* Manual elements are preserved.
|
||||||
|
*/
|
||||||
|
public function setDiscoveryState(DiscoveryState $state): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool true if any tools are registered
|
||||||
|
*/
|
||||||
|
public function hasTools(): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all registered tools.
|
||||||
|
*/
|
||||||
|
public function getTools(?int $limit = null, ?string $cursor = null): Page;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a tool reference by name.
|
||||||
|
*
|
||||||
|
* @throws ToolNotFoundException
|
||||||
|
*/
|
||||||
|
public function getTool(string $name): ToolReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool true if any resources are registered
|
||||||
|
*/
|
||||||
|
public function hasResources(): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all registered resources.
|
||||||
|
*/
|
||||||
|
public function getResources(?int $limit = null, ?string $cursor = null): Page;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a resource reference by URI (includes template matching if enabled).
|
||||||
|
*
|
||||||
|
* @throws ResourceNotFoundException
|
||||||
|
*/
|
||||||
|
public function getResource(string $uri, bool $includeTemplates = true): ResourceReference|ResourceTemplateReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool true if any resource templates are registered
|
||||||
|
*/
|
||||||
|
public function hasResourceTemplates(): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all registered resource templates.
|
||||||
|
*/
|
||||||
|
public function getResourceTemplates(?int $limit = null, ?string $cursor = null): Page;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a resource template reference by URI template.
|
||||||
|
*
|
||||||
|
* @throws ResourceNotFoundException
|
||||||
|
*/
|
||||||
|
public function getResourceTemplate(string $uriTemplate): ResourceTemplateReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool true if any prompts are registered
|
||||||
|
*/
|
||||||
|
public function hasPrompts(): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all registered prompts.
|
||||||
|
*/
|
||||||
|
public function getPrompts(?int $limit = null, ?string $cursor = null): Page;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a prompt reference by name.
|
||||||
|
*
|
||||||
|
* @throws PromptNotFoundException
|
||||||
|
*/
|
||||||
|
public function getPrompt(string $name): PromptReference;
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ use Mcp\Capability\Registry\Loader\ArrayLoader;
|
||||||
use Mcp\Capability\Registry\Loader\DiscoveryLoader;
|
use Mcp\Capability\Registry\Loader\DiscoveryLoader;
|
||||||
use Mcp\Capability\Registry\Loader\LoaderInterface;
|
use Mcp\Capability\Registry\Loader\LoaderInterface;
|
||||||
use Mcp\Capability\Registry\ReferenceHandler;
|
use Mcp\Capability\Registry\ReferenceHandler;
|
||||||
|
use Mcp\Capability\RegistryInterface;
|
||||||
use Mcp\JsonRpc\MessageFactory;
|
use Mcp\JsonRpc\MessageFactory;
|
||||||
use Mcp\Schema\Annotations;
|
use Mcp\Schema\Annotations;
|
||||||
use Mcp\Schema\Enum\ProtocolVersion;
|
use Mcp\Schema\Enum\ProtocolVersion;
|
||||||
|
|
@ -47,6 +48,8 @@ final class Builder
|
||||||
{
|
{
|
||||||
private ?Implementation $serverInfo = null;
|
private ?Implementation $serverInfo = null;
|
||||||
|
|
||||||
|
private RegistryInterface $registry;
|
||||||
|
|
||||||
private ?LoggerInterface $logger = null;
|
private ?LoggerInterface $logger = null;
|
||||||
|
|
||||||
private ?CacheInterface $discoveryCache = null;
|
private ?CacheInterface $discoveryCache = null;
|
||||||
|
|
@ -248,6 +251,13 @@ final class Builder
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setRegistry(RegistryInterface $registry): self
|
||||||
|
{
|
||||||
|
$this->registry = $registry;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a PSR-3 logger instance. Defaults to NullLogger.
|
* Provides a PSR-3 logger instance. Defaults to NullLogger.
|
||||||
*/
|
*/
|
||||||
|
|
@ -440,7 +450,7 @@ final class Builder
|
||||||
{
|
{
|
||||||
$logger = $this->logger ?? new NullLogger();
|
$logger = $this->logger ?? new NullLogger();
|
||||||
$container = $this->container ?? new Container();
|
$container = $this->container ?? new Container();
|
||||||
$registry = new Registry($this->eventDispatcher, $logger);
|
$registry = $this->registry ?? new Registry($this->eventDispatcher, $logger);
|
||||||
|
|
||||||
$loaders = [
|
$loaders = [
|
||||||
...$this->loaders,
|
...$this->loaders,
|
||||||
|
|
@ -455,16 +465,22 @@ final class Builder
|
||||||
$loader->load($registry);
|
$loader->load($registry);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->serverCapabilities) {
|
|
||||||
$registry->setServerCapabilities($this->serverCapabilities);
|
|
||||||
}
|
|
||||||
|
|
||||||
$sessionTtl = $this->sessionTtl ?? 3600;
|
$sessionTtl = $this->sessionTtl ?? 3600;
|
||||||
$sessionFactory = $this->sessionFactory ?? new SessionFactory();
|
$sessionFactory = $this->sessionFactory ?? new SessionFactory();
|
||||||
$sessionStore = $this->sessionStore ?? new InMemorySessionStore($sessionTtl);
|
$sessionStore = $this->sessionStore ?? new InMemorySessionStore($sessionTtl);
|
||||||
$messageFactory = MessageFactory::make();
|
$messageFactory = MessageFactory::make();
|
||||||
|
|
||||||
$capabilities = $registry->getCapabilities();
|
$capabilities = $this->serverCapabilities ?? new ServerCapabilities(
|
||||||
|
tools: $registry->hasTools(),
|
||||||
|
toolsListChanged: $this->eventDispatcher instanceof EventDispatcherInterface,
|
||||||
|
resources: $registry->hasResources() || $registry->hasResourceTemplates(),
|
||||||
|
resourcesSubscribe: false,
|
||||||
|
resourcesListChanged: $this->eventDispatcher instanceof EventDispatcherInterface,
|
||||||
|
prompts: $registry->hasPrompts(),
|
||||||
|
promptsListChanged: $this->eventDispatcher instanceof EventDispatcherInterface,
|
||||||
|
logging: false,
|
||||||
|
completions: true,
|
||||||
|
);
|
||||||
$configuration = new Configuration($this->serverInfo, $capabilities, $this->paginationLimit, $this->instructions, $this->protocolVersion);
|
$configuration = new Configuration($this->serverInfo, $capabilities, $this->paginationLimit, $this->instructions, $this->protocolVersion);
|
||||||
$referenceHandler = new ReferenceHandler($container);
|
$referenceHandler = new ReferenceHandler($container);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
namespace Mcp\Server\Handler\Request;
|
namespace Mcp\Server\Handler\Request;
|
||||||
|
|
||||||
use Mcp\Capability\Registry\ReferenceHandlerInterface;
|
use Mcp\Capability\Registry\ReferenceHandlerInterface;
|
||||||
use Mcp\Capability\Registry\ReferenceProviderInterface;
|
use Mcp\Capability\RegistryInterface;
|
||||||
use Mcp\Exception\ToolCallException;
|
use Mcp\Exception\ToolCallException;
|
||||||
use Mcp\Exception\ToolNotFoundException;
|
use Mcp\Exception\ToolNotFoundException;
|
||||||
use Mcp\Schema\Content\TextContent;
|
use Mcp\Schema\Content\TextContent;
|
||||||
|
|
@ -34,7 +34,7 @@ use Psr\Log\NullLogger;
|
||||||
final class CallToolHandler implements RequestHandlerInterface
|
final class CallToolHandler implements RequestHandlerInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ReferenceProviderInterface $referenceProvider,
|
private readonly RegistryInterface $registry,
|
||||||
private readonly ReferenceHandlerInterface $referenceHandler,
|
private readonly ReferenceHandlerInterface $referenceHandler,
|
||||||
private readonly LoggerInterface $logger = new NullLogger(),
|
private readonly LoggerInterface $logger = new NullLogger(),
|
||||||
) {
|
) {
|
||||||
|
|
@ -58,7 +58,7 @@ final class CallToolHandler implements RequestHandlerInterface
|
||||||
$this->logger->debug('Executing tool', ['name' => $toolName, 'arguments' => $arguments]);
|
$this->logger->debug('Executing tool', ['name' => $toolName, 'arguments' => $arguments]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$reference = $this->referenceProvider->getTool($toolName);
|
$reference = $this->registry->getTool($toolName);
|
||||||
|
|
||||||
$arguments['_session'] = $session;
|
$arguments['_session'] = $session;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
namespace Mcp\Server\Handler\Request;
|
namespace Mcp\Server\Handler\Request;
|
||||||
|
|
||||||
use Mcp\Capability\Completion\ProviderInterface;
|
use Mcp\Capability\Completion\ProviderInterface;
|
||||||
use Mcp\Capability\Registry\ReferenceProviderInterface;
|
use Mcp\Capability\RegistryInterface;
|
||||||
use Mcp\Exception\PromptNotFoundException;
|
use Mcp\Exception\PromptNotFoundException;
|
||||||
use Mcp\Exception\ResourceNotFoundException;
|
use Mcp\Exception\ResourceNotFoundException;
|
||||||
use Mcp\Schema\JsonRpc\Error;
|
use Mcp\Schema\JsonRpc\Error;
|
||||||
|
|
@ -35,7 +35,7 @@ use Psr\Container\ContainerInterface;
|
||||||
final class CompletionCompleteHandler implements RequestHandlerInterface
|
final class CompletionCompleteHandler implements RequestHandlerInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ReferenceProviderInterface $referenceProvider,
|
private readonly RegistryInterface $registry,
|
||||||
private readonly ?ContainerInterface $container = null,
|
private readonly ?ContainerInterface $container = null,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
@ -57,8 +57,8 @@ final class CompletionCompleteHandler implements RequestHandlerInterface
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$reference = match (true) {
|
$reference = match (true) {
|
||||||
$request->ref instanceof PromptReference => $this->referenceProvider->getPrompt($request->ref->name),
|
$request->ref instanceof PromptReference => $this->registry->getPrompt($request->ref->name),
|
||||||
$request->ref instanceof ResourceReference => $this->referenceProvider->getResource($request->ref->uri),
|
$request->ref instanceof ResourceReference => $this->registry->getResource($request->ref->uri),
|
||||||
};
|
};
|
||||||
|
|
||||||
$providers = $reference->completionProviders;
|
$providers = $reference->completionProviders;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
namespace Mcp\Server\Handler\Request;
|
namespace Mcp\Server\Handler\Request;
|
||||||
|
|
||||||
use Mcp\Capability\Registry\ReferenceHandlerInterface;
|
use Mcp\Capability\Registry\ReferenceHandlerInterface;
|
||||||
use Mcp\Capability\Registry\ReferenceProviderInterface;
|
use Mcp\Capability\RegistryInterface;
|
||||||
use Mcp\Exception\PromptGetException;
|
use Mcp\Exception\PromptGetException;
|
||||||
use Mcp\Exception\PromptNotFoundException;
|
use Mcp\Exception\PromptNotFoundException;
|
||||||
use Mcp\Schema\JsonRpc\Error;
|
use Mcp\Schema\JsonRpc\Error;
|
||||||
|
|
@ -32,7 +32,7 @@ use Psr\Log\NullLogger;
|
||||||
final class GetPromptHandler implements RequestHandlerInterface
|
final class GetPromptHandler implements RequestHandlerInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ReferenceProviderInterface $referenceProvider,
|
private readonly RegistryInterface $registry,
|
||||||
private readonly ReferenceHandlerInterface $referenceHandler,
|
private readonly ReferenceHandlerInterface $referenceHandler,
|
||||||
private readonly LoggerInterface $logger = new NullLogger(),
|
private readonly LoggerInterface $logger = new NullLogger(),
|
||||||
) {
|
) {
|
||||||
|
|
@ -54,7 +54,7 @@ final class GetPromptHandler implements RequestHandlerInterface
|
||||||
$arguments = $request->arguments ?? [];
|
$arguments = $request->arguments ?? [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$reference = $this->referenceProvider->getPrompt($promptName);
|
$reference = $this->registry->getPrompt($promptName);
|
||||||
|
|
||||||
$arguments['_session'] = $session;
|
$arguments['_session'] = $session;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
namespace Mcp\Server\Handler\Request;
|
namespace Mcp\Server\Handler\Request;
|
||||||
|
|
||||||
use Mcp\Capability\Registry\ReferenceProviderInterface;
|
use Mcp\Capability\RegistryInterface;
|
||||||
use Mcp\Exception\InvalidCursorException;
|
use Mcp\Exception\InvalidCursorException;
|
||||||
use Mcp\Schema\JsonRpc\Request;
|
use Mcp\Schema\JsonRpc\Request;
|
||||||
use Mcp\Schema\JsonRpc\Response;
|
use Mcp\Schema\JsonRpc\Response;
|
||||||
|
|
@ -27,7 +27,7 @@ use Mcp\Server\Session\SessionInterface;
|
||||||
final class ListPromptsHandler implements RequestHandlerInterface
|
final class ListPromptsHandler implements RequestHandlerInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ReferenceProviderInterface $registry,
|
private readonly RegistryInterface $registry,
|
||||||
private readonly int $pageSize = 20,
|
private readonly int $pageSize = 20,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
namespace Mcp\Server\Handler\Request;
|
namespace Mcp\Server\Handler\Request;
|
||||||
|
|
||||||
use Mcp\Capability\Registry\ReferenceProviderInterface;
|
use Mcp\Capability\RegistryInterface;
|
||||||
use Mcp\Exception\InvalidCursorException;
|
use Mcp\Exception\InvalidCursorException;
|
||||||
use Mcp\Schema\JsonRpc\Request;
|
use Mcp\Schema\JsonRpc\Request;
|
||||||
use Mcp\Schema\JsonRpc\Response;
|
use Mcp\Schema\JsonRpc\Response;
|
||||||
|
|
@ -27,7 +27,7 @@ use Mcp\Server\Session\SessionInterface;
|
||||||
final class ListResourceTemplatesHandler implements RequestHandlerInterface
|
final class ListResourceTemplatesHandler implements RequestHandlerInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ReferenceProviderInterface $registry,
|
private readonly RegistryInterface $registry,
|
||||||
private readonly int $pageSize = 20,
|
private readonly int $pageSize = 20,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
namespace Mcp\Server\Handler\Request;
|
namespace Mcp\Server\Handler\Request;
|
||||||
|
|
||||||
use Mcp\Capability\Registry\ReferenceProviderInterface;
|
use Mcp\Capability\RegistryInterface;
|
||||||
use Mcp\Exception\InvalidCursorException;
|
use Mcp\Exception\InvalidCursorException;
|
||||||
use Mcp\Schema\JsonRpc\Request;
|
use Mcp\Schema\JsonRpc\Request;
|
||||||
use Mcp\Schema\JsonRpc\Response;
|
use Mcp\Schema\JsonRpc\Response;
|
||||||
|
|
@ -27,7 +27,7 @@ use Mcp\Server\Session\SessionInterface;
|
||||||
final class ListResourcesHandler implements RequestHandlerInterface
|
final class ListResourcesHandler implements RequestHandlerInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ReferenceProviderInterface $registry,
|
private readonly RegistryInterface $registry,
|
||||||
private readonly int $pageSize = 20,
|
private readonly int $pageSize = 20,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
namespace Mcp\Server\Handler\Request;
|
namespace Mcp\Server\Handler\Request;
|
||||||
|
|
||||||
use Mcp\Capability\Registry\ReferenceProviderInterface;
|
use Mcp\Capability\RegistryInterface;
|
||||||
use Mcp\Exception\InvalidCursorException;
|
use Mcp\Exception\InvalidCursorException;
|
||||||
use Mcp\Schema\JsonRpc\Request;
|
use Mcp\Schema\JsonRpc\Request;
|
||||||
use Mcp\Schema\JsonRpc\Response;
|
use Mcp\Schema\JsonRpc\Response;
|
||||||
|
|
@ -28,7 +28,7 @@ use Mcp\Server\Session\SessionInterface;
|
||||||
final class ListToolsHandler implements RequestHandlerInterface
|
final class ListToolsHandler implements RequestHandlerInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ReferenceProviderInterface $registry,
|
private readonly RegistryInterface $registry,
|
||||||
private readonly int $pageSize = 20,
|
private readonly int $pageSize = 20,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@
|
||||||
namespace Mcp\Server\Handler\Request;
|
namespace Mcp\Server\Handler\Request;
|
||||||
|
|
||||||
use Mcp\Capability\Registry\ReferenceHandlerInterface;
|
use Mcp\Capability\Registry\ReferenceHandlerInterface;
|
||||||
use Mcp\Capability\Registry\ReferenceProviderInterface;
|
|
||||||
use Mcp\Capability\Registry\ResourceTemplateReference;
|
use Mcp\Capability\Registry\ResourceTemplateReference;
|
||||||
|
use Mcp\Capability\RegistryInterface;
|
||||||
use Mcp\Exception\ResourceNotFoundException;
|
use Mcp\Exception\ResourceNotFoundException;
|
||||||
use Mcp\Exception\ResourceReadException;
|
use Mcp\Exception\ResourceReadException;
|
||||||
use Mcp\Schema\JsonRpc\Error;
|
use Mcp\Schema\JsonRpc\Error;
|
||||||
|
|
@ -33,7 +33,7 @@ use Psr\Log\NullLogger;
|
||||||
final class ReadResourceHandler implements RequestHandlerInterface
|
final class ReadResourceHandler implements RequestHandlerInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ReferenceProviderInterface $referenceProvider,
|
private readonly RegistryInterface $referenceProvider,
|
||||||
private readonly ReferenceHandlerInterface $referenceHandler,
|
private readonly ReferenceHandlerInterface $referenceHandler,
|
||||||
private readonly LoggerInterface $logger = new NullLogger(),
|
private readonly LoggerInterface $logger = new NullLogger(),
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,9 @@
|
||||||
* file that was distributed with this source code.
|
* file that was distributed with this source code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Mcp\Tests\Unit\Capability\Registry;
|
namespace Mcp\Tests\Unit\Capability;
|
||||||
|
|
||||||
|
use Mcp\Capability\Completion\EnumCompletionProvider;
|
||||||
use Mcp\Capability\Registry;
|
use Mcp\Capability\Registry;
|
||||||
use Mcp\Capability\Registry\PromptReference;
|
use Mcp\Capability\Registry\PromptReference;
|
||||||
use Mcp\Capability\Registry\ResourceReference;
|
use Mcp\Capability\Registry\ResourceReference;
|
||||||
|
|
@ -23,15 +24,51 @@ use Mcp\Schema\Prompt;
|
||||||
use Mcp\Schema\Resource;
|
use Mcp\Schema\Resource;
|
||||||
use Mcp\Schema\ResourceTemplate;
|
use Mcp\Schema\ResourceTemplate;
|
||||||
use Mcp\Schema\Tool;
|
use Mcp\Schema\Tool;
|
||||||
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
class RegistryProviderTest extends TestCase
|
class RegistryTest extends TestCase
|
||||||
{
|
{
|
||||||
private Registry $registry;
|
private Registry $registry;
|
||||||
|
private LoggerInterface|MockObject $logger;
|
||||||
|
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
$this->registry = new Registry();
|
$this->logger = $this->createMock(LoggerInterface::class);
|
||||||
|
$this->registry = new Registry(null, $this->logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHasserReturnFalseForEmptyRegistry(): void
|
||||||
|
{
|
||||||
|
$this->assertFalse($this->registry->hasTools());
|
||||||
|
$this->assertFalse($this->registry->hasResources());
|
||||||
|
$this->assertFalse($this->registry->hasResourceTemplates());
|
||||||
|
$this->assertFalse($this->registry->hasPrompts());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHasToolsReturnsTrueWhenToolIsRegistered(): void
|
||||||
|
{
|
||||||
|
$tool = $this->createValidTool('test_tool');
|
||||||
|
$this->registry->registerTool($tool, fn () => 'result');
|
||||||
|
|
||||||
|
$this->assertTrue($this->registry->hasTools());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetToolsReturnsAllRegisteredTools(): void
|
||||||
|
{
|
||||||
|
$tool1 = $this->createValidTool('tool1');
|
||||||
|
$tool2 = $this->createValidTool('tool2');
|
||||||
|
|
||||||
|
$this->registry->registerTool($tool1, fn () => 'result1');
|
||||||
|
$this->registry->registerTool($tool2, fn () => 'result2');
|
||||||
|
|
||||||
|
$tools = $this->registry->getTools();
|
||||||
|
$this->assertCount(2, $tools);
|
||||||
|
$this->assertArrayHasKey('tool1', $tools->references);
|
||||||
|
$this->assertArrayHasKey('tool2', $tools->references);
|
||||||
|
$this->assertInstanceOf(Tool::class, $tools->references['tool1']);
|
||||||
|
$this->assertInstanceOf(Tool::class, $tools->references['tool2']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetToolReturnsRegisteredTool(): void
|
public function testGetToolReturnsRegisteredTool(): void
|
||||||
|
|
@ -48,6 +85,47 @@ class RegistryProviderTest extends TestCase
|
||||||
$this->assertFalse($toolRef->isManual);
|
$this->assertFalse($toolRef->isManual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRegisterToolWithManualFlag(): void
|
||||||
|
{
|
||||||
|
$tool = $this->createValidTool('test_tool');
|
||||||
|
$handler = fn () => 'result';
|
||||||
|
|
||||||
|
$this->registry->registerTool($tool, $handler, true);
|
||||||
|
|
||||||
|
$toolRef = $this->registry->getTool('test_tool');
|
||||||
|
$this->assertTrue($toolRef->isManual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRegisterToolIgnoresDiscoveredWhenManualExists(): void
|
||||||
|
{
|
||||||
|
$manualTool = $this->createValidTool('test_tool');
|
||||||
|
$discoveredTool = $this->createValidTool('test_tool');
|
||||||
|
|
||||||
|
$this->registry->registerTool($manualTool, fn () => 'manual', true);
|
||||||
|
|
||||||
|
$this->logger
|
||||||
|
->expects($this->once())
|
||||||
|
->method('debug')
|
||||||
|
->with('Ignoring discovered tool "test_tool" as it conflicts with a manually registered one.');
|
||||||
|
|
||||||
|
$this->registry->registerTool($discoveredTool, fn () => 'discovered');
|
||||||
|
|
||||||
|
$toolRef = $this->registry->getTool('test_tool');
|
||||||
|
$this->assertTrue($toolRef->isManual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRegisterToolOverridesDiscoveredWithManual(): void
|
||||||
|
{
|
||||||
|
$discoveredTool = $this->createValidTool('test_tool');
|
||||||
|
$manualTool = $this->createValidTool('test_tool');
|
||||||
|
|
||||||
|
$this->registry->registerTool($discoveredTool, fn () => 'discovered');
|
||||||
|
$this->registry->registerTool($manualTool, fn () => 'manual', true);
|
||||||
|
|
||||||
|
$toolRef = $this->registry->getTool('test_tool');
|
||||||
|
$this->assertTrue($toolRef->isManual);
|
||||||
|
}
|
||||||
|
|
||||||
public function testGetToolThrowsExceptionForUnregisteredTool(): void
|
public function testGetToolThrowsExceptionForUnregisteredTool(): void
|
||||||
{
|
{
|
||||||
$this->expectException(ToolNotFoundException::class);
|
$this->expectException(ToolNotFoundException::class);
|
||||||
|
|
@ -56,6 +134,30 @@ class RegistryProviderTest extends TestCase
|
||||||
$this->registry->getTool('non_existent_tool');
|
$this->registry->getTool('non_existent_tool');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testHasResourceReturnsTrueWhenResourceIsRegistered(): void
|
||||||
|
{
|
||||||
|
$resource = $this->createValidResource('test://resource');
|
||||||
|
$this->registry->registerResource($resource, fn () => 'content');
|
||||||
|
|
||||||
|
$this->assertTrue($this->registry->hasResources());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetResourcesReturnsAllRegisteredResources(): void
|
||||||
|
{
|
||||||
|
$resource1 = $this->createValidResource('test://resource1');
|
||||||
|
$resource2 = $this->createValidResource('test://resource2');
|
||||||
|
|
||||||
|
$this->registry->registerResource($resource1, fn () => 'content1');
|
||||||
|
$this->registry->registerResource($resource2, fn () => 'content2');
|
||||||
|
|
||||||
|
$resources = $this->registry->getResources();
|
||||||
|
$this->assertCount(2, $resources);
|
||||||
|
$this->assertArrayHasKey('test://resource1', $resources->references);
|
||||||
|
$this->assertArrayHasKey('test://resource2', $resources->references);
|
||||||
|
$this->assertInstanceOf(Resource::class, $resources->references['test://resource1']);
|
||||||
|
$this->assertInstanceOf(Resource::class, $resources->references['test://resource2']);
|
||||||
|
}
|
||||||
|
|
||||||
public function testGetResourceReturnsRegisteredResource(): void
|
public function testGetResourceReturnsRegisteredResource(): void
|
||||||
{
|
{
|
||||||
$resource = $this->createValidResource('test://resource');
|
$resource = $this->createValidResource('test://resource');
|
||||||
|
|
@ -70,6 +172,35 @@ class RegistryProviderTest extends TestCase
|
||||||
$this->assertFalse($resourceRef->isManual);
|
$this->assertFalse($resourceRef->isManual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRegisterResourceWithManualFlag(): void
|
||||||
|
{
|
||||||
|
$resource = $this->createValidResource('test://resource');
|
||||||
|
$handler = fn () => 'content';
|
||||||
|
|
||||||
|
$this->registry->registerResource($resource, $handler, true);
|
||||||
|
|
||||||
|
$resourceRef = $this->registry->getResource('test://resource');
|
||||||
|
$this->assertTrue($resourceRef->isManual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRegisterResourceIgnoresDiscoveredWhenManualExists(): void
|
||||||
|
{
|
||||||
|
$manualResource = $this->createValidResource('test://resource');
|
||||||
|
$discoveredResource = $this->createValidResource('test://resource');
|
||||||
|
|
||||||
|
$this->registry->registerResource($manualResource, fn () => 'manual', true);
|
||||||
|
|
||||||
|
$this->logger
|
||||||
|
->expects($this->once())
|
||||||
|
->method('debug')
|
||||||
|
->with('Ignoring discovered resource "test://resource" as it conflicts with a manually registered one.');
|
||||||
|
|
||||||
|
$this->registry->registerResource($discoveredResource, fn () => 'discovered');
|
||||||
|
|
||||||
|
$resourceRef = $this->registry->getResource('test://resource');
|
||||||
|
$this->assertTrue($resourceRef->isManual);
|
||||||
|
}
|
||||||
|
|
||||||
public function testGetResourceThrowsExceptionForUnregisteredResource(): void
|
public function testGetResourceThrowsExceptionForUnregisteredResource(): void
|
||||||
{
|
{
|
||||||
$this->expectException(ResourceNotFoundException::class);
|
$this->expectException(ResourceNotFoundException::class);
|
||||||
|
|
@ -78,6 +209,60 @@ class RegistryProviderTest extends TestCase
|
||||||
$this->registry->getResource('test://non_existent');
|
$this->registry->getResource('test://non_existent');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testHasResourceTemplatesReturnsTrueWhenResourceTemplateIsRegistered(): void
|
||||||
|
{
|
||||||
|
$template = $this->createValidResourceTemplate('test://{id}');
|
||||||
|
$this->registry->registerResourceTemplate($template, fn () => 'content');
|
||||||
|
|
||||||
|
$this->assertTrue($this->registry->hasResourceTemplates());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetResourceTemplatesReturnsAllRegisteredTemplates(): void
|
||||||
|
{
|
||||||
|
$template1 = $this->createValidResourceTemplate('test1://{id}');
|
||||||
|
$template2 = $this->createValidResourceTemplate('test2://{category}');
|
||||||
|
|
||||||
|
$this->registry->registerResourceTemplate($template1, fn () => 'content1');
|
||||||
|
$this->registry->registerResourceTemplate($template2, fn () => 'content2');
|
||||||
|
|
||||||
|
$templates = $this->registry->getResourceTemplates();
|
||||||
|
$this->assertCount(2, $templates);
|
||||||
|
$this->assertArrayHasKey('test1://{id}', $templates->references);
|
||||||
|
$this->assertArrayHasKey('test2://{category}', $templates->references);
|
||||||
|
$this->assertInstanceOf(ResourceTemplate::class, $templates->references['test1://{id}']);
|
||||||
|
$this->assertInstanceOf(ResourceTemplate::class, $templates->references['test2://{category}']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetResourceTemplateReturnsRegisteredTemplate(): void
|
||||||
|
{
|
||||||
|
$template = $this->createValidResourceTemplate('test://{id}');
|
||||||
|
$handler = fn (string $id) => "content for {$id}";
|
||||||
|
|
||||||
|
$this->registry->registerResourceTemplate($template, $handler);
|
||||||
|
|
||||||
|
$templateRef = $this->registry->getResourceTemplate('test://{id}');
|
||||||
|
$this->assertInstanceOf(ResourceTemplateReference::class, $templateRef);
|
||||||
|
$this->assertEquals($template->uriTemplate, $templateRef->resourceTemplate->uriTemplate);
|
||||||
|
$this->assertEquals($handler, $templateRef->handler);
|
||||||
|
$this->assertFalse($templateRef->isManual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetResourcePrefersDirectResourceOverTemplate(): void
|
||||||
|
{
|
||||||
|
$resource = $this->createValidResource('test://123');
|
||||||
|
$resourceHandler = fn () => 'direct resource';
|
||||||
|
|
||||||
|
$template = $this->createValidResourceTemplate('test://{id}');
|
||||||
|
$templateHandler = fn (string $id) => "template for {$id}";
|
||||||
|
|
||||||
|
$this->registry->registerResource($resource, $resourceHandler);
|
||||||
|
$this->registry->registerResourceTemplate($template, $templateHandler);
|
||||||
|
|
||||||
|
$resourceRef = $this->registry->getResource('test://123');
|
||||||
|
$this->assertInstanceOf(ResourceReference::class, $resourceRef);
|
||||||
|
$this->assertEquals($resource->uri, $resourceRef->schema->uri);
|
||||||
|
}
|
||||||
|
|
||||||
public function testGetResourceMatchesResourceTemplate(): void
|
public function testGetResourceMatchesResourceTemplate(): void
|
||||||
{
|
{
|
||||||
$template = $this->createValidResourceTemplate('test://{id}');
|
$template = $this->createValidResourceTemplate('test://{id}');
|
||||||
|
|
@ -104,34 +289,47 @@ class RegistryProviderTest extends TestCase
|
||||||
$this->registry->getResource('test://123', false);
|
$this->registry->getResource('test://123', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetResourcePrefersDirectResourceOverTemplate(): void
|
public function testRegisterResourceTemplateWithCompletionProviders(): void
|
||||||
{
|
|
||||||
$resource = $this->createValidResource('test://123');
|
|
||||||
$resourceHandler = fn () => 'direct resource';
|
|
||||||
|
|
||||||
$template = $this->createValidResourceTemplate('test://{id}');
|
|
||||||
$templateHandler = fn (string $id) => "template for {$id}";
|
|
||||||
|
|
||||||
$this->registry->registerResource($resource, $resourceHandler);
|
|
||||||
$this->registry->registerResourceTemplate($template, $templateHandler);
|
|
||||||
|
|
||||||
$resourceRef = $this->registry->getResource('test://123');
|
|
||||||
$this->assertInstanceOf(ResourceReference::class, $resourceRef);
|
|
||||||
$this->assertEquals($resource->uri, $resourceRef->schema->uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetResourceTemplateReturnsRegisteredTemplate(): void
|
|
||||||
{
|
{
|
||||||
$template = $this->createValidResourceTemplate('test://{id}');
|
$template = $this->createValidResourceTemplate('test://{id}');
|
||||||
$handler = fn (string $id) => "content for {$id}";
|
$completionProviders = ['id' => EnumCompletionProvider::class];
|
||||||
|
|
||||||
$this->registry->registerResourceTemplate($template, $handler);
|
$this->registry->registerResourceTemplate($template, fn () => 'content', $completionProviders);
|
||||||
|
|
||||||
$templateRef = $this->registry->getResourceTemplate('test://{id}');
|
$templateRef = $this->registry->getResourceTemplate('test://{id}');
|
||||||
$this->assertInstanceOf(ResourceTemplateReference::class, $templateRef);
|
$this->assertEquals($completionProviders, $templateRef->completionProviders);
|
||||||
$this->assertEquals($template->uriTemplate, $templateRef->resourceTemplate->uriTemplate);
|
}
|
||||||
$this->assertEquals($handler, $templateRef->handler);
|
|
||||||
$this->assertFalse($templateRef->isManual);
|
public function testRegisterResourceTemplateIgnoresDiscoveredWhenManualExists(): void
|
||||||
|
{
|
||||||
|
$manualTemplate = $this->createValidResourceTemplate('test://{id}');
|
||||||
|
$discoveredTemplate = $this->createValidResourceTemplate('test://{id}');
|
||||||
|
|
||||||
|
$this->registry->registerResourceTemplate($manualTemplate, fn () => 'manual', [], true);
|
||||||
|
|
||||||
|
$this->logger
|
||||||
|
->expects($this->once())
|
||||||
|
->method('debug')
|
||||||
|
->with('Ignoring discovered template "test://{id}" as it conflicts with a manually registered one.');
|
||||||
|
|
||||||
|
$this->registry->registerResourceTemplate($discoveredTemplate, fn () => 'discovered');
|
||||||
|
|
||||||
|
$templateRef = $this->registry->getResourceTemplate('test://{id}');
|
||||||
|
$this->assertTrue($templateRef->isManual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testResourceTemplateMatchingPrefersMoreSpecificMatches(): void
|
||||||
|
{
|
||||||
|
$specificTemplate = $this->createValidResourceTemplate('test://users/{userId}/profile');
|
||||||
|
$genericTemplate = $this->createValidResourceTemplate('test://users/{userId}');
|
||||||
|
|
||||||
|
$this->registry->registerResourceTemplate($genericTemplate, fn () => 'generic');
|
||||||
|
$this->registry->registerResourceTemplate($specificTemplate, fn () => 'specific');
|
||||||
|
|
||||||
|
// Should match the more specific template first
|
||||||
|
$resourceRef = $this->registry->getResource('test://users/123/profile');
|
||||||
|
$this->assertInstanceOf(ResourceTemplateReference::class, $resourceRef);
|
||||||
|
$this->assertEquals('test://users/{userId}/profile', $resourceRef->resourceTemplate->uriTemplate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetResourceTemplateThrowsExceptionForUnregisteredTemplate(): void
|
public function testGetResourceTemplateThrowsExceptionForUnregisteredTemplate(): void
|
||||||
|
|
@ -142,58 +340,12 @@ class RegistryProviderTest extends TestCase
|
||||||
$this->registry->getResourceTemplate('test://{non_existent}');
|
$this->registry->getResourceTemplate('test://{non_existent}');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetPromptReturnsRegisteredPrompt(): void
|
public function testHasPromptsReturnsTrueWhenPromptIsRegistered(): void
|
||||||
{
|
{
|
||||||
$prompt = $this->createValidPrompt('test_prompt');
|
$prompt = $this->createValidPrompt('test_prompt');
|
||||||
$handler = fn () => ['role' => 'user', 'content' => 'test message'];
|
$this->registry->registerPrompt($prompt, fn () => []);
|
||||||
|
|
||||||
$this->registry->registerPrompt($prompt, $handler);
|
$this->assertTrue($this->registry->hasPrompts());
|
||||||
|
|
||||||
$promptRef = $this->registry->getPrompt('test_prompt');
|
|
||||||
$this->assertInstanceOf(PromptReference::class, $promptRef);
|
|
||||||
$this->assertEquals($prompt->name, $promptRef->prompt->name);
|
|
||||||
$this->assertEquals($handler, $promptRef->handler);
|
|
||||||
$this->assertFalse($promptRef->isManual);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetPromptThrowsExceptionForUnregisteredPrompt(): void
|
|
||||||
{
|
|
||||||
$this->expectException(PromptNotFoundException::class);
|
|
||||||
$this->expectExceptionMessage('Prompt not found: "non_existent_prompt".');
|
|
||||||
|
|
||||||
$this->registry->getPrompt('non_existent_prompt');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetToolsReturnsAllRegisteredTools(): void
|
|
||||||
{
|
|
||||||
$tool1 = $this->createValidTool('tool1');
|
|
||||||
$tool2 = $this->createValidTool('tool2');
|
|
||||||
|
|
||||||
$this->registry->registerTool($tool1, fn () => 'result1');
|
|
||||||
$this->registry->registerTool($tool2, fn () => 'result2');
|
|
||||||
|
|
||||||
$tools = $this->registry->getTools();
|
|
||||||
$this->assertCount(2, $tools);
|
|
||||||
$this->assertArrayHasKey('tool1', $tools->references);
|
|
||||||
$this->assertArrayHasKey('tool2', $tools->references);
|
|
||||||
$this->assertInstanceOf(Tool::class, $tools->references['tool1']);
|
|
||||||
$this->assertInstanceOf(Tool::class, $tools->references['tool2']);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetResourcesReturnsAllRegisteredResources(): void
|
|
||||||
{
|
|
||||||
$resource1 = $this->createValidResource('test://resource1');
|
|
||||||
$resource2 = $this->createValidResource('test://resource2');
|
|
||||||
|
|
||||||
$this->registry->registerResource($resource1, fn () => 'content1');
|
|
||||||
$this->registry->registerResource($resource2, fn () => 'content2');
|
|
||||||
|
|
||||||
$resources = $this->registry->getResources();
|
|
||||||
$this->assertCount(2, $resources);
|
|
||||||
$this->assertArrayHasKey('test://resource1', $resources->references);
|
|
||||||
$this->assertArrayHasKey('test://resource2', $resources->references);
|
|
||||||
$this->assertInstanceOf(Resource::class, $resources->references['test://resource1']);
|
|
||||||
$this->assertInstanceOf(Resource::class, $resources->references['test://resource2']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetPromptsReturnsAllRegisteredPrompts(): void
|
public function testGetPromptsReturnsAllRegisteredPrompts(): void
|
||||||
|
|
@ -212,71 +364,167 @@ class RegistryProviderTest extends TestCase
|
||||||
$this->assertInstanceOf(Prompt::class, $prompts->references['prompt2']);
|
$this->assertInstanceOf(Prompt::class, $prompts->references['prompt2']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetResourceTemplatesReturnsAllRegisteredTemplates(): void
|
public function testGetPromptReturnsRegisteredPrompt(): void
|
||||||
{
|
|
||||||
$template1 = $this->createValidResourceTemplate('test1://{id}');
|
|
||||||
$template2 = $this->createValidResourceTemplate('test2://{category}');
|
|
||||||
|
|
||||||
$this->registry->registerResourceTemplate($template1, fn () => 'content1');
|
|
||||||
$this->registry->registerResourceTemplate($template2, fn () => 'content2');
|
|
||||||
|
|
||||||
$templates = $this->registry->getResourceTemplates();
|
|
||||||
$this->assertCount(2, $templates);
|
|
||||||
$this->assertArrayHasKey('test1://{id}', $templates->references);
|
|
||||||
$this->assertArrayHasKey('test2://{category}', $templates->references);
|
|
||||||
$this->assertInstanceOf(ResourceTemplate::class, $templates->references['test1://{id}']);
|
|
||||||
$this->assertInstanceOf(ResourceTemplate::class, $templates->references['test2://{category}']);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testHasElementsReturnsFalseForEmptyRegistry(): void
|
|
||||||
{
|
|
||||||
$this->assertFalse($this->registry->hasElements());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testHasElementsReturnsTrueWhenToolIsRegistered(): void
|
|
||||||
{
|
|
||||||
$tool = $this->createValidTool('test_tool');
|
|
||||||
$this->registry->registerTool($tool, fn () => 'result');
|
|
||||||
|
|
||||||
$this->assertTrue($this->registry->hasElements());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testHasElementsReturnsTrueWhenResourceIsRegistered(): void
|
|
||||||
{
|
|
||||||
$resource = $this->createValidResource('test://resource');
|
|
||||||
$this->registry->registerResource($resource, fn () => 'content');
|
|
||||||
|
|
||||||
$this->assertTrue($this->registry->hasElements());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testHasElementsReturnsTrueWhenPromptIsRegistered(): void
|
|
||||||
{
|
{
|
||||||
$prompt = $this->createValidPrompt('test_prompt');
|
$prompt = $this->createValidPrompt('test_prompt');
|
||||||
$this->registry->registerPrompt($prompt, fn () => []);
|
$handler = fn () => ['role' => 'user', 'content' => 'test message'];
|
||||||
|
|
||||||
$this->assertTrue($this->registry->hasElements());
|
$this->registry->registerPrompt($prompt, $handler);
|
||||||
|
|
||||||
|
$promptRef = $this->registry->getPrompt('test_prompt');
|
||||||
|
$this->assertInstanceOf(PromptReference::class, $promptRef);
|
||||||
|
$this->assertEquals($prompt->name, $promptRef->prompt->name);
|
||||||
|
$this->assertEquals($handler, $promptRef->handler);
|
||||||
|
$this->assertFalse($promptRef->isManual);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testHasElementsReturnsTrueWhenResourceTemplateIsRegistered(): void
|
public function testRegisterPromptWithCompletionProviders(): void
|
||||||
{
|
{
|
||||||
$template = $this->createValidResourceTemplate('test://{id}');
|
$prompt = $this->createValidPrompt('test_prompt');
|
||||||
$this->registry->registerResourceTemplate($template, fn () => 'content');
|
$completionProviders = ['param' => EnumCompletionProvider::class];
|
||||||
|
|
||||||
$this->assertTrue($this->registry->hasElements());
|
$this->registry->registerPrompt($prompt, fn () => [], $completionProviders);
|
||||||
|
|
||||||
|
$promptRef = $this->registry->getPrompt('test_prompt');
|
||||||
|
$this->assertEquals($completionProviders, $promptRef->completionProviders);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testResourceTemplateMatchingPrefersMoreSpecificMatches(): void
|
public function testRegisterPromptIgnoresDiscoveredWhenManualExists(): void
|
||||||
{
|
{
|
||||||
$specificTemplate = $this->createValidResourceTemplate('test://users/{userId}/profile');
|
$manualPrompt = $this->createValidPrompt('test_prompt');
|
||||||
$genericTemplate = $this->createValidResourceTemplate('test://users/{userId}');
|
$discoveredPrompt = $this->createValidPrompt('test_prompt');
|
||||||
|
|
||||||
$this->registry->registerResourceTemplate($genericTemplate, fn () => 'generic');
|
$this->registry->registerPrompt($manualPrompt, fn () => 'manual', [], true);
|
||||||
$this->registry->registerResourceTemplate($specificTemplate, fn () => 'specific');
|
|
||||||
|
|
||||||
// Should match the more specific template first
|
$this->logger
|
||||||
$resourceRef = $this->registry->getResource('test://users/123/profile');
|
->expects($this->once())
|
||||||
$this->assertInstanceOf(ResourceTemplateReference::class, $resourceRef);
|
->method('debug')
|
||||||
$this->assertEquals('test://users/{userId}/profile', $resourceRef->resourceTemplate->uriTemplate);
|
->with('Ignoring discovered prompt "test_prompt" as it conflicts with a manually registered one.');
|
||||||
|
|
||||||
|
$this->registry->registerPrompt($discoveredPrompt, fn () => 'discovered');
|
||||||
|
|
||||||
|
$promptRef = $this->registry->getPrompt('test_prompt');
|
||||||
|
$this->assertTrue($promptRef->isManual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetPromptThrowsExceptionForUnregisteredPrompt(): void
|
||||||
|
{
|
||||||
|
$this->expectException(PromptNotFoundException::class);
|
||||||
|
$this->expectExceptionMessage('Prompt not found: "non_existent_prompt".');
|
||||||
|
|
||||||
|
$this->registry->getPrompt('non_existent_prompt');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClearRemovesOnlyDiscoveredElements(): void
|
||||||
|
{
|
||||||
|
$manualTool = $this->createValidTool('manual_tool');
|
||||||
|
$discoveredTool = $this->createValidTool('discovered_tool');
|
||||||
|
$manualResource = $this->createValidResource('test://manual');
|
||||||
|
$discoveredResource = $this->createValidResource('test://discovered');
|
||||||
|
$manualPrompt = $this->createValidPrompt('manual_prompt');
|
||||||
|
$discoveredPrompt = $this->createValidPrompt('discovered_prompt');
|
||||||
|
$manualTemplate = $this->createValidResourceTemplate('manual://{id}');
|
||||||
|
$discoveredTemplate = $this->createValidResourceTemplate('discovered://{id}');
|
||||||
|
|
||||||
|
$this->registry->registerTool($manualTool, fn () => 'manual', true);
|
||||||
|
$this->registry->registerTool($discoveredTool, fn () => 'discovered');
|
||||||
|
$this->registry->registerResource($manualResource, fn () => 'manual', true);
|
||||||
|
$this->registry->registerResource($discoveredResource, fn () => 'discovered');
|
||||||
|
$this->registry->registerPrompt($manualPrompt, fn () => [], [], true);
|
||||||
|
$this->registry->registerPrompt($discoveredPrompt, fn () => []);
|
||||||
|
$this->registry->registerResourceTemplate($manualTemplate, fn () => 'manual', [], true);
|
||||||
|
$this->registry->registerResourceTemplate($discoveredTemplate, fn () => 'discovered');
|
||||||
|
|
||||||
|
// Test that all elements exist
|
||||||
|
$this->registry->getTool('manual_tool');
|
||||||
|
$this->registry->getResource('test://manual');
|
||||||
|
$this->registry->getPrompt('manual_prompt');
|
||||||
|
$this->registry->getResourceTemplate('manual://{id}');
|
||||||
|
$this->registry->getTool('discovered_tool');
|
||||||
|
$this->registry->getResource('test://discovered');
|
||||||
|
$this->registry->getPrompt('discovered_prompt');
|
||||||
|
$this->registry->getResourceTemplate('discovered://{id}');
|
||||||
|
|
||||||
|
$this->registry->clear();
|
||||||
|
|
||||||
|
// Manual elements should still exist
|
||||||
|
$this->registry->getTool('manual_tool');
|
||||||
|
$this->registry->getResource('test://manual');
|
||||||
|
$this->registry->getPrompt('manual_prompt');
|
||||||
|
$this->registry->getResourceTemplate('manual://{id}');
|
||||||
|
|
||||||
|
// Test that all discovered elements throw exceptions
|
||||||
|
$this->expectException(ToolNotFoundException::class);
|
||||||
|
$this->registry->getTool('discovered_tool');
|
||||||
|
|
||||||
|
$this->expectException(ResourceNotFoundException::class);
|
||||||
|
$this->registry->getResource('test://discovered');
|
||||||
|
|
||||||
|
$this->expectException(PromptNotFoundException::class);
|
||||||
|
$this->registry->getPrompt('discovered_prompt');
|
||||||
|
|
||||||
|
$this->expectException(ResourceNotFoundException::class);
|
||||||
|
$this->registry->getResourceTemplate('discovered://{id}');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClearLogsNothingWhenNoDiscoveredElements(): void
|
||||||
|
{
|
||||||
|
$manualTool = $this->createValidTool('manual_tool');
|
||||||
|
$this->registry->registerTool($manualTool, fn () => 'manual', true);
|
||||||
|
|
||||||
|
$this->logger
|
||||||
|
->expects($this->never())
|
||||||
|
->method('debug');
|
||||||
|
|
||||||
|
$this->registry->clear();
|
||||||
|
|
||||||
|
$this->registry->getTool('manual_tool');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRegisterToolHandlesStringHandler(): void
|
||||||
|
{
|
||||||
|
$tool = $this->createValidTool('test_tool');
|
||||||
|
$handler = 'TestClass::testMethod';
|
||||||
|
|
||||||
|
$this->registry->registerTool($tool, $handler);
|
||||||
|
|
||||||
|
$toolRef = $this->registry->getTool('test_tool');
|
||||||
|
$this->assertEquals($handler, $toolRef->handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRegisterToolHandlesArrayHandler(): void
|
||||||
|
{
|
||||||
|
$tool = $this->createValidTool('test_tool');
|
||||||
|
$handler = ['TestClass', 'testMethod'];
|
||||||
|
|
||||||
|
$this->registry->registerTool($tool, $handler);
|
||||||
|
|
||||||
|
$toolRef = $this->registry->getTool('test_tool');
|
||||||
|
$this->assertEquals($handler, $toolRef->handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRegisterResourceHandlesCallableHandler(): void
|
||||||
|
{
|
||||||
|
$resource = $this->createValidResource('test://resource');
|
||||||
|
$handler = fn () => 'content';
|
||||||
|
|
||||||
|
$this->registry->registerResource($resource, $handler);
|
||||||
|
|
||||||
|
$resourceRef = $this->registry->getResource('test://resource');
|
||||||
|
$this->assertEquals($handler, $resourceRef->handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMultipleRegistrationsOfSameElementWithSameType(): void
|
||||||
|
{
|
||||||
|
$tool1 = $this->createValidTool('test_tool');
|
||||||
|
$tool2 = $this->createValidTool('test_tool');
|
||||||
|
|
||||||
|
$this->registry->registerTool($tool1, fn () => 'first');
|
||||||
|
$this->registry->registerTool($tool2, fn () => 'second');
|
||||||
|
|
||||||
|
// Second registration should override the first
|
||||||
|
$toolRef = $this->registry->getTool('test_tool');
|
||||||
|
$this->assertEquals('second', ($toolRef->handler)());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createValidTool(string $name): Tool
|
private function createValidTool(string $name): Tool
|
||||||
|
|
@ -291,7 +539,7 @@ class RegistryProviderTest extends TestCase
|
||||||
'required' => null,
|
'required' => null,
|
||||||
],
|
],
|
||||||
description: "Test tool: {$name}",
|
description: "Test tool: {$name}",
|
||||||
annotations: null
|
annotations: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,7 +549,7 @@ class RegistryProviderTest extends TestCase
|
||||||
uri: $uri,
|
uri: $uri,
|
||||||
name: 'test_resource',
|
name: 'test_resource',
|
||||||
description: 'Test resource',
|
description: 'Test resource',
|
||||||
mimeType: 'text/plain'
|
mimeType: 'text/plain',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -311,7 +559,7 @@ class RegistryProviderTest extends TestCase
|
||||||
uriTemplate: $uriTemplate,
|
uriTemplate: $uriTemplate,
|
||||||
name: 'test_template',
|
name: 'test_template',
|
||||||
description: 'Test resource template',
|
description: 'Test resource template',
|
||||||
mimeType: 'text/plain'
|
mimeType: 'text/plain',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -320,7 +568,7 @@ class RegistryProviderTest extends TestCase
|
||||||
return new Prompt(
|
return new Prompt(
|
||||||
name: $name,
|
name: $name,
|
||||||
description: "Test prompt: {$name}",
|
description: "Test prompt: {$name}",
|
||||||
arguments: []
|
arguments: [],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -12,8 +12,8 @@
|
||||||
namespace Mcp\Tests\Unit\Server\Handler\Request;
|
namespace Mcp\Tests\Unit\Server\Handler\Request;
|
||||||
|
|
||||||
use Mcp\Capability\Registry\ReferenceHandlerInterface;
|
use Mcp\Capability\Registry\ReferenceHandlerInterface;
|
||||||
use Mcp\Capability\Registry\ReferenceProviderInterface;
|
|
||||||
use Mcp\Capability\Registry\ToolReference;
|
use Mcp\Capability\Registry\ToolReference;
|
||||||
|
use Mcp\Capability\RegistryInterface;
|
||||||
use Mcp\Exception\ToolCallException;
|
use Mcp\Exception\ToolCallException;
|
||||||
use Mcp\Exception\ToolNotFoundException;
|
use Mcp\Exception\ToolNotFoundException;
|
||||||
use Mcp\Schema\Content\TextContent;
|
use Mcp\Schema\Content\TextContent;
|
||||||
|
|
@ -30,20 +30,20 @@ use Psr\Log\LoggerInterface;
|
||||||
class CallToolHandlerTest extends TestCase
|
class CallToolHandlerTest extends TestCase
|
||||||
{
|
{
|
||||||
private CallToolHandler $handler;
|
private CallToolHandler $handler;
|
||||||
private ReferenceProviderInterface&MockObject $referenceProvider;
|
private RegistryInterface&MockObject $registry;
|
||||||
private ReferenceHandlerInterface&MockObject $referenceHandler;
|
private ReferenceHandlerInterface&MockObject $referenceHandler;
|
||||||
private LoggerInterface&MockObject $logger;
|
private LoggerInterface&MockObject $logger;
|
||||||
private SessionInterface&MockObject $session;
|
private SessionInterface&MockObject $session;
|
||||||
|
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
$this->referenceProvider = $this->createMock(ReferenceProviderInterface::class);
|
$this->registry = $this->createMock(RegistryInterface::class);
|
||||||
$this->referenceHandler = $this->createMock(ReferenceHandlerInterface::class);
|
$this->referenceHandler = $this->createMock(ReferenceHandlerInterface::class);
|
||||||
$this->logger = $this->createMock(LoggerInterface::class);
|
$this->logger = $this->createMock(LoggerInterface::class);
|
||||||
$this->session = $this->createMock(SessionInterface::class);
|
$this->session = $this->createMock(SessionInterface::class);
|
||||||
|
|
||||||
$this->handler = new CallToolHandler(
|
$this->handler = new CallToolHandler(
|
||||||
$this->referenceProvider,
|
$this->registry,
|
||||||
$this->referenceHandler,
|
$this->referenceHandler,
|
||||||
$this->logger,
|
$this->logger,
|
||||||
);
|
);
|
||||||
|
|
@ -62,7 +62,7 @@ class CallToolHandlerTest extends TestCase
|
||||||
$toolReference = $this->createMock(ToolReference::class);
|
$toolReference = $this->createMock(ToolReference::class);
|
||||||
$expectedResult = new CallToolResult([new TextContent('Hello, John!')]);
|
$expectedResult = new CallToolResult([new TextContent('Hello, John!')]);
|
||||||
|
|
||||||
$this->referenceProvider
|
$this->registry
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getTool')
|
->method('getTool')
|
||||||
->with('greet_user')
|
->with('greet_user')
|
||||||
|
|
@ -95,7 +95,7 @@ class CallToolHandlerTest extends TestCase
|
||||||
$toolReference = $this->createMock(ToolReference::class);
|
$toolReference = $this->createMock(ToolReference::class);
|
||||||
$expectedResult = new CallToolResult([new TextContent('Simple result')]);
|
$expectedResult = new CallToolResult([new TextContent('Simple result')]);
|
||||||
|
|
||||||
$this->referenceProvider
|
$this->registry
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getTool')
|
->method('getTool')
|
||||||
->with('simple_tool')
|
->with('simple_tool')
|
||||||
|
|
@ -132,7 +132,7 @@ class CallToolHandlerTest extends TestCase
|
||||||
$toolReference = $this->createMock(ToolReference::class);
|
$toolReference = $this->createMock(ToolReference::class);
|
||||||
$expectedResult = new CallToolResult([new TextContent('Complex result')]);
|
$expectedResult = new CallToolResult([new TextContent('Complex result')]);
|
||||||
|
|
||||||
$this->referenceProvider
|
$this->registry
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getTool')
|
->method('getTool')
|
||||||
->with('complex_tool')
|
->with('complex_tool')
|
||||||
|
|
@ -160,7 +160,7 @@ class CallToolHandlerTest extends TestCase
|
||||||
{
|
{
|
||||||
$request = $this->createCallToolRequest('nonexistent_tool', ['param' => 'value']);
|
$request = $this->createCallToolRequest('nonexistent_tool', ['param' => 'value']);
|
||||||
|
|
||||||
$this->referenceProvider
|
$this->registry
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getTool')
|
->method('getTool')
|
||||||
->with('nonexistent_tool')
|
->with('nonexistent_tool')
|
||||||
|
|
@ -183,7 +183,7 @@ class CallToolHandlerTest extends TestCase
|
||||||
$exception = new ToolCallException('Tool execution failed');
|
$exception = new ToolCallException('Tool execution failed');
|
||||||
|
|
||||||
$toolReference = $this->createMock(ToolReference::class);
|
$toolReference = $this->createMock(ToolReference::class);
|
||||||
$this->referenceProvider
|
$this->registry
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getTool')
|
->method('getTool')
|
||||||
->with('failing_tool')
|
->with('failing_tool')
|
||||||
|
|
@ -218,7 +218,7 @@ class CallToolHandlerTest extends TestCase
|
||||||
$expectedResult = new CallToolResult([]);
|
$expectedResult = new CallToolResult([]);
|
||||||
|
|
||||||
$toolReference = $this->createMock(ToolReference::class);
|
$toolReference = $this->createMock(ToolReference::class);
|
||||||
$this->referenceProvider
|
$this->registry
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getTool')
|
->method('getTool')
|
||||||
->with('null_tool')
|
->with('null_tool')
|
||||||
|
|
@ -244,7 +244,7 @@ class CallToolHandlerTest extends TestCase
|
||||||
|
|
||||||
public function testConstructorWithDefaultLogger(): void
|
public function testConstructorWithDefaultLogger(): void
|
||||||
{
|
{
|
||||||
$handler = new CallToolHandler($this->referenceProvider, $this->referenceHandler);
|
$handler = new CallToolHandler($this->registry, $this->referenceHandler);
|
||||||
|
|
||||||
$this->assertInstanceOf(CallToolHandler::class, $handler);
|
$this->assertInstanceOf(CallToolHandler::class, $handler);
|
||||||
}
|
}
|
||||||
|
|
@ -255,7 +255,7 @@ class CallToolHandlerTest extends TestCase
|
||||||
$exception = new ToolCallException('Custom error message');
|
$exception = new ToolCallException('Custom error message');
|
||||||
|
|
||||||
$toolReference = $this->createMock(ToolReference::class);
|
$toolReference = $this->createMock(ToolReference::class);
|
||||||
$this->referenceProvider
|
$this->registry
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getTool')
|
->method('getTool')
|
||||||
->with('test_tool')
|
->with('test_tool')
|
||||||
|
|
@ -298,7 +298,7 @@ class CallToolHandlerTest extends TestCase
|
||||||
$exception = new \RuntimeException('Internal database connection failed');
|
$exception = new \RuntimeException('Internal database connection failed');
|
||||||
|
|
||||||
$toolReference = $this->createMock(ToolReference::class);
|
$toolReference = $this->createMock(ToolReference::class);
|
||||||
$this->referenceProvider
|
$this->registry
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getTool')
|
->method('getTool')
|
||||||
->with('failing_tool')
|
->with('failing_tool')
|
||||||
|
|
@ -325,7 +325,7 @@ class CallToolHandlerTest extends TestCase
|
||||||
$expectedResult = new CallToolResult([new TextContent('Special tool result')]);
|
$expectedResult = new CallToolResult([new TextContent('Special tool result')]);
|
||||||
|
|
||||||
$toolReference = $this->createMock(ToolReference::class);
|
$toolReference = $this->createMock(ToolReference::class);
|
||||||
$this->referenceProvider
|
$this->registry
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getTool')
|
->method('getTool')
|
||||||
->with('tool-with_special.chars')
|
->with('tool-with_special.chars')
|
||||||
|
|
@ -360,7 +360,7 @@ class CallToolHandlerTest extends TestCase
|
||||||
$expectedResult = new CallToolResult([new TextContent('Unicode handled')]);
|
$expectedResult = new CallToolResult([new TextContent('Unicode handled')]);
|
||||||
|
|
||||||
$toolReference = $this->createMock(ToolReference::class);
|
$toolReference = $this->createMock(ToolReference::class);
|
||||||
$this->referenceProvider
|
$this->registry
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getTool')
|
->method('getTool')
|
||||||
->with('unicode_tool')
|
->with('unicode_tool')
|
||||||
|
|
@ -390,7 +390,7 @@ class CallToolHandlerTest extends TestCase
|
||||||
$toolReference = $this->createMock(ToolReference::class);
|
$toolReference = $this->createMock(ToolReference::class);
|
||||||
$structuredResult = new CallToolResult([new TextContent('Rendered results')], false, ['result' => 'Rendered results']);
|
$structuredResult = new CallToolResult([new TextContent('Rendered results')], false, ['result' => 'Rendered results']);
|
||||||
|
|
||||||
$this->referenceProvider
|
$this->registry
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getTool')
|
->method('getTool')
|
||||||
->with('structured_tool')
|
->with('structured_tool')
|
||||||
|
|
@ -419,7 +419,7 @@ class CallToolHandlerTest extends TestCase
|
||||||
$toolReference = $this->createMock(ToolReference::class);
|
$toolReference = $this->createMock(ToolReference::class);
|
||||||
$callToolResult = new CallToolResult([new TextContent('Error result')], true);
|
$callToolResult = new CallToolResult([new TextContent('Error result')], true);
|
||||||
|
|
||||||
$this->referenceProvider
|
$this->registry
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getTool')
|
->method('getTool')
|
||||||
->with('result_tool')
|
->with('result_tool')
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue