From 6925f61cfb7213e709bef2b125649e1dcd8f2530 Mon Sep 17 00:00:00 2001
From: Tobias Nyholm <tobias.nyholm@gmail.com>
Date: Sun, 25 May 2025 22:48:59 +0200
Subject: [PATCH 1/4] cleanup

---
 examples/cli/index.php                       |  9 +++---
 examples/cli/src/Builder.php                 | 14 +++++++--
 examples/cli/src/ExamplePrompt.php           | 17 +++++++++--
 examples/cli/src/ExampleResource.php         | 10 +++++++
 examples/cli/src/ExampleTool.php             | 10 +++++--
 examples/cli/src/Manager/PromptManager.php   | 23 ++++-----------
 examples/cli/src/Manager/ResourceManager.php | 31 +++++++++-----------
 examples/cli/src/Manager/ToolManager.php     | 22 +++++---------
 8 files changed, 76 insertions(+), 60 deletions(-)

diff --git a/examples/cli/index.php b/examples/cli/index.php
index 13cdfc9..1c630d9 100644
--- a/examples/cli/index.php
+++ b/examples/cli/index.php
@@ -3,15 +3,16 @@
 require __DIR__.'/vendor/autoload.php';
 
 use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console as SymfonyConsole;
 
 $debug = (bool) ($_SERVER['DEBUG'] ?? false);
 
 // Setup input, output and logger
-$input = new Symfony\Component\Console\Input\ArgvInput($argv);
-$output = new Symfony\Component\Console\Output\ConsoleOutput($debug ? OutputInterface::VERBOSITY_VERY_VERBOSE : OutputInterface::VERBOSITY_NORMAL);
-$logger = new Symfony\Component\Console\Logger\ConsoleLogger($output);
+$input = new SymfonyConsole\Input\ArgvInput($argv);
+$output = new SymfonyConsole\Output\ConsoleOutput($debug ? OutputInterface::VERBOSITY_VERY_VERBOSE : OutputInterface::VERBOSITY_NORMAL);
+$logger = new SymfonyConsole\Logger\ConsoleLogger($output);
 
-// Configure the JsonRpcHandler
+// Configure the JsonRpcHandler and build the functionality
 $jsonRpcHandler = new PhpLlm\McpSdk\Server\JsonRpcHandler(
     new PhpLlm\McpSdk\Message\Factory(),
     App\Builder::buildRequestHandlers(),
diff --git a/examples/cli/src/Builder.php b/examples/cli/src/Builder.php
index 20126e8..b40664e 100644
--- a/examples/cli/src/Builder.php
+++ b/examples/cli/src/Builder.php
@@ -24,9 +24,17 @@ class Builder
      */
     public static function buildRequestHandlers(): array
     {
-        $promptManager = new PromptManager();
-        $resourceManager = new ResourceManager();
-        $toolManager = new ToolManager();
+        $promptManager = new PromptManager([
+            new ExamplePrompt(),
+        ]);
+
+        $resourceManager = new ResourceManager([
+            new ExampleResource(),
+        ]);
+
+        $toolManager = new ToolManager([
+            new ExampleTool(),
+        ]);
 
         return [
             new InitializeHandler(),
diff --git a/examples/cli/src/ExamplePrompt.php b/examples/cli/src/ExamplePrompt.php
index b46de9c..6a6b6ce 100644
--- a/examples/cli/src/ExamplePrompt.php
+++ b/examples/cli/src/ExamplePrompt.php
@@ -3,12 +3,23 @@
 namespace App;
 
 use PhpLlm\McpSdk\Capability\Prompt\MetadataInterface;
+use PhpLlm\McpSdk\Capability\Prompt\PromptGet;
+use PhpLlm\McpSdk\Capability\Prompt\PromptGetResult;
+use PhpLlm\McpSdk\Capability\Prompt\PromptGetResultMessages;
 
 class ExamplePrompt implements MetadataInterface
 {
-    public function __invoke(?string $firstName = null): string
+    public function __invoke(PromptGet $request): PromptGetResult
     {
-        return sprintf('Hello %s', $firstName ?? 'World');
+        $firstName = $request->arguments['first name'] ?? null;
+
+        return new PromptGetResult(
+            $this->getDescription(),
+            [new PromptGetResultMessages(
+                'user',
+                sprintf('Hello %s', $firstName ?? 'World')
+            )]
+        );
     }
 
     public function getName(): string
@@ -25,7 +36,7 @@ public function getArguments(): array
     {
         return [
             [
-                'name' => 'firstName',
+                'name' => 'first name',
                 'description' => 'The name of the person to greet',
                 'required' => false,
             ],
diff --git a/examples/cli/src/ExampleResource.php b/examples/cli/src/ExampleResource.php
index 28830c4..874c860 100644
--- a/examples/cli/src/ExampleResource.php
+++ b/examples/cli/src/ExampleResource.php
@@ -3,9 +3,19 @@
 namespace App;
 
 use PhpLlm\McpSdk\Capability\Resource\MetadataInterface;
+use PhpLlm\McpSdk\Capability\Resource\ResourceRead;
+use PhpLlm\McpSdk\Capability\Resource\ResourceReadResult;
 
 class ExampleResource implements MetadataInterface
 {
+    public function __invoke(ResourceRead $request): ResourceReadResult
+    {
+        return new ResourceReadResult(
+            'Content of '.$this->getName(),
+            $this->getUri(),
+        );
+    }
+
     public function getUri(): string
     {
         return 'file:///project/src/main.rs';
diff --git a/examples/cli/src/ExampleTool.php b/examples/cli/src/ExampleTool.php
index b31a417..66b966b 100644
--- a/examples/cli/src/ExampleTool.php
+++ b/examples/cli/src/ExampleTool.php
@@ -3,12 +3,18 @@
 namespace App;
 
 use PhpLlm\McpSdk\Capability\Tool\MetadataInterface;
+use PhpLlm\McpSdk\Capability\Tool\ToolCall;
+use PhpLlm\McpSdk\Capability\Tool\ToolCallResult;
 
 class ExampleTool implements MetadataInterface
 {
-    public function __invoke(string $format = 'Y-m-d H:i:s'): string
+    public function __invoke(ToolCall $toolCall): ToolCallResult
     {
-        return (new \DateTime('now', new \DateTimeZone('UTC')))->format($format);
+        $format = $toolCall->arguments['format'] ?? 'Y-m-d H:i:s';
+
+        return new ToolCallResult(
+            (new \DateTime('now', new \DateTimeZone('UTC')))->format($format)
+        );
     }
 
     public function getName(): string
diff --git a/examples/cli/src/Manager/PromptManager.php b/examples/cli/src/Manager/PromptManager.php
index 0c09159..fce427c 100644
--- a/examples/cli/src/Manager/PromptManager.php
+++ b/examples/cli/src/Manager/PromptManager.php
@@ -2,27 +2,22 @@
 
 namespace App\Manager;
 
-use App\ExamplePrompt;
 use PhpLlm\McpSdk\Capability\Prompt\CollectionInterface;
+use PhpLlm\McpSdk\Capability\Prompt\MetadataInterface;
 use PhpLlm\McpSdk\Capability\Prompt\PromptGet;
 use PhpLlm\McpSdk\Capability\Prompt\PromptGetResult;
-use PhpLlm\McpSdk\Capability\Prompt\PromptGetResultMessages;
 use PhpLlm\McpSdk\Capability\Prompt\PromptGetterInterface;
 use PhpLlm\McpSdk\Exception\PromptGetException;
 use PhpLlm\McpSdk\Exception\PromptNotFoundException;
 
 class PromptManager implements PromptGetterInterface, CollectionInterface
 {
-    /**
-     * @var mixed[]
-     */
-    private array $items;
-
     public function __construct(
+        /**
+         * @var (MetadataInterface | callable(PromptGet):PromptGetResult)[]
+         */
+        private array $items,
     ) {
-        $this->items = [
-            new ExamplePrompt(),
-        ];
     }
 
     public function getMetadata(): array
@@ -35,13 +30,7 @@ public function get(PromptGet $request): PromptGetResult
         foreach ($this->items as $item) {
             if ($request->name === $item->getName()) {
                 try {
-                    return new PromptGetResult(
-                        $item->getDescription(),
-                        [new PromptGetResultMessages(
-                            'user',
-                            $item->__invoke(...$request->arguments),
-                        )]
-                    );
+                    return $item($request);
                 } catch (\Throwable $e) {
                     throw new PromptGetException($request, $e);
                 }
diff --git a/examples/cli/src/Manager/ResourceManager.php b/examples/cli/src/Manager/ResourceManager.php
index 64aa0e9..b19951c 100644
--- a/examples/cli/src/Manager/ResourceManager.php
+++ b/examples/cli/src/Manager/ResourceManager.php
@@ -2,25 +2,23 @@
 
 namespace App\Manager;
 
-use App\ExampleResource;
 use PhpLlm\McpSdk\Capability\Resource\CollectionInterface;
+use PhpLlm\McpSdk\Capability\Resource\MetadataInterface;
 use PhpLlm\McpSdk\Capability\Resource\ResourceRead;
 use PhpLlm\McpSdk\Capability\Resource\ResourceReaderInterface;
 use PhpLlm\McpSdk\Capability\Resource\ResourceReadResult;
 use PhpLlm\McpSdk\Exception\ResourceNotFoundException;
+use PhpLlm\McpSdk\Exception\ResourceReadException;
 
 class ResourceManager implements CollectionInterface, ResourceReaderInterface
 {
-    /**
-     * @var mixed[]
-     */
-    private array $items;
-
     public function __construct(
+        /**
+         * TODO this is bad design. What if we want to add resource/exists, then this becomes invalid and we need a BC break
+         * @var (MetadataInterface | callable(ResourceRead):ResourceReadResult)[]
+         */
+        private array $items,
     ) {
-        $this->items = [
-            new ExampleResource(),
-        ];
     }
 
     public function getMetadata(): array
@@ -30,14 +28,13 @@ public function getMetadata(): array
 
     public function read(ResourceRead $request): ResourceReadResult
     {
-        foreach ($this->items as $resource) {
-            if ($request->uri === $resource->getUri()) {
-                // In a real implementation, you would read the resource from its URI.
-                // Here we just return a dummy string for demonstration purposes.
-                return new ResourceReadResult(
-                    'Content of '.$resource->getName(),
-                    $resource->getUri(),
-                );
+        foreach ($this->items as $item) {
+            if ($item instanceof ReadInterface && $request->uri === $item->getUri()) {
+                try {
+                    return $item($request);
+                } catch (\Throwable $e) {
+                    throw new ResourceReadException($request, $e);
+                }
             }
         }
 
diff --git a/examples/cli/src/Manager/ToolManager.php b/examples/cli/src/Manager/ToolManager.php
index 3b2188a..7ec4530 100644
--- a/examples/cli/src/Manager/ToolManager.php
+++ b/examples/cli/src/Manager/ToolManager.php
@@ -2,8 +2,8 @@
 
 namespace App\Manager;
 
-use App\ExampleTool;
 use PhpLlm\McpSdk\Capability\Tool\CollectionInterface;
+use PhpLlm\McpSdk\Capability\Tool\MetadataInterface;
 use PhpLlm\McpSdk\Capability\Tool\ToolCall;
 use PhpLlm\McpSdk\Capability\Tool\ToolCallResult;
 use PhpLlm\McpSdk\Capability\Tool\ToolExecutorInterface;
@@ -12,16 +12,12 @@
 
 class ToolManager implements ToolExecutorInterface, CollectionInterface
 {
-    /**
-     * @var mixed[]
-     */
-    private array $items;
-
     public function __construct(
+        /**
+         * @var (MetadataInterface | callable(ToolCall):ToolCallResult)[] $items
+         */
+        private array $items,
     ) {
-        $this->items = [
-            new ExampleTool(),
-        ];
     }
 
     public function getMetadata(): array
@@ -31,12 +27,10 @@ public function getMetadata(): array
 
     public function execute(ToolCall $toolCall): ToolCallResult
     {
-        foreach ($this->items as $tool) {
-            if ($toolCall->name === $tool->getName()) {
+        foreach ($this->items as $item) {
+            if ($toolCall->name === $item->getName()) {
                 try {
-                    return new ToolCallResult(
-                        $tool->__invoke(...$toolCall->arguments),
-                    );
+                    return $item($toolCall);
                 } catch (\Throwable $e) {
                     throw new ToolExecutionException($toolCall, $e);
                 }

From 6662d2f63ff73fe996cdc0535bd37c95d781451b Mon Sep 17 00:00:00 2001
From: Tobias Nyholm <tobias.nyholm@gmail.com>
Date: Sun, 25 May 2025 23:16:20 +0200
Subject: [PATCH 2/4] feat: add Prompt/Resource/Tool Chain

---
 examples/cli/index.php                        |  2 +-
 examples/cli/src/Builder.php                  | 12 +++++------
 examples/cli/src/ExamplePrompt.php            |  7 ++++---
 examples/cli/src/ExampleResource.php          |  5 +++--
 examples/cli/src/ExampleTool.php              |  7 ++++---
 src/Capability/Prompt/IdentifierInterface.php |  8 ++++++++
 src/Capability/Prompt/MetadataInterface.php   |  4 +---
 .../Prompt/PromptGetterInterface.php          |  2 +-
 .../Capability/PromptChain.php                | 19 +++++++++---------
 .../Resource/IdentifierInterface.php          |  8 ++++++++
 src/Capability/Resource/MetadataInterface.php |  4 +---
 .../Resource/ResourceReaderInterface.php      |  2 +-
 .../Capability/ResourceChain.php              | 20 +++++++++----------
 src/Capability/Tool/IdentifierInterface.php   |  8 ++++++++
 src/Capability/Tool/MetadataInterface.php     |  4 +---
 src/Capability/Tool/ToolExecutorInterface.php |  2 +-
 .../Capability/ToolChain.php                  | 19 +++++++++---------
 src/Server/RequestHandler/ToolCallHandler.php |  2 +-
 18 files changed, 79 insertions(+), 56 deletions(-)
 create mode 100644 src/Capability/Prompt/IdentifierInterface.php
 rename examples/cli/src/Manager/PromptManager.php => src/Capability/PromptChain.php (51%)
 create mode 100644 src/Capability/Resource/IdentifierInterface.php
 rename examples/cli/src/Manager/ResourceManager.php => src/Capability/ResourceChain.php (51%)
 create mode 100644 src/Capability/Tool/IdentifierInterface.php
 rename examples/cli/src/Manager/ToolManager.php => src/Capability/ToolChain.php (51%)

diff --git a/examples/cli/index.php b/examples/cli/index.php
index 1c630d9..bf7040d 100644
--- a/examples/cli/index.php
+++ b/examples/cli/index.php
@@ -2,8 +2,8 @@
 
 require __DIR__.'/vendor/autoload.php';
 
-use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\Console as SymfonyConsole;
+use Symfony\Component\Console\Output\OutputInterface;
 
 $debug = (bool) ($_SERVER['DEBUG'] ?? false);
 
diff --git a/examples/cli/src/Builder.php b/examples/cli/src/Builder.php
index b40664e..7b91d2b 100644
--- a/examples/cli/src/Builder.php
+++ b/examples/cli/src/Builder.php
@@ -2,9 +2,9 @@
 
 namespace App;
 
-use App\Manager\PromptManager;
-use App\Manager\ResourceManager;
-use App\Manager\ToolManager;
+use PhpLlm\McpSdk\Capability\PromptChain;
+use PhpLlm\McpSdk\Capability\ResourceChain;
+use PhpLlm\McpSdk\Capability\ToolChain;
 use PhpLlm\McpSdk\Server\NotificationHandler;
 use PhpLlm\McpSdk\Server\NotificationHandler\InitializedHandler;
 use PhpLlm\McpSdk\Server\RequestHandler;
@@ -24,15 +24,15 @@ class Builder
      */
     public static function buildRequestHandlers(): array
     {
-        $promptManager = new PromptManager([
+        $promptManager = new PromptChain([
             new ExamplePrompt(),
         ]);
 
-        $resourceManager = new ResourceManager([
+        $resourceManager = new ResourceChain([
             new ExampleResource(),
         ]);
 
-        $toolManager = new ToolManager([
+        $toolManager = new ToolChain([
             new ExampleTool(),
         ]);
 
diff --git a/examples/cli/src/ExamplePrompt.php b/examples/cli/src/ExamplePrompt.php
index 6a6b6ce..bbe86c9 100644
--- a/examples/cli/src/ExamplePrompt.php
+++ b/examples/cli/src/ExamplePrompt.php
@@ -6,12 +6,13 @@
 use PhpLlm\McpSdk\Capability\Prompt\PromptGet;
 use PhpLlm\McpSdk\Capability\Prompt\PromptGetResult;
 use PhpLlm\McpSdk\Capability\Prompt\PromptGetResultMessages;
+use PhpLlm\McpSdk\Capability\Prompt\PromptGetterInterface;
 
-class ExamplePrompt implements MetadataInterface
+class ExamplePrompt implements MetadataInterface, PromptGetterInterface
 {
-    public function __invoke(PromptGet $request): PromptGetResult
+    public function get(PromptGet $input): PromptGetResult
     {
-        $firstName = $request->arguments['first name'] ?? null;
+        $firstName = $input->arguments['first name'] ?? null;
 
         return new PromptGetResult(
             $this->getDescription(),
diff --git a/examples/cli/src/ExampleResource.php b/examples/cli/src/ExampleResource.php
index 874c860..9e6cdba 100644
--- a/examples/cli/src/ExampleResource.php
+++ b/examples/cli/src/ExampleResource.php
@@ -4,11 +4,12 @@
 
 use PhpLlm\McpSdk\Capability\Resource\MetadataInterface;
 use PhpLlm\McpSdk\Capability\Resource\ResourceRead;
+use PhpLlm\McpSdk\Capability\Resource\ResourceReaderInterface;
 use PhpLlm\McpSdk\Capability\Resource\ResourceReadResult;
 
-class ExampleResource implements MetadataInterface
+class ExampleResource implements MetadataInterface, ResourceReaderInterface
 {
-    public function __invoke(ResourceRead $request): ResourceReadResult
+    public function read(ResourceRead $input): ResourceReadResult
     {
         return new ResourceReadResult(
             'Content of '.$this->getName(),
diff --git a/examples/cli/src/ExampleTool.php b/examples/cli/src/ExampleTool.php
index 66b966b..618acb9 100644
--- a/examples/cli/src/ExampleTool.php
+++ b/examples/cli/src/ExampleTool.php
@@ -5,12 +5,13 @@
 use PhpLlm\McpSdk\Capability\Tool\MetadataInterface;
 use PhpLlm\McpSdk\Capability\Tool\ToolCall;
 use PhpLlm\McpSdk\Capability\Tool\ToolCallResult;
+use PhpLlm\McpSdk\Capability\Tool\ToolExecutorInterface;
 
-class ExampleTool implements MetadataInterface
+class ExampleTool implements MetadataInterface, ToolExecutorInterface
 {
-    public function __invoke(ToolCall $toolCall): ToolCallResult
+    public function call(ToolCall $input): ToolCallResult
     {
-        $format = $toolCall->arguments['format'] ?? 'Y-m-d H:i:s';
+        $format = $input->arguments['format'] ?? 'Y-m-d H:i:s';
 
         return new ToolCallResult(
             (new \DateTime('now', new \DateTimeZone('UTC')))->format($format)
diff --git a/src/Capability/Prompt/IdentifierInterface.php b/src/Capability/Prompt/IdentifierInterface.php
new file mode 100644
index 0000000..8096c17
--- /dev/null
+++ b/src/Capability/Prompt/IdentifierInterface.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpLlm\McpSdk\Capability\Prompt;
+
+interface IdentifierInterface
+{
+    public function getName(): string;
+}
diff --git a/src/Capability/Prompt/MetadataInterface.php b/src/Capability/Prompt/MetadataInterface.php
index 6bc5f46..6dc3404 100644
--- a/src/Capability/Prompt/MetadataInterface.php
+++ b/src/Capability/Prompt/MetadataInterface.php
@@ -4,10 +4,8 @@
 
 namespace PhpLlm\McpSdk\Capability\Prompt;
 
-interface MetadataInterface
+interface MetadataInterface extends IdentifierInterface
 {
-    public function getName(): string;
-
     public function getDescription(): ?string;
 
     /**
diff --git a/src/Capability/Prompt/PromptGetterInterface.php b/src/Capability/Prompt/PromptGetterInterface.php
index cf537b1..75f2b86 100644
--- a/src/Capability/Prompt/PromptGetterInterface.php
+++ b/src/Capability/Prompt/PromptGetterInterface.php
@@ -11,5 +11,5 @@ interface PromptGetterInterface
      * @throws PromptGetException      if the prompt execution fails
      * @throws PromptNotFoundException if the prompt is not found
      */
-    public function get(PromptGet $request): PromptGetResult;
+    public function get(PromptGet $input): PromptGetResult;
 }
diff --git a/examples/cli/src/Manager/PromptManager.php b/src/Capability/PromptChain.php
similarity index 51%
rename from examples/cli/src/Manager/PromptManager.php
rename to src/Capability/PromptChain.php
index fce427c..a312542 100644
--- a/examples/cli/src/Manager/PromptManager.php
+++ b/src/Capability/PromptChain.php
@@ -1,8 +1,9 @@
 <?php
 
-namespace App\Manager;
+namespace PhpLlm\McpSdk\Capability;
 
 use PhpLlm\McpSdk\Capability\Prompt\CollectionInterface;
+use PhpLlm\McpSdk\Capability\Prompt\IdentifierInterface;
 use PhpLlm\McpSdk\Capability\Prompt\MetadataInterface;
 use PhpLlm\McpSdk\Capability\Prompt\PromptGet;
 use PhpLlm\McpSdk\Capability\Prompt\PromptGetResult;
@@ -10,11 +11,11 @@
 use PhpLlm\McpSdk\Exception\PromptGetException;
 use PhpLlm\McpSdk\Exception\PromptNotFoundException;
 
-class PromptManager implements PromptGetterInterface, CollectionInterface
+class PromptChain implements PromptGetterInterface, CollectionInterface
 {
     public function __construct(
         /**
-         * @var (MetadataInterface | callable(PromptGet):PromptGetResult)[]
+         * @var (IdentifierInterface & (MetadataInterface | PromptGetterInterface))[]
          */
         private array $items,
     ) {
@@ -22,21 +23,21 @@ public function __construct(
 
     public function getMetadata(): array
     {
-        return $this->items;
+        return array_filter($this->items, fn ($item) => $item instanceof MetadataInterface);
     }
 
-    public function get(PromptGet $request): PromptGetResult
+    public function get(PromptGet $input): PromptGetResult
     {
         foreach ($this->items as $item) {
-            if ($request->name === $item->getName()) {
+            if ($item instanceof PromptGetterInterface && $input->name === $item->getName()) {
                 try {
-                    return $item($request);
+                    return $item->get($input);
                 } catch (\Throwable $e) {
-                    throw new PromptGetException($request, $e);
+                    throw new PromptGetException($input, $e);
                 }
             }
         }
 
-        throw new PromptNotFoundException($request);
+        throw new PromptNotFoundException($input);
     }
 }
diff --git a/src/Capability/Resource/IdentifierInterface.php b/src/Capability/Resource/IdentifierInterface.php
new file mode 100644
index 0000000..e3ee06e
--- /dev/null
+++ b/src/Capability/Resource/IdentifierInterface.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpLlm\McpSdk\Capability\Resource;
+
+interface IdentifierInterface
+{
+    public function getUri(): string;
+}
diff --git a/src/Capability/Resource/MetadataInterface.php b/src/Capability/Resource/MetadataInterface.php
index 702d1eb..253ce8c 100644
--- a/src/Capability/Resource/MetadataInterface.php
+++ b/src/Capability/Resource/MetadataInterface.php
@@ -4,10 +4,8 @@
 
 namespace PhpLlm\McpSdk\Capability\Resource;
 
-interface MetadataInterface
+interface MetadataInterface extends IdentifierInterface
 {
-    public function getUri(): string;
-
     public function getName(): string;
 
     public function getDescription(): ?string;
diff --git a/src/Capability/Resource/ResourceReaderInterface.php b/src/Capability/Resource/ResourceReaderInterface.php
index 10808f7..3102ac8 100644
--- a/src/Capability/Resource/ResourceReaderInterface.php
+++ b/src/Capability/Resource/ResourceReaderInterface.php
@@ -11,5 +11,5 @@ interface ResourceReaderInterface
      * @throws ResourceReadException     if the resource execution fails
      * @throws ResourceNotFoundException if the resource is not found
      */
-    public function read(ResourceRead $request): ResourceReadResult;
+    public function read(ResourceRead $input): ResourceReadResult;
 }
diff --git a/examples/cli/src/Manager/ResourceManager.php b/src/Capability/ResourceChain.php
similarity index 51%
rename from examples/cli/src/Manager/ResourceManager.php
rename to src/Capability/ResourceChain.php
index b19951c..a4cd6a7 100644
--- a/examples/cli/src/Manager/ResourceManager.php
+++ b/src/Capability/ResourceChain.php
@@ -1,8 +1,9 @@
 <?php
 
-namespace App\Manager;
+namespace PhpLlm\McpSdk\Capability;
 
 use PhpLlm\McpSdk\Capability\Resource\CollectionInterface;
+use PhpLlm\McpSdk\Capability\Resource\IdentifierInterface;
 use PhpLlm\McpSdk\Capability\Resource\MetadataInterface;
 use PhpLlm\McpSdk\Capability\Resource\ResourceRead;
 use PhpLlm\McpSdk\Capability\Resource\ResourceReaderInterface;
@@ -10,12 +11,11 @@
 use PhpLlm\McpSdk\Exception\ResourceNotFoundException;
 use PhpLlm\McpSdk\Exception\ResourceReadException;
 
-class ResourceManager implements CollectionInterface, ResourceReaderInterface
+class ResourceChain implements CollectionInterface, ResourceReaderInterface
 {
     public function __construct(
         /**
-         * TODO this is bad design. What if we want to add resource/exists, then this becomes invalid and we need a BC break
-         * @var (MetadataInterface | callable(ResourceRead):ResourceReadResult)[]
+         * @var (IdentifierInterface & (MetadataInterface | ResourceReaderInterface))[]
          */
         private array $items,
     ) {
@@ -23,21 +23,21 @@ public function __construct(
 
     public function getMetadata(): array
     {
-        return $this->items;
+        return array_filter($this->items, fn ($item) => $item instanceof MetadataInterface);
     }
 
-    public function read(ResourceRead $request): ResourceReadResult
+    public function read(ResourceRead $input): ResourceReadResult
     {
         foreach ($this->items as $item) {
-            if ($item instanceof ReadInterface && $request->uri === $item->getUri()) {
+            if ($item instanceof ResourceReaderInterface && $input->uri === $item->getUri()) {
                 try {
-                    return $item($request);
+                    return $item->read($input);
                 } catch (\Throwable $e) {
-                    throw new ResourceReadException($request, $e);
+                    throw new ResourceReadException($input, $e);
                 }
             }
         }
 
-        throw new ResourceNotFoundException($request);
+        throw new ResourceNotFoundException($input);
     }
 }
diff --git a/src/Capability/Tool/IdentifierInterface.php b/src/Capability/Tool/IdentifierInterface.php
new file mode 100644
index 0000000..7dd5bb2
--- /dev/null
+++ b/src/Capability/Tool/IdentifierInterface.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpLlm\McpSdk\Capability\Tool;
+
+interface IdentifierInterface
+{
+    public function getName(): string;
+}
diff --git a/src/Capability/Tool/MetadataInterface.php b/src/Capability/Tool/MetadataInterface.php
index a0dfea3..24d870d 100644
--- a/src/Capability/Tool/MetadataInterface.php
+++ b/src/Capability/Tool/MetadataInterface.php
@@ -4,10 +4,8 @@
 
 namespace PhpLlm\McpSdk\Capability\Tool;
 
-interface MetadataInterface
+interface MetadataInterface extends IdentifierInterface
 {
-    public function getName(): string;
-
     public function getDescription(): string;
 
     /**
diff --git a/src/Capability/Tool/ToolExecutorInterface.php b/src/Capability/Tool/ToolExecutorInterface.php
index 0834ad8..a80c201 100644
--- a/src/Capability/Tool/ToolExecutorInterface.php
+++ b/src/Capability/Tool/ToolExecutorInterface.php
@@ -11,5 +11,5 @@ interface ToolExecutorInterface
      * @throws ToolExecutionException if the tool execution fails
      * @throws ToolNotFoundException  if the tool is not found
      */
-    public function execute(ToolCall $toolCall): ToolCallResult;
+    public function call(ToolCall $input): ToolCallResult;
 }
diff --git a/examples/cli/src/Manager/ToolManager.php b/src/Capability/ToolChain.php
similarity index 51%
rename from examples/cli/src/Manager/ToolManager.php
rename to src/Capability/ToolChain.php
index 7ec4530..c805614 100644
--- a/examples/cli/src/Manager/ToolManager.php
+++ b/src/Capability/ToolChain.php
@@ -1,8 +1,9 @@
 <?php
 
-namespace App\Manager;
+namespace PhpLlm\McpSdk\Capability;
 
 use PhpLlm\McpSdk\Capability\Tool\CollectionInterface;
+use PhpLlm\McpSdk\Capability\Tool\IdentifierInterface;
 use PhpLlm\McpSdk\Capability\Tool\MetadataInterface;
 use PhpLlm\McpSdk\Capability\Tool\ToolCall;
 use PhpLlm\McpSdk\Capability\Tool\ToolCallResult;
@@ -10,11 +11,11 @@
 use PhpLlm\McpSdk\Exception\ToolExecutionException;
 use PhpLlm\McpSdk\Exception\ToolNotFoundException;
 
-class ToolManager implements ToolExecutorInterface, CollectionInterface
+class ToolChain implements ToolExecutorInterface, CollectionInterface
 {
     public function __construct(
         /**
-         * @var (MetadataInterface | callable(ToolCall):ToolCallResult)[] $items
+         * @var (IdentifierInterface & (MetadataInterface | ToolExecutorInterface))[] $items
          */
         private array $items,
     ) {
@@ -22,21 +23,21 @@ public function __construct(
 
     public function getMetadata(): array
     {
-        return $this->items;
+        return array_filter($this->items, fn ($item) => $item instanceof MetadataInterface);
     }
 
-    public function execute(ToolCall $toolCall): ToolCallResult
+    public function call(ToolCall $input): ToolCallResult
     {
         foreach ($this->items as $item) {
-            if ($toolCall->name === $item->getName()) {
+            if ($item instanceof ToolExecutorInterface && $input->name === $item->getName()) {
                 try {
-                    return $item($toolCall);
+                    return $item->call($input);
                 } catch (\Throwable $e) {
-                    throw new ToolExecutionException($toolCall, $e);
+                    throw new ToolExecutionException($input, $e);
                 }
             }
         }
 
-        throw new ToolNotFoundException($toolCall);
+        throw new ToolNotFoundException($input);
     }
 }
diff --git a/src/Server/RequestHandler/ToolCallHandler.php b/src/Server/RequestHandler/ToolCallHandler.php
index 35015a9..4cab6e1 100644
--- a/src/Server/RequestHandler/ToolCallHandler.php
+++ b/src/Server/RequestHandler/ToolCallHandler.php
@@ -24,7 +24,7 @@ public function createResponse(Request $message): Response|Error
         $arguments = $message->params['arguments'] ?? [];
 
         try {
-            $result = $this->toolExecutor->execute(new ToolCall(uniqid('', true), $name, $arguments));
+            $result = $this->toolExecutor->call(new ToolCall(uniqid('', true), $name, $arguments));
         } catch (ExceptionInterface) {
             return Error::internalError($message->id, 'Error while executing tool');
         }

From 9d8200e1976cc51122c8e65e3e72f5ea90c6a525 Mon Sep 17 00:00:00 2001
From: Tobias Nyholm <tobias.nyholm@gmail.com>
Date: Sun, 25 May 2025 23:21:58 +0200
Subject: [PATCH 3/4] feat: update comments

---
 src/Capability/PromptChain.php   | 5 ++++-
 src/Capability/ResourceChain.php | 5 ++++-
 src/Capability/ToolChain.php     | 5 ++++-
 3 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/src/Capability/PromptChain.php b/src/Capability/PromptChain.php
index a312542..cfad56a 100644
--- a/src/Capability/PromptChain.php
+++ b/src/Capability/PromptChain.php
@@ -11,11 +11,14 @@
 use PhpLlm\McpSdk\Exception\PromptGetException;
 use PhpLlm\McpSdk\Exception\PromptNotFoundException;
 
+/**
+ * A collection of prompts. All prompts need to implement IdentifierInterface.
+ */
 class PromptChain implements PromptGetterInterface, CollectionInterface
 {
     public function __construct(
         /**
-         * @var (IdentifierInterface & (MetadataInterface | PromptGetterInterface))[]
+         * @var IdentifierInterface[]
          */
         private array $items,
     ) {
diff --git a/src/Capability/ResourceChain.php b/src/Capability/ResourceChain.php
index a4cd6a7..6581269 100644
--- a/src/Capability/ResourceChain.php
+++ b/src/Capability/ResourceChain.php
@@ -11,11 +11,14 @@
 use PhpLlm\McpSdk\Exception\ResourceNotFoundException;
 use PhpLlm\McpSdk\Exception\ResourceReadException;
 
+/**
+ * A collection of resources. All resources need to implement IdentifierInterface.
+ */
 class ResourceChain implements CollectionInterface, ResourceReaderInterface
 {
     public function __construct(
         /**
-         * @var (IdentifierInterface & (MetadataInterface | ResourceReaderInterface))[]
+         * @var IdentifierInterface[]
          */
         private array $items,
     ) {
diff --git a/src/Capability/ToolChain.php b/src/Capability/ToolChain.php
index c805614..c5cd4b7 100644
--- a/src/Capability/ToolChain.php
+++ b/src/Capability/ToolChain.php
@@ -11,11 +11,14 @@
 use PhpLlm\McpSdk\Exception\ToolExecutionException;
 use PhpLlm\McpSdk\Exception\ToolNotFoundException;
 
+/**
+ * A collection of tools. All tools need to implement IdentifierInterface.
+ */
 class ToolChain implements ToolExecutorInterface, CollectionInterface
 {
     public function __construct(
         /**
-         * @var (IdentifierInterface & (MetadataInterface | ToolExecutorInterface))[] $items
+         * @var IdentifierInterface[] $items
          */
         private array $items,
     ) {

From 22086135cc1ffaaf17cb086ad13ac4275b373d11 Mon Sep 17 00:00:00 2001
From: Tobias Nyholm <tobias.nyholm@gmail.com>
Date: Sun, 25 May 2025 23:26:57 +0200
Subject: [PATCH 4/4] fix: rector

---
 src/Capability/PromptChain.php   | 2 +-
 src/Capability/ResourceChain.php | 2 +-
 src/Capability/ToolChain.php     | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/Capability/PromptChain.php b/src/Capability/PromptChain.php
index cfad56a..49f8e1d 100644
--- a/src/Capability/PromptChain.php
+++ b/src/Capability/PromptChain.php
@@ -20,7 +20,7 @@ public function __construct(
         /**
          * @var IdentifierInterface[]
          */
-        private array $items,
+        private readonly array $items,
     ) {
     }
 
diff --git a/src/Capability/ResourceChain.php b/src/Capability/ResourceChain.php
index 6581269..e16d74c 100644
--- a/src/Capability/ResourceChain.php
+++ b/src/Capability/ResourceChain.php
@@ -20,7 +20,7 @@ public function __construct(
         /**
          * @var IdentifierInterface[]
          */
-        private array $items,
+        private readonly array $items,
     ) {
     }
 
diff --git a/src/Capability/ToolChain.php b/src/Capability/ToolChain.php
index c5cd4b7..6d3eb9e 100644
--- a/src/Capability/ToolChain.php
+++ b/src/Capability/ToolChain.php
@@ -20,7 +20,7 @@ public function __construct(
         /**
          * @var IdentifierInterface[] $items
          */
-        private array $items,
+        private readonly array $items,
     ) {
     }