Skip to content
This repository was archived by the owner on Jul 16, 2025. It is now read-only.

Commit e088c55

Browse files
committed
feat: add capabilities and example app
1 parent 2e754bf commit e088c55

36 files changed

+811
-31
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/.git* export-ignore
2+
/examples export-ignore
23
/tests export-ignore
34
/.php-cs-fixer.dist.php export-ignore
45
/phpstan.dist.neon export-ignore

examples/cli/composer.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "php-llm/mcp-cli-example",
3+
"description": "An example applicationf for CLI",
4+
"license": "MIT",
5+
"type": "project",
6+
"authors": [
7+
{
8+
"name": "Tobias Nyholm",
9+
"email": "tobias.nyholm@gmail.com"
10+
}
11+
],
12+
"require": {
13+
"php": ">=8.2",
14+
"php-llm/mcp-sdk": "@dev",
15+
"symfony/console": "^7.2"
16+
},
17+
"minimum-stability": "stable",
18+
"autoload": {
19+
"psr-4": {
20+
"App\\": "src/"
21+
}
22+
}
23+
}
24+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{"jsonrpc": "2.0", "id": 1, "method": "resources/list", "params": []}
2+
{"jsonrpc": "2.0", "id": 2, "method": "resources/read", "params": {"uri": "file:///project/src/main.rs"}}
3+
4+
{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}
5+
{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "Current time"}}
6+
{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "Current time","arguments": {"format": "Y-m-d",
7+
8+
{"jsonrpc": "2.0", "id": 1, "method": "prompts/list"}
9+
{"jsonrpc": "2.0", "id": 2 ,"method": "prompts/get", "params": {"name": "Greet"}}
10+
{"jsonrpc": "2.0", "id": 2 ,"method": "prompts/get", "params": {"name": "Greet", "arguments": { "firstName": "Tobias" }}}

examples/cli/index.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
require __DIR__.'/vendor/autoload.php';
4+
5+
use Symfony\Component\Console\Output\OutputInterface;
6+
7+
// Setup input, output and logger
8+
$input = new Symfony\Component\Console\Input\ArgvInput($argv);
9+
$output = new Symfony\Component\Console\Output\ConsoleOutput(OutputInterface::VERBOSITY_VERY_VERBOSE);
10+
$logger = new Symfony\Component\Console\Logger\ConsoleLogger($output);
11+
12+
// Configure the JsonRpcHandler
13+
$jsonRpcHandler = new PhpLlm\McpSdk\Server\JsonRpcHandler(
14+
new PhpLlm\McpSdk\Message\Factory(),
15+
App\Builder::buildRequestHandlers(),
16+
App\Builder::buildNotificationHandlers(),
17+
$logger
18+
);
19+
20+
// Set up the server
21+
$sever = new PhpLlm\McpSdk\Server($jsonRpcHandler, $logger);
22+
23+
// Create the transport layer using Symfony Console
24+
$transport = new PhpLlm\McpSdk\Server\Transport\Stdio\SymfonyConsoleTransport($input, $output);
25+
26+
// Start our application
27+
$sever->connect($transport);

examples/cli/src/Builder.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace App;
4+
5+
use App\Manager\PromptManager;
6+
use App\Manager\ResourceManager;
7+
use App\Manager\ToolManager;
8+
use PhpLlm\McpSdk\Server\NotificationHandler\InitializedHandler;
9+
use PhpLlm\McpSdk\Server\RequestHandler\InitializeHandler;
10+
use PhpLlm\McpSdk\Server\RequestHandler\PingHandler;
11+
use PhpLlm\McpSdk\Server\RequestHandler\PromptGetHandler;
12+
use PhpLlm\McpSdk\Server\RequestHandler\PromptListHandler;
13+
use PhpLlm\McpSdk\Server\RequestHandler\ResourceListHandler;
14+
use PhpLlm\McpSdk\Server\RequestHandler\ResourceReadHandler;
15+
use PhpLlm\McpSdk\Server\RequestHandler\ToolCallHandler;
16+
use PhpLlm\McpSdk\Server\RequestHandler\ToolListHandler;
17+
18+
class Builder
19+
{
20+
public static function buildRequestHandlers(): array
21+
{
22+
$promptManager = new PromptManager();
23+
$resourceManager = new ResourceManager();
24+
$toolManager = new ToolManager();
25+
26+
return [
27+
new InitializeHandler(),
28+
new PingHandler(),
29+
new PromptListHandler($promptManager),
30+
new PromptGetHandler($promptManager),
31+
new ResourceListHandler($resourceManager),
32+
new ResourceReadHandler($resourceManager),
33+
new ToolCallHandler($toolManager),
34+
new ToolListHandler($toolManager),
35+
];
36+
}
37+
38+
public static function buildNotificationHandlers(): array
39+
{
40+
return [
41+
new InitializedHandler(),
42+
];
43+
}
44+
}

examples/cli/src/ExamplePrompt.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace App;
4+
5+
use PhpLlm\McpSdk\Capability\Prompt\MetadataInterface;
6+
7+
class ExamplePrompt implements MetadataInterface
8+
{
9+
public function __invoke(?string $firstName = null)
10+
{
11+
return sprintf('Hello %s', $firstName ?? 'World');
12+
}
13+
14+
public function getName(): string
15+
{
16+
return 'Greet';
17+
}
18+
19+
public function getDescription(): ?string
20+
{
21+
return 'Greet a person with a nice message';
22+
}
23+
24+
public function getArguments(): array
25+
{
26+
return [
27+
[
28+
'name' => 'firstName',
29+
'description' => 'The name of the person to greet',
30+
'required' => false,
31+
],
32+
];
33+
}
34+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace App;
4+
5+
use PhpLlm\McpSdk\Capability\Resource\MetadataInterface;
6+
7+
class ExampleResource implements MetadataInterface
8+
{
9+
public function getUri(): string
10+
{
11+
return 'file:///project/src/main.rs';
12+
}
13+
14+
public function getName(): string
15+
{
16+
return 'My resource';
17+
}
18+
19+
public function getDescription(): ?string
20+
{
21+
return 'This is just an example';
22+
}
23+
24+
public function getMimeType(): ?string
25+
{
26+
return null;
27+
}
28+
29+
public function getSize(): ?int
30+
{
31+
return null;
32+
}
33+
}

examples/cli/src/ExampleTool.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace App;
4+
5+
use PhpLlm\McpSdk\Capability\Tool\MetadataInterface;
6+
7+
class ExampleTool implements MetadataInterface
8+
{
9+
public function __invoke(string $format = 'Y-m-d H:i:s')
10+
{
11+
return (new \DateTime('now', new \DateTimeZone('UTC')))->format($format);
12+
}
13+
14+
public function getName(): string
15+
{
16+
return 'Current time';
17+
}
18+
19+
public function getDescription(): string
20+
{
21+
return 'Returns the current time in UTC';
22+
}
23+
24+
public function getInputSchema(): array
25+
{
26+
return [
27+
'type' => 'object',
28+
'properties' => [
29+
'format' => [
30+
'type' => 'string',
31+
'description' => 'The format of the time, e.g. "Y-m-d H:i:s"',
32+
'default' => 'Y-m-d H:i:s',
33+
],
34+
],
35+
'required' => [],
36+
];
37+
}
38+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace App\Manager;
4+
5+
use App\ExamplePrompt;
6+
use PhpLlm\McpSdk\Capability\Prompt\CollectionInterface;
7+
use PhpLlm\McpSdk\Capability\Prompt\PromptGet;
8+
use PhpLlm\McpSdk\Capability\Prompt\PromptGetterInterface;
9+
use PhpLlm\McpSdk\Exception\PromptGetException;
10+
use PhpLlm\McpSdk\Exception\PromptNotFoundException;
11+
12+
class PromptManager implements PromptGetterInterface, CollectionInterface
13+
{
14+
private array $items;
15+
16+
public function __construct(
17+
) {
18+
$this->items = [
19+
new ExamplePrompt(),
20+
];
21+
}
22+
23+
public function getMetadata(): array
24+
{
25+
return $this->items;
26+
}
27+
28+
public function get(PromptGet $request): mixed
29+
{
30+
foreach ($this->items as $item) {
31+
if ($request->name === $item->getName()) {
32+
try {
33+
return $item->__invoke(...$request->arguments);
34+
} catch (\Throwable $e) {
35+
throw PromptGetException::executionFailed($request, $e);
36+
}
37+
}
38+
}
39+
40+
throw PromptNotFoundException::create($request);
41+
}
42+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace App\Manager;
4+
5+
use App\ExampleResource;
6+
use PhpLlm\McpSdk\Capability\Resource\CollectionInterface;
7+
use PhpLlm\McpSdk\Capability\Resource\ResourceRead;
8+
use PhpLlm\McpSdk\Capability\Resource\ResourceReaderInterface;
9+
use PhpLlm\McpSdk\Capability\Resource\ResourceReadResult;
10+
use PhpLlm\McpSdk\Exception\ResourceNotFoundException;
11+
12+
class ResourceManager implements CollectionInterface, ResourceReaderInterface
13+
{
14+
private array $items;
15+
16+
public function __construct(
17+
) {
18+
$this->items = [
19+
new ExampleResource(),
20+
];
21+
}
22+
23+
public function getMetadata(): array
24+
{
25+
return $this->items;
26+
}
27+
28+
public function read(ResourceRead $request): ResourceReadResult
29+
{
30+
foreach ($this->items as $resource) {
31+
if ($request->uri === $resource->getUri()) {
32+
// In a real implementation, you would read the resource from its URI.
33+
// Here we just return a dummy string for demonstration purposes.
34+
return new ResourceReadResult(
35+
$request->id,
36+
'Content of '.$resource->getName(),
37+
$resource->getUri(),
38+
);
39+
}
40+
}
41+
42+
throw new ResourceNotFoundException();
43+
}
44+
}

0 commit comments

Comments
 (0)