Kick off the SDK based on Symfony's MCP SDK
This commit is contained in:
parent
0044ffb365
commit
ee0dffe369
8 changed files with 3924 additions and 0 deletions
29
examples/cli/README.md
Normal file
29
examples/cli/README.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Example app with CLI
|
||||
|
||||
This is just for testing and debugging purposes.
|
||||
|
||||
|
||||
Install and create symlink with:
|
||||
|
||||
```bash
|
||||
cd /path/to/your/project/examples/cli
|
||||
composer update
|
||||
rm -rf vendor/mcp/sdk/src
|
||||
ln -s /path/to/your/project/src /path/to/your/project/examples/cli/vendor/mcp/sdk/src
|
||||
```
|
||||
|
||||
Run the CLI with:
|
||||
|
||||
```bash
|
||||
DEBUG=1 php index.php
|
||||
```
|
||||
|
||||
You will see debug outputs to help you understand what is happening.
|
||||
|
||||
In this terminal you can now test add some json strings. See `example-requests.json`.
|
||||
|
||||
Run with Inspector:
|
||||
|
||||
```bash
|
||||
npx @modelcontextprotocol/inspector php index.php
|
||||
```
|
||||
27
examples/cli/composer.json
Normal file
27
examples/cli/composer.json
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "mcp/cli-server-example",
|
||||
"description": "An example application for CLI",
|
||||
"license": "MIT",
|
||||
"type": "project",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Tobias Nyholm",
|
||||
"email": "tobias.nyholm@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"mcp/sdk": "@dev",
|
||||
"symfony/console": "^7.2"
|
||||
},
|
||||
"minimum-stability": "stable",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "src/"
|
||||
}
|
||||
},
|
||||
"repositories": [
|
||||
{ "type": "path", "url": "../../" }
|
||||
]
|
||||
}
|
||||
|
||||
12
examples/cli/example-requests.json
Normal file
12
examples/cli/example-requests.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[
|
||||
{"jsonrpc": "2.0", "id": 1, "method": "resources/list", "params": []},
|
||||
{"jsonrpc": "2.0", "id": 2, "method": "resources/read", "params": {"uri": "file:///project/src/main.rs"}},
|
||||
|
||||
{"jsonrpc": "2.0", "id": 1, "method": "tools/list"},
|
||||
{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "Current time"}},
|
||||
{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "Current time","arguments": {"format": "Y-m-d"}}},
|
||||
|
||||
{"jsonrpc": "2.0", "id": 1, "method": "prompts/list"},
|
||||
{"jsonrpc": "2.0", "id": 2 ,"method": "prompts/get", "params": {"name": "Greet"}},
|
||||
{"jsonrpc": "2.0", "id": 2 ,"method": "prompts/get", "params": {"name": "Greet", "arguments": { "firstName": "Tobias" }}}
|
||||
]
|
||||
39
examples/cli/index.php
Normal file
39
examples/cli/index.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the official PHP MCP SDK.
|
||||
*
|
||||
* A collaboration between Symfony and the PHP Foundation.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
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 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 and build the functionality
|
||||
$jsonRpcHandler = new Mcp\Server\JsonRpcHandler(
|
||||
new Mcp\Message\Factory(),
|
||||
App\Builder::buildRequestHandlers(),
|
||||
App\Builder::buildNotificationHandlers(),
|
||||
$logger
|
||||
);
|
||||
|
||||
// Set up the server
|
||||
$sever = new Mcp\Server($jsonRpcHandler, $logger);
|
||||
|
||||
// Create the transport layer using Symfony Console
|
||||
$transport = new Mcp\Server\Transport\Stdio\SymfonyConsoleTransport($input, $output);
|
||||
|
||||
// Start our application
|
||||
$sever->connect($transport);
|
||||
72
examples/cli/src/Builder.php
Normal file
72
examples/cli/src/Builder.php
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the official PHP MCP SDK.
|
||||
*
|
||||
* A collaboration between Symfony and the PHP Foundation.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App;
|
||||
|
||||
use Mcp\Capability\PromptChain;
|
||||
use Mcp\Capability\ResourceChain;
|
||||
use Mcp\Capability\ToolChain;
|
||||
use Mcp\Server\NotificationHandler\InitializedHandler;
|
||||
use Mcp\Server\NotificationHandlerInterface;
|
||||
use Mcp\Server\RequestHandler\InitializeHandler;
|
||||
use Mcp\Server\RequestHandler\PingHandler;
|
||||
use Mcp\Server\RequestHandler\PromptGetHandler;
|
||||
use Mcp\Server\RequestHandler\PromptListHandler;
|
||||
use Mcp\Server\RequestHandler\ResourceListHandler;
|
||||
use Mcp\Server\RequestHandler\ResourceReadHandler;
|
||||
use Mcp\Server\RequestHandler\ToolCallHandler;
|
||||
use Mcp\Server\RequestHandler\ToolListHandler;
|
||||
use Mcp\Server\RequestHandlerInterface;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
class Builder
|
||||
{
|
||||
/**
|
||||
* @return list<RequestHandlerInterface>
|
||||
*/
|
||||
public static function buildRequestHandlers(): array
|
||||
{
|
||||
$promptManager = new PromptChain([
|
||||
new ExamplePrompt(),
|
||||
]);
|
||||
|
||||
$resourceManager = new ResourceChain([
|
||||
new ExampleResource(),
|
||||
]);
|
||||
|
||||
$toolManager = new ToolChain([
|
||||
new ExampleTool(),
|
||||
]);
|
||||
|
||||
return [
|
||||
new InitializeHandler(),
|
||||
new PingHandler(),
|
||||
new PromptListHandler($promptManager),
|
||||
new PromptGetHandler($promptManager),
|
||||
new ResourceListHandler($resourceManager),
|
||||
new ResourceReadHandler($resourceManager),
|
||||
new ToolCallHandler($toolManager),
|
||||
new ToolListHandler($toolManager),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<NotificationHandlerInterface>
|
||||
*/
|
||||
public static function buildNotificationHandlers(): array
|
||||
{
|
||||
return [
|
||||
new InitializedHandler(),
|
||||
];
|
||||
}
|
||||
}
|
||||
58
examples/cli/src/ExamplePrompt.php
Normal file
58
examples/cli/src/ExamplePrompt.php
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the official PHP MCP SDK.
|
||||
*
|
||||
* A collaboration between Symfony and the PHP Foundation.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App;
|
||||
|
||||
use Mcp\Capability\Prompt\MetadataInterface;
|
||||
use Mcp\Capability\Prompt\PromptGet;
|
||||
use Mcp\Capability\Prompt\PromptGetResult;
|
||||
use Mcp\Capability\Prompt\PromptGetResultMessages;
|
||||
use Mcp\Capability\Prompt\PromptGetterInterface;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
class ExamplePrompt implements MetadataInterface, PromptGetterInterface
|
||||
{
|
||||
public function get(PromptGet $input): PromptGetResult
|
||||
{
|
||||
$firstName = $input->arguments['first name'] ?? null;
|
||||
|
||||
return new PromptGetResult(
|
||||
$this->getDescription(),
|
||||
[new PromptGetResultMessages(
|
||||
'user',
|
||||
\sprintf('Hello %s', $firstName ?? 'World')
|
||||
)]
|
||||
);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Greet';
|
||||
}
|
||||
|
||||
public function getDescription(): ?string
|
||||
{
|
||||
return 'Greet a person with a nice message';
|
||||
}
|
||||
|
||||
public function getArguments(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'name' => 'first name',
|
||||
'description' => 'The name of the person to greet',
|
||||
'required' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
56
examples/cli/src/ExampleResource.php
Normal file
56
examples/cli/src/ExampleResource.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the official PHP MCP SDK.
|
||||
*
|
||||
* A collaboration between Symfony and the PHP Foundation.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App;
|
||||
|
||||
use Mcp\Capability\Resource\MetadataInterface;
|
||||
use Mcp\Capability\Resource\ResourceRead;
|
||||
use Mcp\Capability\Resource\ResourceReaderInterface;
|
||||
use Mcp\Capability\Resource\ResourceReadResult;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
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';
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'My resource';
|
||||
}
|
||||
|
||||
public function getDescription(): ?string
|
||||
{
|
||||
return 'This is just an example';
|
||||
}
|
||||
|
||||
public function getMimeType(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getSize(): ?int
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
57
examples/cli/src/ExampleTool.php
Normal file
57
examples/cli/src/ExampleTool.php
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the official PHP MCP SDK.
|
||||
*
|
||||
* A collaboration between Symfony and the PHP Foundation.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App;
|
||||
|
||||
use Mcp\Capability\Tool\MetadataInterface;
|
||||
use Mcp\Capability\Tool\ToolCall;
|
||||
use Mcp\Capability\Tool\ToolCallResult;
|
||||
use Mcp\Capability\Tool\ToolExecutorInterface;
|
||||
|
||||
/**
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
class ExampleTool implements MetadataInterface, ToolExecutorInterface
|
||||
{
|
||||
public function call(ToolCall $input): ToolCallResult
|
||||
{
|
||||
$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
|
||||
{
|
||||
return 'Current time';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Returns the current time in UTC';
|
||||
}
|
||||
|
||||
public function getInputSchema(): array
|
||||
{
|
||||
return [
|
||||
'type' => 'object',
|
||||
'properties' => [
|
||||
'format' => [
|
||||
'type' => 'string',
|
||||
'description' => 'The format of the time, e.g. "Y-m-d H:i:s"',
|
||||
'default' => 'Y-m-d H:i:s',
|
||||
],
|
||||
],
|
||||
'required' => [],
|
||||
];
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue