Skip to content

feat: add Prompt/Resource/Tool Chain #25

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 4 commits into from
May 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions examples/cli/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@

require __DIR__.'/vendor/autoload.php';

use Symfony\Component\Console as SymfonyConsole;
use Symfony\Component\Console\Output\OutputInterface;

$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(),
Expand Down
20 changes: 14 additions & 6 deletions examples/cli/src/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -24,9 +24,17 @@ class Builder
*/
public static function buildRequestHandlers(): array
{
$promptManager = new PromptManager();
$resourceManager = new ResourceManager();
$toolManager = new ToolManager();
$promptManager = new PromptChain([
new ExamplePrompt(),
]);

$resourceManager = new ResourceChain([
new ExampleResource(),
]);

$toolManager = new ToolChain([
new ExampleTool(),
]);

return [
new InitializeHandler(),
Expand Down
20 changes: 16 additions & 4 deletions examples/cli/src/ExamplePrompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,24 @@
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;
use PhpLlm\McpSdk\Capability\Prompt\PromptGetterInterface;

class ExamplePrompt implements MetadataInterface
class ExamplePrompt implements MetadataInterface, PromptGetterInterface
{
public function __invoke(?string $firstName = null): string
public function get(PromptGet $input): PromptGetResult
{
return sprintf('Hello %s', $firstName ?? 'World');
$firstName = $input->arguments['first name'] ?? null;

return new PromptGetResult(
$this->getDescription(),
[new PromptGetResultMessages(
'user',
sprintf('Hello %s', $firstName ?? 'World')
)]
);
}

public function getName(): string
Expand All @@ -25,7 +37,7 @@ public function getArguments(): array
{
return [
[
'name' => 'firstName',
'name' => 'first name',
'description' => 'The name of the person to greet',
'required' => false,
],
Expand Down
13 changes: 12 additions & 1 deletion examples/cli/src/ExampleResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,20 @@
namespace App;

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 read(ResourceRead $input): ResourceReadResult
{
return new ResourceReadResult(
'Content of '.$this->getName(),
$this->getUri(),
);
}

public function getUri(): string
{
return 'file:///project/src/main.rs';
Expand Down
13 changes: 10 additions & 3 deletions examples/cli/src/ExampleTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@
namespace App;

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(string $format = 'Y-m-d H:i:s'): string
public function call(ToolCall $input): ToolCallResult
{
return (new \DateTime('now', new \DateTimeZone('UTC')))->format($format);
$format = $input->arguments['format'] ?? 'Y-m-d H:i:s';

return new ToolCallResult(
(new \DateTime('now', new \DateTimeZone('UTC')))->format($format)
);
}

public function getName(): string
Expand Down
53 changes: 0 additions & 53 deletions examples/cli/src/Manager/PromptManager.php

This file was deleted.

46 changes: 0 additions & 46 deletions examples/cli/src/Manager/ResourceManager.php

This file was deleted.

48 changes: 0 additions & 48 deletions examples/cli/src/Manager/ToolManager.php

This file was deleted.

8 changes: 8 additions & 0 deletions src/Capability/Prompt/IdentifierInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace PhpLlm\McpSdk\Capability\Prompt;

interface IdentifierInterface
{
public function getName(): string;
}
4 changes: 1 addition & 3 deletions src/Capability/Prompt/MetadataInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@

namespace PhpLlm\McpSdk\Capability\Prompt;

interface MetadataInterface
interface MetadataInterface extends IdentifierInterface
{
public function getName(): string;

public function getDescription(): ?string;

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Capability/Prompt/PromptGetterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
46 changes: 46 additions & 0 deletions src/Capability/PromptChain.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

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;
use PhpLlm\McpSdk\Capability\Prompt\PromptGetterInterface;
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[]
*/
private readonly array $items,
) {
}

public function getMetadata(): array
{
return array_filter($this->items, fn ($item) => $item instanceof MetadataInterface);
}

public function get(PromptGet $input): PromptGetResult
{
foreach ($this->items as $item) {
if ($item instanceof PromptGetterInterface && $input->name === $item->getName()) {
try {
return $item->get($input);
} catch (\Throwable $e) {
throw new PromptGetException($input, $e);
}
}
}

throw new PromptNotFoundException($input);
}
}
8 changes: 8 additions & 0 deletions src/Capability/Resource/IdentifierInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace PhpLlm\McpSdk\Capability\Resource;

interface IdentifierInterface
{
public function getUri(): string;
}
4 changes: 1 addition & 3 deletions src/Capability/Resource/MetadataInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/Capability/Resource/ResourceReaderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Loading
Loading