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:
Tobias Nyholm 2025-12-27 15:39:05 +01:00 • committed by GitHub
commit 6dac6f3b4a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
46 changed files with 56 additions and 56 deletions

View file

@ -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
View 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
```

View 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;
}

View 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\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;
}
}

View 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);

View 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,
];
}
}

View 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);

View 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,
];
}
}

View 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);

View 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';
}
}

View 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)';
}
}

View 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);

View 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,
];
}
}

View 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;
}

View 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';
}

View 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);

View 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();
}
}

View 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\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;
}
}

View 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\CustomDependencies\Service;
interface StatsServiceInterface
{
/**
* @return array<string, int>
*/
public function getSystemStats(): array;
}

View 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\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