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": {
|
||||
"psr-4": {
|
||||
"Mcp\\Example\\CachedDiscovery\\": "examples/cached-discovery/",
|
||||
"Mcp\\Example\\ClientCommunication\\": "examples/client-communication/",
|
||||
"Mcp\\Example\\CombinedRegistration\\": "examples/combined-registration/",
|
||||
"Mcp\\Example\\ComplexToolSchema\\": "examples/complex-tool-schema/",
|
||||
"Mcp\\Example\\CustomDependencies\\": "examples/custom-dependencies/",
|
||||
"Mcp\\Example\\CustomMethodHandlers\\": "examples/custom-method-handlers/",
|
||||
"Mcp\\Example\\DiscoveryCalculator\\": "examples/discovery-calculator/",
|
||||
"Mcp\\Example\\DiscoveryUserProfile\\": "examples/discovery-userprofile/",
|
||||
"Mcp\\Example\\EnvVariables\\": "examples/env-variables/",
|
||||
"Mcp\\Example\\ExplicitRegistration\\": "examples/explicit-registration/",
|
||||
"Mcp\\Example\\SchemaShowcase\\": "examples/schema-showcase/",
|
||||
"Mcp\\Example\\ClientLogging\\": "examples/client-logging/",
|
||||
"Mcp\\Example\\Server\\CachedDiscovery\\": "examples/server/cached-discovery/",
|
||||
"Mcp\\Example\\Server\\ClientCommunication\\": "examples/server/client-communication/",
|
||||
"Mcp\\Example\\Server\\CombinedRegistration\\": "examples/server/combined-registration/",
|
||||
"Mcp\\Example\\Server\\ComplexToolSchema\\": "examples/server/complex-tool-schema/",
|
||||
"Mcp\\Example\\Server\\CustomDependencies\\": "examples/server/custom-dependencies/",
|
||||
"Mcp\\Example\\Server\\CustomMethodHandlers\\": "examples/server/custom-method-handlers/",
|
||||
"Mcp\\Example\\Server\\DiscoveryCalculator\\": "examples/server/discovery-calculator/",
|
||||
"Mcp\\Example\\Server\\DiscoveryUserProfile\\": "examples/server/discovery-userprofile/",
|
||||
"Mcp\\Example\\Server\\EnvVariables\\": "examples/server/env-variables/",
|
||||
"Mcp\\Example\\Server\\ExplicitRegistration\\": "examples/server/explicit-registration/",
|
||||
"Mcp\\Example\\Server\\SchemaShowcase\\": "examples/server/schema-showcase/",
|
||||
"Mcp\\Example\\Server\\ClientLogging\\": "examples/server/client-logging/",
|
||||
"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