Adopt server examples

This commit is contained in:
Christopher Hertel 2025-08-17 02:46:01 +02:00
commit 2ef051a355
No known key found for this signature in database
GPG key ID: 111F6BFF7B6B5AA3
8 changed files with 3639 additions and 756 deletions

View file

@ -0,0 +1,27 @@
# Standalone example app with CLI
This is just for testing and debugging purposes. Different from the other examples, this one does not use the same
autoloader, but installs the SDK via path repository and therefore has mostly decoupled dependencies.
Install dependencies:
```bash
cd /path/to/your/project/examples/09-standalone-cli
composer update
```
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
```

View 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": "../../" }
]
}

View 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" }}}
]

View file

@ -0,0 +1,37 @@
<?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
$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\JsonRpc\Handler(
Mcp\JsonRpc\MessageFactory::make(),
App\Builder::buildMethodHandlers(),
$logger
);
// Set up the server
$sever = new Mcp\Server($jsonRpcHandler, $logger);
// Create the transport layer using Stdio
$transport = new Mcp\Server\Transport\StdioTransport(logger: $logger);
// Start our application
$sever->connect($transport);

View file

@ -0,0 +1,55 @@
<?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\MethodHandlerInterface;
use Mcp\Server\NotificationHandler;
use Mcp\Server\RequestHandler;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
class Builder
{
/**
* @return list<MethodHandlerInterface>
*/
public static function buildMethodHandlers(): array
{
$promptManager = new PromptChain([
new ExamplePrompt(),
]);
$resourceManager = new ResourceChain([
new ExampleResource(),
]);
$toolManager = new ToolChain([
new ExampleTool(),
]);
return [
new NotificationHandler\InitializedHandler(),
new RequestHandler\InitializeHandler(),
new RequestHandler\PingHandler(),
new RequestHandler\ListPromptsHandler($promptManager),
new RequestHandler\GetPromptHandler($promptManager),
new RequestHandler\ListResourcesHandler($resourceManager),
new RequestHandler\ReadResourceHandler($resourceManager),
new RequestHandler\CallToolHandler($toolManager),
new RequestHandler\ListToolsHandler($toolManager),
];
}
}

View file

@ -0,0 +1,60 @@
<?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\PromptGetterInterface;
use Mcp\Schema\Content\PromptMessage;
use Mcp\Schema\Content\TextContent;
use Mcp\Schema\Enum\Role;
use Mcp\Schema\Request\GetPromptRequest;
use Mcp\Schema\Result\GetPromptResult;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
class ExamplePrompt implements MetadataInterface, PromptGetterInterface
{
public function get(GetPromptRequest $request): GetPromptResult
{
$firstName = $request->arguments['firstName'] ?? null;
return new GetPromptResult(
[new PromptMessage(
Role::User,
new TextContent(\sprintf('Hello %s', $firstName ?? 'World')),
)],
$this->getDescription(),
);
}
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,
],
];
}
}

View 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\ResourceReaderInterface;
use Mcp\Schema\Content\TextResourceContents;
use Mcp\Schema\Request\ReadResourceRequest;
use Mcp\Schema\Result\ReadResourceResult;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
class ExampleResource implements MetadataInterface, ResourceReaderInterface
{
public function read(ReadResourceRequest $request): ReadResourceResult
{
return new ReadResourceResult([
new TextResourceContents($this->getUri(), null, 'Content of My Resource'),
]);
}
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;
}
}

View file

@ -0,0 +1,60 @@
<?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\ToolExecutorInterface;
use Mcp\Schema\Content\TextContent;
use Mcp\Schema\Request\CallToolRequest;
use Mcp\Schema\Result\CallToolResult;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
*/
class ExampleTool implements MetadataInterface, ToolExecutorInterface
{
public function call(CallToolRequest $request): CallToolResult
{
$format = $request->arguments['format'] ?? 'Y-m-d H:i:s';
return new CallToolResult([
new TextContent(
(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' => [],
];
}
}