Skip to content

Commit a978c57

Browse files
committed
refactor: Separate Registry concerns following SOLID principles [WIP]
- Extract ReferenceProvider and ReferenceRegistryInterface interfaces - Create DefaultToolExecutor with ReferenceHandlerInterface - Remove execution responsibility from Registry class - Enable custom handler and executor implementations
1 parent 4b66732 commit a978c57

File tree

6 files changed

+326
-81
lines changed

6 files changed

+326
-81
lines changed

src/Capability/Registry.php

Lines changed: 57 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,15 @@
1313

1414
use Mcp\Capability\Registry\ElementReference;
1515
use Mcp\Capability\Registry\PromptReference;
16-
use Mcp\Capability\Registry\ReferenceHandler;
16+
use Mcp\Capability\Registry\ReferenceProviderInterface;
17+
use Mcp\Capability\Registry\ReferenceRegistryInterface;
1718
use Mcp\Capability\Registry\ResourceReference;
1819
use Mcp\Capability\Registry\ResourceTemplateReference;
1920
use Mcp\Capability\Registry\ToolReference;
2021
use Mcp\Event\PromptListChangedEvent;
2122
use Mcp\Event\ResourceListChangedEvent;
2223
use Mcp\Event\ResourceTemplateListChangedEvent;
2324
use Mcp\Event\ToolListChangedEvent;
24-
use Mcp\Exception\InvalidArgumentException;
25-
use Mcp\Schema\Content\PromptMessage;
26-
use Mcp\Schema\Content\ResourceContents;
2725
use Mcp\Schema\Prompt;
2826
use Mcp\Schema\Resource;
2927
use Mcp\Schema\ResourceTemplate;
@@ -36,9 +34,14 @@
3634
/**
3735
* @phpstan-import-type CallableArray from ElementReference
3836
*
37+
* Registry implementation that manages MCP element registration and access.
38+
* Implements both ReferenceProvider (for access) and ReferenceRegistry (for registration)
39+
* following the Interface Segregation Principle.
40+
*
3941
* @author Kyrian Obikwelu <[email protected]>
42+
* @author Pavel Buchnev <[email protected]>
4043
*/
41-
class Registry
44+
final class Registry implements ReferenceProviderInterface, ReferenceRegistryInterface
4245
{
4346
/**
4447
* @var array<string, ToolReference>
@@ -61,11 +64,11 @@ class Registry
6164
private array $resourceTemplates = [];
6265

6366
public function __construct(
64-
private readonly ReferenceHandler $referenceHandler = new ReferenceHandler(),
6567
private readonly ?EventDispatcherInterface $eventDispatcher = null,
6668
private readonly LoggerInterface $logger = new NullLogger(),
67-
) {
68-
}
69+
) {}
70+
71+
// === ReferenceRegistry interface methods ===
6972

7073
public function getCapabilities(): ServerCapabilities
7174
{
@@ -74,14 +77,14 @@ public function getCapabilities(): ServerCapabilities
7477
}
7578

7679
return new ServerCapabilities(
77-
tools: true, // [] !== $this->tools,
80+
tools: [] !== $this->tools,
7881
toolsListChanged: $this->eventDispatcher instanceof EventDispatcherInterface,
7982
resources: [] !== $this->resources || [] !== $this->resourceTemplates,
8083
resourcesSubscribe: false,
8184
resourcesListChanged: $this->eventDispatcher instanceof EventDispatcherInterface,
8285
prompts: [] !== $this->prompts,
8386
promptsListChanged: $this->eventDispatcher instanceof EventDispatcherInterface,
84-
logging: false, // true,
87+
logging: false,
8588
completions: true,
8689
);
8790
}
@@ -95,7 +98,9 @@ public function registerTool(Tool $tool, callable|array|string $handler, bool $i
9598
$existing = $this->tools[$toolName] ?? null;
9699

97100
if ($existing && !$isManual && $existing->isManual) {
98-
$this->logger->debug("Ignoring discovered tool '{$toolName}' as it conflicts with a manually registered one.");
101+
$this->logger->debug(
102+
"Ignoring discovered tool '{$toolName}' as it conflicts with a manually registered one.",
103+
);
99104

100105
return;
101106
}
@@ -114,7 +119,9 @@ public function registerResource(Resource $resource, callable|array|string $hand
114119
$existing = $this->resources[$uri] ?? null;
115120

116121
if ($existing && !$isManual && $existing->isManual) {
117-
$this->logger->debug("Ignoring discovered resource '{$uri}' as it conflicts with a manually registered one.");
122+
$this->logger->debug(
123+
"Ignoring discovered resource '{$uri}' as it conflicts with a manually registered one.",
124+
);
118125

119126
return;
120127
}
@@ -125,7 +132,7 @@ public function registerResource(Resource $resource, callable|array|string $hand
125132
}
126133

127134
/**
128-
* @param callable|CallableArray|string $handler
135+
* @param callable|CallableArray|string $handler
129136
* @param array<string, class-string|object> $completionProviders
130137
*/
131138
public function registerResourceTemplate(
@@ -138,18 +145,25 @@ public function registerResourceTemplate(
138145
$existing = $this->resourceTemplates[$uriTemplate] ?? null;
139146

140147
if ($existing && !$isManual && $existing->isManual) {
141-
$this->logger->debug("Ignoring discovered template '{$uriTemplate}' as it conflicts with a manually registered one.");
148+
$this->logger->debug(
149+
"Ignoring discovered template '{$uriTemplate}' as it conflicts with a manually registered one.",
150+
);
142151

143152
return;
144153
}
145154

146-
$this->resourceTemplates[$uriTemplate] = new ResourceTemplateReference($template, $handler, $isManual, $completionProviders);
155+
$this->resourceTemplates[$uriTemplate] = new ResourceTemplateReference(
156+
$template,
157+
$handler,
158+
$isManual,
159+
$completionProviders,
160+
);
147161

148162
$this->eventDispatcher?->dispatch(new ResourceTemplateListChangedEvent());
149163
}
150164

151165
/**
152-
* @param callable|CallableArray|string $handler
166+
* @param callable|CallableArray|string $handler
153167
* @param array<string, class-string|object> $completionProviders
154168
*/
155169
public function registerPrompt(
@@ -162,7 +176,9 @@ public function registerPrompt(
162176
$existing = $this->prompts[$promptName] ?? null;
163177

164178
if ($existing && !$isManual && $existing->isManual) {
165-
$this->logger->debug("Ignoring discovered prompt '{$promptName}' as it conflicts with a manually registered one.");
179+
$this->logger->debug(
180+
"Ignoring discovered prompt '{$promptName}' as it conflicts with a manually registered one.",
181+
);
166182

167183
return;
168184
}
@@ -172,17 +188,6 @@ public function registerPrompt(
172188
$this->eventDispatcher?->dispatch(new PromptListChangedEvent());
173189
}
174190

175-
/**
176-
* Checks if any elements (manual or discovered) are currently registered.
177-
*/
178-
public function hasElements(): bool
179-
{
180-
return !empty($this->tools)
181-
|| !empty($this->resources)
182-
|| !empty($this->prompts)
183-
|| !empty($this->resourceTemplates);
184-
}
185-
186191
/**
187192
* Clear discovered elements from registry.
188193
*/
@@ -220,43 +225,17 @@ public function clear(): void
220225
}
221226
}
222227

223-
public function handleCallTool(string $name, array $arguments): array
224-
{
225-
$reference = $this->getTool($name);
226-
227-
if (null === $reference) {
228-
throw new InvalidArgumentException(\sprintf('Tool "%s" is not registered.', $name));
229-
}
230-
231-
return $reference->formatResult(
232-
$this->referenceHandler->handle($reference, $arguments)
233-
);
234-
}
228+
// === ReferenceProvider interface methods ===
235229

236230
public function getTool(string $name): ?ToolReference
237231
{
238232
return $this->tools[$name] ?? null;
239233
}
240234

241-
/**
242-
* @return ResourceContents[]
243-
*/
244-
public function handleReadResource(string $uri): array
245-
{
246-
$reference = $this->getResource($uri);
247-
248-
if (null === $reference) {
249-
throw new InvalidArgumentException(\sprintf('Resource "%s" is not registered.', $uri));
250-
}
251-
252-
return $reference->formatResult(
253-
$this->referenceHandler->handle($reference, ['uri' => $uri]),
254-
$uri,
255-
);
256-
}
257-
258-
public function getResource(string $uri, bool $includeTemplates = true): ResourceReference|ResourceTemplateReference|null
259-
{
235+
public function getResource(
236+
string $uri,
237+
bool $includeTemplates = true,
238+
): ResourceReference|ResourceTemplateReference|null {
260239
$registration = $this->resources[$uri] ?? null;
261240
if ($registration) {
262241
return $registration;
@@ -282,22 +261,6 @@ public function getResourceTemplate(string $uriTemplate): ?ResourceTemplateRefer
282261
return $this->resourceTemplates[$uriTemplate] ?? null;
283262
}
284263

285-
/**
286-
* @return PromptMessage[]
287-
*/
288-
public function handleGetPrompt(string $name, ?array $arguments): array
289-
{
290-
$reference = $this->getPrompt($name);
291-
292-
if (null === $reference) {
293-
throw new InvalidArgumentException(\sprintf('Prompt "%s" is not registered.', $name));
294-
}
295-
296-
return $reference->formatResult(
297-
$this->referenceHandler->handle($reference, $arguments)
298-
);
299-
}
300-
301264
public function getPrompt(string $name): ?PromptReference
302265
{
303266
return $this->prompts[$name] ?? null;
@@ -308,28 +271,42 @@ public function getPrompt(string $name): ?PromptReference
308271
*/
309272
public function getTools(): array
310273
{
311-
return array_map(fn (ToolReference $tool) => $tool->tool, $this->tools);
274+
return array_map(fn(ToolReference $tool) => $tool->tool, $this->tools);
312275
}
313276

314277
/**
315278
* @return array<string, resource>
316279
*/
317280
public function getResources(): array
318281
{
319-
return array_map(fn (ResourceReference $resource) => $resource->schema, $this->resources);
282+
return array_map(fn(ResourceReference $resource) => $resource->schema, $this->resources);
320283
}
321284

322285
/**
323286
* @return array<string, Prompt>
324287
*/
325288
public function getPrompts(): array
326289
{
327-
return array_map(fn (PromptReference $prompt) => $prompt->prompt, $this->prompts);
290+
return array_map(fn(PromptReference $prompt) => $prompt->prompt, $this->prompts);
328291
}
329292

330-
/** @return array<string, ResourceTemplate> */
293+
/**
294+
* @return array<string, ResourceTemplate>
295+
*/
331296
public function getResourceTemplates(): array
332297
{
333-
return array_map(fn ($template) => $template->resourceTemplate, $this->resourceTemplates);
298+
return array_map(fn(ResourceTemplateReference $template) => $template->resourceTemplate,
299+
$this->resourceTemplates);
300+
}
301+
302+
/**
303+
* Checks if any elements (manual or discovered) are currently registered.
304+
*/
305+
public function hasElements(): bool
306+
{
307+
return !empty($this->tools)
308+
|| !empty($this->resources)
309+
|| !empty($this->prompts)
310+
|| !empty($this->resourceTemplates);
334311
}
335312
}

src/Capability/Registry/ReferenceHandler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
/**
1919
* @author Kyrian Obikwelu <[email protected]>
2020
*/
21-
class ReferenceHandler
21+
final class ReferenceHandler implements ReferenceHandlerInterface
2222
{
2323
public function __construct(
2424
private readonly ?ContainerInterface $container = null,
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the official PHP MCP SDK.
5+
*
6+
* A collaboration between Symfony and the PHP Foundation.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Mcp\Capability\Registry;
13+
14+
/**
15+
* Interface for handling execution of MCP elements.
16+
* Allows custom implementations of element execution logic.
17+
*
18+
* @author Pavel Buchnev <[email protected]>
19+
*/
20+
interface ReferenceHandlerInterface
21+
{
22+
/**
23+
* Handles execution of an MCP element reference.
24+
*
25+
* @param ElementReference $reference the element reference to execute
26+
* @param array<string, mixed> $arguments arguments to pass to the handler
27+
*
28+
* @return mixed the result of the element execution
29+
*
30+
* @throws \Mcp\Exception\InvalidArgumentException if the handler is invalid
31+
* @throws \Mcp\Exception\RegistryException if execution fails
32+
*/
33+
public function handle(ElementReference $reference, array $arguments): mixed;
34+
}

0 commit comments

Comments
 (0)