Move examples to server directory (#198)
* move examples to server directory This will prepare for the client implementation in https://github.com/modelcontextprotocol/php-sdk/pull/192 * fix move bootstrap too
This commit is contained in:
parent
c3244fcdd4
commit
6dac6f3b4a
46 changed files with 56 additions and 56 deletions
|
|
@ -51,18 +51,18 @@
|
||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Mcp\\Example\\CachedDiscovery\\": "examples/cached-discovery/",
|
"Mcp\\Example\\Server\\CachedDiscovery\\": "examples/server/cached-discovery/",
|
||||||
"Mcp\\Example\\ClientCommunication\\": "examples/client-communication/",
|
"Mcp\\Example\\Server\\ClientCommunication\\": "examples/server/client-communication/",
|
||||||
"Mcp\\Example\\CombinedRegistration\\": "examples/combined-registration/",
|
"Mcp\\Example\\Server\\CombinedRegistration\\": "examples/server/combined-registration/",
|
||||||
"Mcp\\Example\\ComplexToolSchema\\": "examples/complex-tool-schema/",
|
"Mcp\\Example\\Server\\ComplexToolSchema\\": "examples/server/complex-tool-schema/",
|
||||||
"Mcp\\Example\\CustomDependencies\\": "examples/custom-dependencies/",
|
"Mcp\\Example\\Server\\CustomDependencies\\": "examples/server/custom-dependencies/",
|
||||||
"Mcp\\Example\\CustomMethodHandlers\\": "examples/custom-method-handlers/",
|
"Mcp\\Example\\Server\\CustomMethodHandlers\\": "examples/server/custom-method-handlers/",
|
||||||
"Mcp\\Example\\DiscoveryCalculator\\": "examples/discovery-calculator/",
|
"Mcp\\Example\\Server\\DiscoveryCalculator\\": "examples/server/discovery-calculator/",
|
||||||
"Mcp\\Example\\DiscoveryUserProfile\\": "examples/discovery-userprofile/",
|
"Mcp\\Example\\Server\\DiscoveryUserProfile\\": "examples/server/discovery-userprofile/",
|
||||||
"Mcp\\Example\\EnvVariables\\": "examples/env-variables/",
|
"Mcp\\Example\\Server\\EnvVariables\\": "examples/server/env-variables/",
|
||||||
"Mcp\\Example\\ExplicitRegistration\\": "examples/explicit-registration/",
|
"Mcp\\Example\\Server\\ExplicitRegistration\\": "examples/server/explicit-registration/",
|
||||||
"Mcp\\Example\\SchemaShowcase\\": "examples/schema-showcase/",
|
"Mcp\\Example\\Server\\SchemaShowcase\\": "examples/server/schema-showcase/",
|
||||||
"Mcp\\Example\\ClientLogging\\": "examples/client-logging/",
|
"Mcp\\Example\\Server\\ClientLogging\\": "examples/server/client-logging/",
|
||||||
"Mcp\\Tests\\": "tests/"
|
"Mcp\\Tests\\": "tests/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
34
examples/server/README.md
Normal file
34
examples/server/README.md
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
# MCP SDK Examples
|
||||||
|
|
||||||
|
This directory contains various examples of how to use the PHP MCP SDK.
|
||||||
|
|
||||||
|
You can run the examples with the dependencies already installed in the root directory of the SDK.
|
||||||
|
The bootstrapping of the example will choose the used transport based on the SAPI you use.
|
||||||
|
|
||||||
|
For running an example, you execute the `server.php` like this:
|
||||||
|
```bash
|
||||||
|
# For using the STDIO transport:
|
||||||
|
php examples/discovery-calculator/server.php
|
||||||
|
|
||||||
|
# For using the Streamable HTTP transport:
|
||||||
|
php -S localhost:8000 examples/discovery-userprofile/server.php
|
||||||
|
```
|
||||||
|
|
||||||
|
You will see debug outputs to help you understand what is happening.
|
||||||
|
|
||||||
|
Run with Inspector:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx @modelcontextprotocol/inspector php examples/discovery-calculator/server.php
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
You can enable debug output by setting the `DEBUG` environment variable to `1`, and additionally log to a file by
|
||||||
|
setting the `FILE_LOG` environment variable to `1` as well. A `dev.log` file gets written within the example's
|
||||||
|
directory.
|
||||||
|
|
||||||
|
With the Inspector you can set the environment variables like this:
|
||||||
|
```bash
|
||||||
|
npx @modelcontextprotocol/inspector -e DEBUG=1 -e FILE_LOG=1 php examples/discovery-calculator/server.php
|
||||||
|
```
|
||||||
88
examples/server/bootstrap.php
Normal file
88
examples/server/bootstrap.php
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
<?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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Http\Discovery\Psr17Factory;
|
||||||
|
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
|
||||||
|
use Mcp\Capability\Registry\Container;
|
||||||
|
use Mcp\Server\Transport\StdioTransport;
|
||||||
|
use Mcp\Server\Transport\StreamableHttpTransport;
|
||||||
|
use Mcp\Server\Transport\TransportInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Log\AbstractLogger;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 2).'/vendor/autoload.php';
|
||||||
|
|
||||||
|
set_exception_handler(function (Throwable $t): never {
|
||||||
|
logger()->critical('Uncaught exception: '.$t->getMessage(), ['exception' => $t]);
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return TransportInterface<int>|TransportInterface<ResponseInterface>
|
||||||
|
*/
|
||||||
|
function transport(): TransportInterface
|
||||||
|
{
|
||||||
|
if ('cli' === \PHP_SAPI) {
|
||||||
|
return new StdioTransport(logger: logger());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StreamableHttpTransport(
|
||||||
|
(new Psr17Factory())->createServerRequestFromGlobals(),
|
||||||
|
logger: logger(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function shutdown(ResponseInterface|int $result): never
|
||||||
|
{
|
||||||
|
if ('cli' === \PHP_SAPI) {
|
||||||
|
exit($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
(new SapiEmitter())->emit($result);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function logger(): LoggerInterface
|
||||||
|
{
|
||||||
|
return new class extends AbstractLogger {
|
||||||
|
public function log($level, Stringable|string $message, array $context = []): void
|
||||||
|
{
|
||||||
|
$debug = $_SERVER['DEBUG'] ?? false;
|
||||||
|
|
||||||
|
if (!$debug && 'debug' === $level) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$logMessage = sprintf(
|
||||||
|
"[%s] %s %s\n",
|
||||||
|
strtoupper($level),
|
||||||
|
$message,
|
||||||
|
([] === $context || !$debug) ? '' : json_encode($context),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (($_SERVER['FILE_LOG'] ?? false) || !defined('STDERR')) {
|
||||||
|
file_put_contents('dev.log', $logMessage, \FILE_APPEND);
|
||||||
|
} else {
|
||||||
|
fwrite(\STDERR, $logMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function container(): Container
|
||||||
|
{
|
||||||
|
$container = new Container();
|
||||||
|
$container->set(LoggerInterface::class, logger());
|
||||||
|
|
||||||
|
return $container;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 Mcp\Example\Server\CachedDiscovery;
|
||||||
|
|
||||||
|
use Mcp\Capability\Attribute\McpTool;
|
||||||
|
use Mcp\Exception\ToolCallException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example MCP elements for demonstrating cached discovery.
|
||||||
|
*
|
||||||
|
* This class contains simple calculator tools that will be discovered
|
||||||
|
* and cached for improved performance on subsequent server starts.
|
||||||
|
*/
|
||||||
|
class CachedCalculatorElements
|
||||||
|
{
|
||||||
|
#[McpTool(name: 'add_numbers')]
|
||||||
|
public function add(int $a, int $b): int
|
||||||
|
{
|
||||||
|
return $a + $b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[McpTool(name: 'multiply_numbers')]
|
||||||
|
public function multiply(int $a, int $b): int
|
||||||
|
{
|
||||||
|
return $a * $b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[McpTool(name: 'divide_numbers')]
|
||||||
|
public function divide(int $a, int $b): float
|
||||||
|
{
|
||||||
|
if (0 === $b) {
|
||||||
|
throw new ToolCallException('Division by zero is not allowed');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $a / $b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[McpTool(name: 'power')]
|
||||||
|
public function power(int $base, int $exponent): int
|
||||||
|
{
|
||||||
|
return (int) $base ** $exponent;
|
||||||
|
}
|
||||||
|
}
|
||||||
37
examples/server/cached-discovery/server.php
Normal file
37
examples/server/cached-discovery/server.php
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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_once dirname(__DIR__).'/bootstrap.php';
|
||||||
|
chdir(__DIR__);
|
||||||
|
|
||||||
|
use Mcp\Server;
|
||||||
|
use Mcp\Server\Session\FileSessionStore;
|
||||||
|
use Symfony\Component\Cache\Adapter\PhpFilesAdapter;
|
||||||
|
use Symfony\Component\Cache\Psr16Cache;
|
||||||
|
|
||||||
|
logger()->info('Starting MCP Cached Discovery Calculator Server...');
|
||||||
|
|
||||||
|
$server = Server::builder()
|
||||||
|
->setServerInfo('Cached Discovery Calculator', '1.0.0', 'Calculator with cached discovery for better performance.')
|
||||||
|
->setContainer(container())
|
||||||
|
->setSession(new FileSessionStore(__DIR__.'/sessions'))
|
||||||
|
->setLogger(logger())
|
||||||
|
->setDiscovery(__DIR__, cache: new Psr16Cache(new PhpFilesAdapter(directory: __DIR__.'/cache')))
|
||||||
|
->build();
|
||||||
|
|
||||||
|
$result = $server->run(transport());
|
||||||
|
|
||||||
|
logger()->info('Server listener stopped gracefully.', ['result' => $result]);
|
||||||
|
|
||||||
|
shutdown($result);
|
||||||
69
examples/server/client-communication/ClientAwareService.php
Normal file
69
examples/server/client-communication/ClientAwareService.php
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
<?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 Mcp\Example\Server\ClientCommunication;
|
||||||
|
|
||||||
|
use Mcp\Capability\Attribute\McpTool;
|
||||||
|
use Mcp\Schema\Content\TextContent;
|
||||||
|
use Mcp\Schema\Enum\LoggingLevel;
|
||||||
|
use Mcp\Server\RequestContext;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
final class ClientAwareService
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly LoggerInterface $logger,
|
||||||
|
) {
|
||||||
|
$this->logger->info('SamplingTool instantiated for sampling example.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{incident: string, recommended_actions: string, model: string}
|
||||||
|
*/
|
||||||
|
#[McpTool('coordinate_incident_response', 'Coordinate an incident response with logging, progress, and sampling.')]
|
||||||
|
public function coordinateIncident(RequestContext $context, string $incidentTitle): array
|
||||||
|
{
|
||||||
|
$clientGateway = $context->getClientGateway();
|
||||||
|
$clientGateway->log(LoggingLevel::Warning, \sprintf('Incident triage started: %s', $incidentTitle));
|
||||||
|
|
||||||
|
$steps = [
|
||||||
|
'Collecting telemetry',
|
||||||
|
'Assessing scope',
|
||||||
|
'Coordinating responders',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($steps as $index => $step) {
|
||||||
|
$progress = ($index + 1) / \count($steps);
|
||||||
|
|
||||||
|
$clientGateway->progress($progress, 1, $step);
|
||||||
|
|
||||||
|
usleep(180_000); // Simulate work being done
|
||||||
|
}
|
||||||
|
|
||||||
|
$prompt = \sprintf(
|
||||||
|
'Provide a concise response strategy for incident "%s" based on the steps completed: %s.',
|
||||||
|
$incidentTitle,
|
||||||
|
implode(', ', $steps)
|
||||||
|
);
|
||||||
|
|
||||||
|
$result = $clientGateway->sample($prompt, 350, 90, ['temperature' => 0.5]);
|
||||||
|
|
||||||
|
$recommendation = $result->content instanceof TextContent ? trim((string) $result->content->text) : '';
|
||||||
|
|
||||||
|
$clientGateway->log(LoggingLevel::Info, \sprintf('Incident triage completed for %s', $incidentTitle));
|
||||||
|
|
||||||
|
return [
|
||||||
|
'incident' => $incidentTitle,
|
||||||
|
'recommended_actions' => $recommendation,
|
||||||
|
'model' => $result->model,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
63
examples/server/client-communication/server.php
Normal file
63
examples/server/client-communication/server.php
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
#!/usr/bin/env php
|
||||||
|
<?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_once dirname(__DIR__).'/bootstrap.php';
|
||||||
|
chdir(__DIR__);
|
||||||
|
|
||||||
|
use Mcp\Schema\Enum\LoggingLevel;
|
||||||
|
use Mcp\Schema\ServerCapabilities;
|
||||||
|
use Mcp\Server;
|
||||||
|
use Mcp\Server\RequestContext;
|
||||||
|
use Mcp\Server\Session\FileSessionStore;
|
||||||
|
|
||||||
|
$server = Server::builder()
|
||||||
|
->setServerInfo('Client Communication Demo', '1.0.0')
|
||||||
|
->setLogger(logger())
|
||||||
|
->setContainer(container())
|
||||||
|
->setSession(new FileSessionStore(__DIR__.'/sessions'))
|
||||||
|
->setCapabilities(new ServerCapabilities(logging: true, tools: true))
|
||||||
|
->setDiscovery(__DIR__)
|
||||||
|
->addTool(
|
||||||
|
function (RequestContext $context, string $dataset): array {
|
||||||
|
$client = $context->getClientGateway();
|
||||||
|
$client->log(LoggingLevel::Info, sprintf('Running quality checks on dataset "%s"', $dataset));
|
||||||
|
|
||||||
|
$tasks = [
|
||||||
|
'Validating schema',
|
||||||
|
'Scanning for anomalies',
|
||||||
|
'Reviewing statistical summary',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($tasks as $index => $task) {
|
||||||
|
$progress = ($index + 1) / count($tasks);
|
||||||
|
|
||||||
|
$client->progress(progress: $progress, total: 1, message: $task);
|
||||||
|
|
||||||
|
usleep(140_000); // Simulate work being done
|
||||||
|
}
|
||||||
|
|
||||||
|
$client->log(LoggingLevel::Info, sprintf('Dataset "%s" passed automated checks.', $dataset));
|
||||||
|
|
||||||
|
return [
|
||||||
|
'dataset' => $dataset,
|
||||||
|
'status' => 'passed',
|
||||||
|
'notes' => 'No significant integrity issues detected during automated checks.',
|
||||||
|
];
|
||||||
|
},
|
||||||
|
name: 'run_dataset_quality_checks',
|
||||||
|
description: 'Perform dataset quality checks with progress updates and logging.'
|
||||||
|
)
|
||||||
|
->build();
|
||||||
|
|
||||||
|
$result = $server->run(transport());
|
||||||
|
|
||||||
|
shutdown($result);
|
||||||
81
examples/server/client-logging/LoggingShowcaseHandlers.php
Normal file
81
examples/server/client-logging/LoggingShowcaseHandlers.php
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
<?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 Mcp\Example\Server\ClientLogging;
|
||||||
|
|
||||||
|
use Mcp\Capability\Attribute\McpTool;
|
||||||
|
use Mcp\Capability\Logger\ClientLogger;
|
||||||
|
use Mcp\Server\RequestContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example handlers showcasing auto-injected MCP logging capabilities.
|
||||||
|
*
|
||||||
|
* This demonstrates how handlers can receive ClientLogger automatically
|
||||||
|
* without any manual configuration - just declare it as a parameter!
|
||||||
|
*/
|
||||||
|
final class LoggingShowcaseHandlers
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Tool that demonstrates different logging levels.
|
||||||
|
*
|
||||||
|
* @param string $message The message to log
|
||||||
|
* @param string $level The logging level (debug, info, warning, error)
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
#[McpTool(name: 'log_message', description: 'Demonstrates MCP logging with different levels')]
|
||||||
|
public function logMessage(RequestContext $context, string $message, string $level): array
|
||||||
|
{
|
||||||
|
$logger = $context->getClientLogger();
|
||||||
|
$logger->info('🚀 Starting log_message tool', [
|
||||||
|
'requested_level' => $level,
|
||||||
|
'message_length' => \strlen($message),
|
||||||
|
]);
|
||||||
|
|
||||||
|
switch (strtolower($level)) {
|
||||||
|
case 'debug':
|
||||||
|
$logger->debug("Debug: $message", ['tool' => 'log_message']);
|
||||||
|
break;
|
||||||
|
case 'info':
|
||||||
|
$logger->info("Info: $message", ['tool' => 'log_message']);
|
||||||
|
break;
|
||||||
|
case 'notice':
|
||||||
|
$logger->notice("Notice: $message", ['tool' => 'log_message']);
|
||||||
|
break;
|
||||||
|
case 'warning':
|
||||||
|
$logger->warning("Warning: $message", ['tool' => 'log_message']);
|
||||||
|
break;
|
||||||
|
case 'error':
|
||||||
|
$logger->error("Error: $message", ['tool' => 'log_message']);
|
||||||
|
break;
|
||||||
|
case 'critical':
|
||||||
|
$logger->critical("Critical: $message", ['tool' => 'log_message']);
|
||||||
|
break;
|
||||||
|
case 'alert':
|
||||||
|
$logger->alert("Alert: $message", ['tool' => 'log_message']);
|
||||||
|
break;
|
||||||
|
case 'emergency':
|
||||||
|
$logger->emergency("Emergency: $message", ['tool' => 'log_message']);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$logger->warning("Unknown level '$level', defaulting to info");
|
||||||
|
$logger->info("Info: $message", ['tool' => 'log_message']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$logger->debug('log_message tool completed successfully');
|
||||||
|
|
||||||
|
return [
|
||||||
|
'message' => "Logged message with level: $level",
|
||||||
|
'logged_at' => date('Y-m-d H:i:s'),
|
||||||
|
'level_used' => $level,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
29
examples/server/client-logging/server.php
Normal file
29
examples/server/client-logging/server.php
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/usr/bin/env php
|
||||||
|
<?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_once dirname(__DIR__).'/bootstrap.php';
|
||||||
|
chdir(__DIR__);
|
||||||
|
|
||||||
|
use Mcp\Server;
|
||||||
|
|
||||||
|
$server = Server::builder()
|
||||||
|
->setServerInfo('Client Logging', '1.0.0', 'Demonstration of MCP logging in capability handlers.')
|
||||||
|
->setContainer(container())
|
||||||
|
->setLogger(logger())
|
||||||
|
->setDiscovery(__DIR__)
|
||||||
|
->build();
|
||||||
|
|
||||||
|
$result = $server->run(transport());
|
||||||
|
|
||||||
|
logger()->info('Server listener stopped gracefully.', ['result' => $result]);
|
||||||
|
|
||||||
|
shutdown($result);
|
||||||
41
examples/server/combined-registration/DiscoveredElements.php
Normal file
41
examples/server/combined-registration/DiscoveredElements.php
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?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 Mcp\Example\Server\CombinedRegistration;
|
||||||
|
|
||||||
|
use Mcp\Capability\Attribute\McpResource;
|
||||||
|
use Mcp\Capability\Attribute\McpTool;
|
||||||
|
|
||||||
|
final class DiscoveredElements
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A tool discovered via attributes.
|
||||||
|
*
|
||||||
|
* @return string a status message
|
||||||
|
*/
|
||||||
|
#[McpTool(name: 'discovered_status_check')]
|
||||||
|
public function checkSystemStatus(): string
|
||||||
|
{
|
||||||
|
return 'System status: OK (discovered)';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A resource discovered via attributes.
|
||||||
|
* This will be overridden by a manual registration with the same URI.
|
||||||
|
*
|
||||||
|
* @return string content
|
||||||
|
*/
|
||||||
|
#[McpResource(uri: 'config://priority', name: 'priority_config_discovered')]
|
||||||
|
public function getPriorityConfigDiscovered(): string
|
||||||
|
{
|
||||||
|
return 'Discovered Priority Config: Low';
|
||||||
|
}
|
||||||
|
}
|
||||||
48
examples/server/combined-registration/ManualHandlers.php
Normal file
48
examples/server/combined-registration/ManualHandlers.php
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?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 Mcp\Example\Server\CombinedRegistration;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
final class ManualHandlers
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly LoggerInterface $logger,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A manually registered tool.
|
||||||
|
*
|
||||||
|
* @param string $user the user to greet
|
||||||
|
*
|
||||||
|
* @return string greeting
|
||||||
|
*/
|
||||||
|
public function manualGreeter(string $user): string
|
||||||
|
{
|
||||||
|
$this->logger->info("Manual tool 'manual_greeter' called for {$user}");
|
||||||
|
|
||||||
|
return "Hello {$user}, from manual registration!";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manually registered resource that overrides a discovered one.
|
||||||
|
*
|
||||||
|
* @return string content
|
||||||
|
*/
|
||||||
|
public function getPriorityConfigManual(): string
|
||||||
|
{
|
||||||
|
$this->logger->info("Manual resource 'config://priority' read.");
|
||||||
|
|
||||||
|
return 'Manual Priority Config: HIGH (overrides discovered)';
|
||||||
|
}
|
||||||
|
}
|
||||||
36
examples/server/combined-registration/server.php
Normal file
36
examples/server/combined-registration/server.php
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/usr/bin/env php
|
||||||
|
<?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_once dirname(__DIR__).'/bootstrap.php';
|
||||||
|
chdir(__DIR__);
|
||||||
|
|
||||||
|
use Mcp\Example\Server\CombinedRegistration\ManualHandlers;
|
||||||
|
use Mcp\Server;
|
||||||
|
use Mcp\Server\Session\FileSessionStore;
|
||||||
|
|
||||||
|
$server = Server::builder()
|
||||||
|
->setServerInfo('Combined HTTP Server', '1.0.0')
|
||||||
|
->setLogger(logger())
|
||||||
|
->setContainer(container())
|
||||||
|
->setSession(new FileSessionStore(__DIR__.'/sessions'))
|
||||||
|
->setDiscovery(__DIR__)
|
||||||
|
->addTool([ManualHandlers::class, 'manualGreeter'])
|
||||||
|
->addResource(
|
||||||
|
[ManualHandlers::class, 'getPriorityConfigManual'],
|
||||||
|
'config://priority',
|
||||||
|
'priority_config_manual',
|
||||||
|
)
|
||||||
|
->build();
|
||||||
|
|
||||||
|
$response = $server->run(transport());
|
||||||
|
|
||||||
|
shutdown($response);
|
||||||
72
examples/server/complex-tool-schema/McpEventScheduler.php
Normal file
72
examples/server/complex-tool-schema/McpEventScheduler.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 Mcp\Example\Server\ComplexToolSchema;
|
||||||
|
|
||||||
|
use Mcp\Capability\Attribute\McpTool;
|
||||||
|
use Mcp\Example\Server\ComplexToolSchema\Model\EventPriority;
|
||||||
|
use Mcp\Example\Server\ComplexToolSchema\Model\EventType;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
final class McpEventScheduler
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly LoggerInterface $logger,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a new event.
|
||||||
|
* The inputSchema for this tool will reflect all parameter types and defaults.
|
||||||
|
*
|
||||||
|
* @param string $title the title of the event
|
||||||
|
* @param string $date the date of the event (YYYY-MM-DD)
|
||||||
|
* @param EventType $type the type of event
|
||||||
|
* @param string|null $time the time of the event (HH:MM), optional
|
||||||
|
* @param EventPriority $priority The priority of the event. Defaults to Normal.
|
||||||
|
* @param string[]|null $attendees an optional list of attendee email addresses
|
||||||
|
* @param bool $sendInvites send calendar invites to attendees? Defaults to true if attendees are provided
|
||||||
|
*
|
||||||
|
* @return array<string, mixed> confirmation of the scheduled event
|
||||||
|
*/
|
||||||
|
#[McpTool(name: 'schedule_event')]
|
||||||
|
public function scheduleEvent(
|
||||||
|
string $title,
|
||||||
|
string $date,
|
||||||
|
EventType $type,
|
||||||
|
?string $time = null,
|
||||||
|
EventPriority $priority = EventPriority::Normal,
|
||||||
|
?array $attendees = null,
|
||||||
|
bool $sendInvites = true,
|
||||||
|
): array {
|
||||||
|
$this->logger->info("Tool 'schedule_event' called", compact('title', 'date', 'type', 'time', 'priority', 'attendees', 'sendInvites'));
|
||||||
|
|
||||||
|
// Simulate scheduling logic
|
||||||
|
$eventDetails = [
|
||||||
|
'title' => $title,
|
||||||
|
'date' => $date,
|
||||||
|
'type' => $type->value, // Use enum value
|
||||||
|
'time' => $time ?? 'All day',
|
||||||
|
'priority' => $priority->name, // Use enum name
|
||||||
|
'attendees' => $attendees ?? [],
|
||||||
|
'invites_will_be_sent' => ($attendees && $sendInvites),
|
||||||
|
];
|
||||||
|
|
||||||
|
// In a real app, this would interact with a calendar service
|
||||||
|
$this->logger->info('Event scheduled', ['details' => $eventDetails]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'message' => \sprintf('Event "%s" scheduled successfully for "%s".', $title, $date),
|
||||||
|
'event_details' => $eventDetails,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
19
examples/server/complex-tool-schema/Model/EventPriority.php
Normal file
19
examples/server/complex-tool-schema/Model/EventPriority.php
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?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 Mcp\Example\Server\ComplexToolSchema\Model;
|
||||||
|
|
||||||
|
enum EventPriority: int
|
||||||
|
{
|
||||||
|
case Low = 0;
|
||||||
|
case Normal = 1;
|
||||||
|
case High = 2;
|
||||||
|
}
|
||||||
20
examples/server/complex-tool-schema/Model/EventType.php
Normal file
20
examples/server/complex-tool-schema/Model/EventType.php
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?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 Mcp\Example\Server\ComplexToolSchema\Model;
|
||||||
|
|
||||||
|
enum EventType: string
|
||||||
|
{
|
||||||
|
case Meeting = 'meeting';
|
||||||
|
case Reminder = 'reminder';
|
||||||
|
case Call = 'call';
|
||||||
|
case Other = 'other';
|
||||||
|
}
|
||||||
29
examples/server/complex-tool-schema/server.php
Normal file
29
examples/server/complex-tool-schema/server.php
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/usr/bin/env php
|
||||||
|
<?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_once dirname(__DIR__).'/bootstrap.php';
|
||||||
|
chdir(__DIR__);
|
||||||
|
|
||||||
|
use Mcp\Server;
|
||||||
|
use Mcp\Server\Session\FileSessionStore;
|
||||||
|
|
||||||
|
$server = Server::builder()
|
||||||
|
->setServerInfo('Event Scheduler Server', '1.0.0')
|
||||||
|
->setLogger(logger())
|
||||||
|
->setContainer(container())
|
||||||
|
->setSession(new FileSessionStore(__DIR__.'/sessions'))
|
||||||
|
->setDiscovery(__DIR__)
|
||||||
|
->build();
|
||||||
|
|
||||||
|
$response = $server->run(transport());
|
||||||
|
|
||||||
|
shutdown($response);
|
||||||
92
examples/server/custom-dependencies/McpTaskHandlers.php
Normal file
92
examples/server/custom-dependencies/McpTaskHandlers.php
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
<?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 Mcp\Example\Server\CustomDependencies;
|
||||||
|
|
||||||
|
use Mcp\Capability\Attribute\McpResource;
|
||||||
|
use Mcp\Capability\Attribute\McpTool;
|
||||||
|
use Mcp\Example\Server\CustomDependencies\Service\StatsServiceInterface;
|
||||||
|
use Mcp\Example\Server\CustomDependencies\Service\TaskRepositoryInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-import-type Task from TaskRepositoryInterface
|
||||||
|
*/
|
||||||
|
final class McpTaskHandlers
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly TaskRepositoryInterface $taskRepo,
|
||||||
|
private readonly StatsServiceInterface $statsService,
|
||||||
|
private readonly LoggerInterface $logger,
|
||||||
|
) {
|
||||||
|
$this->logger->info('McpTaskHandlers instantiated with dependencies.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new task for a given user.
|
||||||
|
*
|
||||||
|
* @param string $userId the ID of the user
|
||||||
|
* @param string $description the task description
|
||||||
|
*
|
||||||
|
* @return Task the created task details
|
||||||
|
*/
|
||||||
|
#[McpTool(name: 'add_task')]
|
||||||
|
public function addTask(string $userId, string $description): array
|
||||||
|
{
|
||||||
|
$this->logger->info("Tool 'add_task' invoked", ['userId' => $userId]);
|
||||||
|
|
||||||
|
return $this->taskRepo->addTask($userId, $description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists pending tasks for a specific user.
|
||||||
|
*
|
||||||
|
* @param string $userId the ID of the user
|
||||||
|
*
|
||||||
|
* @return Task[] a list of tasks
|
||||||
|
*/
|
||||||
|
#[McpTool(name: 'list_user_tasks')]
|
||||||
|
public function listUserTasks(string $userId): array
|
||||||
|
{
|
||||||
|
$this->logger->info("Tool 'list_user_tasks' invoked", ['userId' => $userId]);
|
||||||
|
|
||||||
|
return $this->taskRepo->getTasksForUser($userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks a task as complete.
|
||||||
|
*
|
||||||
|
* @param int $taskId the ID of the task to complete
|
||||||
|
*
|
||||||
|
* @return array<string, mixed> status of the operation
|
||||||
|
*/
|
||||||
|
#[McpTool(name: 'complete_task')]
|
||||||
|
public function completeTask(int $taskId): array
|
||||||
|
{
|
||||||
|
$this->logger->info("Tool 'complete_task' invoked", ['taskId' => $taskId]);
|
||||||
|
$success = $this->taskRepo->completeTask($taskId);
|
||||||
|
|
||||||
|
return ['success' => $success, 'message' => $success ? "Task {$taskId} completed." : "Task {$taskId} not found."];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides current system statistics.
|
||||||
|
*
|
||||||
|
* @return array<string, int> system statistics
|
||||||
|
*/
|
||||||
|
#[McpResource(uri: 'stats://system/overview', name: 'system_stats', mimeType: 'application/json')]
|
||||||
|
public function getSystemStatistics(): array
|
||||||
|
{
|
||||||
|
$this->logger->info("Resource 'stats://system/overview' invoked");
|
||||||
|
|
||||||
|
return $this->statsService->getSystemStats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 Mcp\Example\Server\CustomDependencies\Service;
|
||||||
|
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-import-type Task from TaskRepositoryInterface
|
||||||
|
*/
|
||||||
|
final class InMemoryTaskRepository implements TaskRepositoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array<int, Task>
|
||||||
|
*/
|
||||||
|
private array $tasks = [];
|
||||||
|
private int $nextTaskId = 1;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly LoggerInterface $logger,
|
||||||
|
) {
|
||||||
|
// Add some initial tasks
|
||||||
|
$this->addTask('user1', 'Buy groceries');
|
||||||
|
$this->addTask('user1', 'Write MCP example');
|
||||||
|
$this->addTask('user2', 'Review PR');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addTask(string $userId, string $description): array
|
||||||
|
{
|
||||||
|
$task = [
|
||||||
|
'id' => $this->nextTaskId++,
|
||||||
|
'userId' => $userId,
|
||||||
|
'description' => $description,
|
||||||
|
'completed' => false,
|
||||||
|
'createdAt' => date('c'),
|
||||||
|
];
|
||||||
|
$this->tasks[$task['id']] = $task;
|
||||||
|
$this->logger->info('Task added', ['id' => $task['id'], 'user' => $userId]);
|
||||||
|
|
||||||
|
return $task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTasksForUser(string $userId): array
|
||||||
|
{
|
||||||
|
return array_values(array_filter($this->tasks, fn ($task) => $task['userId'] === $userId && !$task['completed']));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllTasks(): array
|
||||||
|
{
|
||||||
|
return array_values($this->tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function completeTask(int $taskId): bool
|
||||||
|
{
|
||||||
|
if (isset($this->tasks[$taskId])) {
|
||||||
|
$this->tasks[$taskId]['completed'] = true;
|
||||||
|
$this->logger->info('Task completed', ['id' => $taskId]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?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 Mcp\Example\Server\CustomDependencies\Service;
|
||||||
|
|
||||||
|
interface StatsServiceInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return array<string, int>
|
||||||
|
*/
|
||||||
|
public function getSystemStats(): array;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?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 Mcp\Example\Server\CustomDependencies\Service;
|
||||||
|
|
||||||
|
final class SystemStatsService implements StatsServiceInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly TaskRepositoryInterface $taskRepository,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSystemStats(): array
|
||||||
|
{
|
||||||
|
$allTasks = $this->taskRepository->getAllTasks();
|
||||||
|
$completed = \count(array_filter($allTasks, fn ($task) => $task['completed']));
|
||||||
|
$pending = \count($allTasks) - $completed;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'total_tasks' => \count($allTasks),
|
||||||
|
'completed_tasks' => $completed,
|
||||||
|
'pending_tasks' => $pending,
|
||||||
|
'server_uptime_seconds' => time() - $_SERVER['REQUEST_TIME_FLOAT'], // Approx uptime for CLI script
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue