refactor: transport agnostic examples (#131)
This commit is contained in:
parent
fb3c1e65a6
commit
ff26b4ff61
32 changed files with 157 additions and 289 deletions
|
|
@ -3,14 +3,15 @@
|
|||
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 examples using STDIO transport
|
||||
php examples/stdio-discovery-calculator/server.php
|
||||
# For using the STDIO transport:
|
||||
php examples/discovery-calculator/server.php
|
||||
|
||||
# For examples using Streamable HTTP transport
|
||||
php -S localhost:8000 examples/http-discovery-userprofile/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.
|
||||
|
|
@ -18,7 +19,7 @@ You will see debug outputs to help you understand what is happening.
|
|||
Run with Inspector:
|
||||
|
||||
```bash
|
||||
npx @modelcontextprotocol/inspector php examples/stdio-discovery-calculator/server.php
|
||||
npx @modelcontextprotocol/inspector php examples/discovery-calculator/server.php
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
|
@ -29,5 +30,5 @@ 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/stdio-discovery-calculator/server.php
|
||||
npx @modelcontextprotocol/inspector -e DEBUG=1 -e FILE_LOG=1 php examples/discovery-calculator/server.php
|
||||
```
|
||||
|
|
|
|||
|
|
@ -9,7 +9,13 @@
|
|||
* 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;
|
||||
|
||||
|
|
@ -21,6 +27,31 @@ set_exception_handler(function (Throwable $t): never {
|
|||
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 {
|
||||
|
|
|
|||
54
examples/cached-discovery/CachedCalculatorElements.php
Normal file
54
examples/cached-discovery/CachedCalculatorElements.php
Normal file
|
|
@ -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\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/cached-discovery/server.php
Normal file
37
examples/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);
|
||||
71
examples/client-communication/ClientAwareService.php
Normal file
71
examples/client-communication/ClientAwareService.php
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<?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\ClientCommunication;
|
||||
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Mcp\Schema\Content\TextContent;
|
||||
use Mcp\Schema\Enum\LoggingLevel;
|
||||
use Mcp\Server\ClientAwareInterface;
|
||||
use Mcp\Server\ClientAwareTrait;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
final class ClientAwareService implements ClientAwareInterface
|
||||
{
|
||||
use ClientAwareTrait;
|
||||
|
||||
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(string $incidentTitle): array
|
||||
{
|
||||
$this->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);
|
||||
|
||||
$this->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 = $this->sample($prompt, 350, 90, ['temperature' => 0.5]);
|
||||
|
||||
$recommendation = $result->content instanceof TextContent ? trim((string) $result->content->text) : '';
|
||||
|
||||
$this->log(LoggingLevel::Info, \sprintf('Incident triage completed for %s', $incidentTitle));
|
||||
|
||||
return [
|
||||
'incident' => $incidentTitle,
|
||||
'recommended_actions' => $recommendation,
|
||||
'model' => $result->model,
|
||||
];
|
||||
}
|
||||
}
|
||||
62
examples/client-communication/server.php
Normal file
62
examples/client-communication/server.php
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#!/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\ClientGateway;
|
||||
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 (string $dataset, ClientGateway $client): array {
|
||||
$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);
|
||||
41
examples/combined-registration/DiscoveredElements.php
Normal file
41
examples/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\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/combined-registration/ManualHandlers.php
Normal file
48
examples/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\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/combined-registration/server.php
Normal file
36
examples/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\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/complex-tool-schema/McpEventScheduler.php
Normal file
72
examples/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\ComplexToolSchema;
|
||||
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Mcp\Example\ComplexToolSchema\Model\EventPriority;
|
||||
use Mcp\Example\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/complex-tool-schema/Model/EventPriority.php
Normal file
19
examples/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\ComplexToolSchema\Model;
|
||||
|
||||
enum EventPriority: int
|
||||
{
|
||||
case Low = 0;
|
||||
case Normal = 1;
|
||||
case High = 2;
|
||||
}
|
||||
20
examples/complex-tool-schema/Model/EventType.php
Normal file
20
examples/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\ComplexToolSchema\Model;
|
||||
|
||||
enum EventType: string
|
||||
{
|
||||
case Meeting = 'meeting';
|
||||
case Reminder = 'reminder';
|
||||
case Call = 'call';
|
||||
case Other = 'other';
|
||||
}
|
||||
29
examples/complex-tool-schema/server.php
Normal file
29
examples/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/custom-dependencies/McpTaskHandlers.php
Normal file
92
examples/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\CustomDependencies;
|
||||
|
||||
use Mcp\Capability\Attribute\McpResource;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Mcp\Example\CustomDependencies\Service\StatsServiceInterface;
|
||||
use Mcp\Example\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\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\CustomDependencies\Service;
|
||||
|
||||
interface StatsServiceInterface
|
||||
{
|
||||
/**
|
||||
* @return array<string, int>
|
||||
*/
|
||||
public function getSystemStats(): array;
|
||||
}
|
||||
34
examples/custom-dependencies/Service/SystemStatsService.php
Normal file
34
examples/custom-dependencies/Service/SystemStatsService.php
Normal file
|
|
@ -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\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
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?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\CustomDependencies\Service;
|
||||
|
||||
/**
|
||||
* @phpstan-type Task array{id: int, userId: string, description: string, completed: bool, createdAt: string}
|
||||
*/
|
||||
interface TaskRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* @return Task
|
||||
*/
|
||||
public function addTask(string $userId, string $description): array;
|
||||
|
||||
/**
|
||||
* @return Task[]
|
||||
*/
|
||||
public function getTasksForUser(string $userId): array;
|
||||
|
||||
/**
|
||||
* @return Task[]
|
||||
*/
|
||||
public function getAllTasks(): array;
|
||||
|
||||
public function completeTask(int $taskId): bool;
|
||||
}
|
||||
45
examples/custom-dependencies/server.php
Normal file
45
examples/custom-dependencies/server.php
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#!/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\CustomDependencies\Service\InMemoryTaskRepository;
|
||||
use Mcp\Example\CustomDependencies\Service\StatsServiceInterface;
|
||||
use Mcp\Example\CustomDependencies\Service\SystemStatsService;
|
||||
use Mcp\Example\CustomDependencies\Service\TaskRepositoryInterface;
|
||||
use Mcp\Server;
|
||||
use Mcp\Server\Session\FileSessionStore;
|
||||
|
||||
logger()->info('Starting MCP Custom Dependencies Server...');
|
||||
|
||||
$container = container();
|
||||
|
||||
$taskRepo = new InMemoryTaskRepository(logger());
|
||||
$container->set(TaskRepositoryInterface::class, $taskRepo);
|
||||
|
||||
$statsService = new SystemStatsService($taskRepo);
|
||||
$container->set(StatsServiceInterface::class, $statsService);
|
||||
|
||||
$server = Server::builder()
|
||||
->setServerInfo('Task Manager Server', '1.0.0')
|
||||
->setContainer($container)
|
||||
->setSession(new FileSessionStore(__DIR__.'/sessions'))
|
||||
->setLogger(logger())
|
||||
->setDiscovery(__DIR__)
|
||||
->build();
|
||||
|
||||
$result = $server->run(transport());
|
||||
|
||||
logger()->info('Server listener stopped gracefully.', ['result' => $result]);
|
||||
|
||||
shutdown($result);
|
||||
|
|
@ -18,9 +18,9 @@ use Mcp\Example\CustomMethodHandlers\ListToolsRequestHandler;
|
|||
use Mcp\Schema\ServerCapabilities;
|
||||
use Mcp\Schema\Tool;
|
||||
use Mcp\Server;
|
||||
use Mcp\Server\Transport\StdioTransport;
|
||||
use Mcp\Server\Session\FileSessionStore;
|
||||
|
||||
logger()->info('Starting MCP Custom Method Handlers (Stdio) Server...');
|
||||
logger()->info('Starting MCP Custom Method Handlers Server...');
|
||||
|
||||
$toolDefinitions = [
|
||||
'say_hello' => new Tool(
|
||||
|
|
@ -56,16 +56,15 @@ $capabilities = new ServerCapabilities(tools: true, resources: false, prompts: f
|
|||
|
||||
$server = Server::builder()
|
||||
->setServerInfo('Custom Handlers Server', '1.0.0')
|
||||
->setLogger(logger())
|
||||
->setContainer(container())
|
||||
->setSession(new FileSessionStore(__DIR__.'/sessions'))
|
||||
->setLogger(logger())
|
||||
->setCapabilities($capabilities)
|
||||
->addRequestHandlers([$listToolsHandler, $callToolHandler])
|
||||
->build();
|
||||
|
||||
$transport = new StdioTransport(logger: logger());
|
||||
|
||||
$result = $server->run($transport);
|
||||
$result = $server->run(transport());
|
||||
|
||||
logger()->info('Server listener stopped gracefully.', ['result' => $result]);
|
||||
|
||||
exit($result);
|
||||
shutdown($result);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue