-
Notifications
You must be signed in to change notification settings - Fork 123
[Server] Rework registry to combine provider and registry interface, move capabilities out and enable Registry injection in Builder #150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -13,8 +13,6 @@ | |||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| use Mcp\Capability\Discovery\DiscoveryState; | ||||||||||||||||||||||||||||||||
| use Mcp\Capability\Registry\PromptReference; | ||||||||||||||||||||||||||||||||
| use Mcp\Capability\Registry\ReferenceProviderInterface; | ||||||||||||||||||||||||||||||||
| use Mcp\Capability\Registry\ReferenceRegistryInterface; | ||||||||||||||||||||||||||||||||
| use Mcp\Capability\Registry\ResourceReference; | ||||||||||||||||||||||||||||||||
| use Mcp\Capability\Registry\ResourceTemplateReference; | ||||||||||||||||||||||||||||||||
| use Mcp\Capability\Registry\ToolReference; | ||||||||||||||||||||||||||||||||
|
|
@@ -31,21 +29,17 @@ | |||||||||||||||||||||||||||||||
| use Mcp\Schema\Prompt; | ||||||||||||||||||||||||||||||||
| use Mcp\Schema\Resource; | ||||||||||||||||||||||||||||||||
| use Mcp\Schema\ResourceTemplate; | ||||||||||||||||||||||||||||||||
| use Mcp\Schema\ServerCapabilities; | ||||||||||||||||||||||||||||||||
| use Mcp\Schema\Tool; | ||||||||||||||||||||||||||||||||
| use Psr\EventDispatcher\EventDispatcherInterface; | ||||||||||||||||||||||||||||||||
| use Psr\Log\LoggerInterface; | ||||||||||||||||||||||||||||||||
| use Psr\Log\NullLogger; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||
| * 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 Pavel Buchnev <butschster@gmail.com> | ||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||
| final class Registry implements ReferenceProviderInterface, ReferenceRegistryInterface | ||||||||||||||||||||||||||||||||
| final class Registry implements RegistryInterface | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||
| * @var array<string, ToolReference> | ||||||||||||||||||||||||||||||||
|
|
@@ -67,34 +61,13 @@ final class Registry implements ReferenceProviderInterface, ReferenceRegistryInt | |||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||
| private array $resourceTemplates = []; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| private ServerCapabilities $serverCapabilities; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public function __construct( | ||||||||||||||||||||||||||||||||
| private readonly ?EventDispatcherInterface $eventDispatcher = null, | ||||||||||||||||||||||||||||||||
| private readonly LoggerInterface $logger = new NullLogger(), | ||||||||||||||||||||||||||||||||
| private readonly NameValidator $nameValidator = new NameValidator(), | ||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| $toolName = $tool->name; | ||||||||||||||||||||||||||||||||
|
|
@@ -220,41 +193,9 @@ public function clear(): void | |||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public function getTool(string $name): ToolReference | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| return $this->tools[$name] ?? throw new ToolNotFoundException($name); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||
| public function hasTools(): bool | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| return $this->resourceTemplates[$uriTemplate] ?? throw new ResourceNotFoundException($uriTemplate); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public function getPrompt(string $name): PromptReference | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| return $this->prompts[$name] ?? throw new PromptNotFoundException($name); | ||||||||||||||||||||||||||||||||
| return [] !== $this->tools; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public function getTools(?int $limit = null, ?string $cursor = null): Page | ||||||||||||||||||||||||||||||||
|
|
@@ -279,6 +220,16 @@ public function getTools(?int $limit = null, ?string $cursor = null): Page | |||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| $resources = []; | ||||||||||||||||||||||||||||||||
|
|
@@ -301,26 +252,31 @@ public function getResources(?int $limit = null, ?string $cursor = null): Page | |||||||||||||||||||||||||||||||
| return new Page($paginatedResources, $nextCursor); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public function getPrompts(?int $limit = null, ?string $cursor = null): Page | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| $prompts = []; | ||||||||||||||||||||||||||||||||
| foreach ($this->prompts as $promptReference) { | ||||||||||||||||||||||||||||||||
| $prompts[$promptReference->prompt->name] = $promptReference->prompt; | ||||||||||||||||||||||||||||||||
| public function getResource( | ||||||||||||||||||||||||||||||||
| string $uri, | ||||||||||||||||||||||||||||||||
| bool $includeTemplates = true, | ||||||||||||||||||||||||||||||||
| ): ResourceReference|ResourceTemplateReference { | ||||||||||||||||||||||||||||||||
| $registration = $this->resources[$uri] ?? null; | ||||||||||||||||||||||||||||||||
| if ($registration) { | ||||||||||||||||||||||||||||||||
| return $registration; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (null === $limit) { | ||||||||||||||||||||||||||||||||
| return new Page($prompts, null); | ||||||||||||||||||||||||||||||||
| if ($includeTemplates) { | ||||||||||||||||||||||||||||||||
| foreach ($this->resourceTemplates as $template) { | ||||||||||||||||||||||||||||||||
| if ($template->matches($uri)) { | ||||||||||||||||||||||||||||||||
| return $template; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Comment on lines
+264
to
+269
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| $paginatedPrompts = $this->paginateResults($prompts, $limit, $cursor); | ||||||||||||||||||||||||||||||||
| $this->logger->debug('No resource matched URI.', ['uri' => $uri]); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| $nextCursor = $this->calculateNextCursor( | ||||||||||||||||||||||||||||||||
| \count($prompts), | ||||||||||||||||||||||||||||||||
| $cursor, | ||||||||||||||||||||||||||||||||
| $limit | ||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||
| throw new ResourceNotFoundException($uri); | ||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. weird to log and to throw ? |
||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return new Page($paginatedPrompts, $nextCursor); | ||||||||||||||||||||||||||||||||
| public function hasResourceTemplates(): bool | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| return [] !== $this->resourceTemplates; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public function getResourceTemplates(?int $limit = null, ?string $cursor = null): Page | ||||||||||||||||||||||||||||||||
|
|
@@ -345,12 +301,41 @@ public function getResourceTemplates(?int $limit = null, ?string $cursor = null) | |||||||||||||||||||||||||||||||
| return new Page($paginatedTemplates, $nextCursor); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public function hasElements(): bool | ||||||||||||||||||||||||||||||||
| public function getResourceTemplate(string $uriTemplate): ResourceTemplateReference | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| return !empty($this->tools) | ||||||||||||||||||||||||||||||||
| || !empty($this->resources) | ||||||||||||||||||||||||||||||||
| || !empty($this->prompts) | ||||||||||||||||||||||||||||||||
| || !empty($this->resourceTemplates); | ||||||||||||||||||||||||||||||||
| return $this->resourceTemplates[$uriTemplate] ?? throw new ResourceNotFoundException($uriTemplate); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| 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 @@ private function paginateResults(array $items, int $limit, ?string $cursor = nul | |||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return array_values(\array_slice($items, $offset, $limit)); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public function setServerCapabilities(ServerCapabilities $serverCapabilities): void | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| $this->serverCapabilities = $serverCapabilities; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it'd be nice for reviewing to keep the previous order of methods :D.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea fair, i think we're currently a bit rough with PR scopes 😬