Adopt server examples
This commit is contained in:
parent
da0d5a87a0
commit
2ef051a355
83 changed files with 3639 additions and 756 deletions
2
.github/workflows/pipeline.yml
vendored
2
.github/workflows/pipeline.yml
vendored
|
|
@ -34,7 +34,7 @@ jobs:
|
|||
run: composer install --no-scripts
|
||||
|
||||
- name: Tests
|
||||
run: vendor/bin/phpunit
|
||||
run: vendor/bin/phpunit --exclude-group inspector
|
||||
|
||||
qa:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@
|
|||
.php-cs-fixer.cache
|
||||
composer.lock
|
||||
vendor
|
||||
examples/**/dev.log
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"ext-fileinfo": "*",
|
||||
"opis/json-schema": "^2.4",
|
||||
"phpdocumentor/reflection-docblock": "^5.6",
|
||||
"psr/container": "^2.0",
|
||||
"psr/event-dispatcher": "^1.0",
|
||||
"psr/log": "^1.0 || ^2.0 || ^3.0",
|
||||
"symfony/finder": "^6.4 || ^7.2",
|
||||
|
|
@ -34,7 +35,9 @@
|
|||
"phpunit/phpunit": "^10.5",
|
||||
"psr/cache": "^3.0",
|
||||
"symfony/console": "^6.4 || ^7.0",
|
||||
"symfony/event-dispatcher": "^6.4 || ^7.0"
|
||||
"symfony/dependency-injection": "^6.4 || ^7.0",
|
||||
"symfony/event-dispatcher": "^6.4 || ^7.0",
|
||||
"symfony/process": "^6.4 || ^7.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/console": "To use SymfonyConsoleTransport for STDIO",
|
||||
|
|
@ -47,6 +50,14 @@
|
|||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Mcp\\Example\\StdioCalculatorExample\\": "examples/01-discovery-stdio-calculator/",
|
||||
"Mcp\\Example\\HttpUserProfileExample\\": "examples/02-discovery-http-userprofile/",
|
||||
"Mcp\\Example\\ManualStdioExample\\": "examples/03-manual-registration-stdio/",
|
||||
"Mcp\\Example\\CombinedHttpExample\\": "examples/04-combined-registration-http/",
|
||||
"Mcp\\Example\\StdioEnvVariables\\": "examples/05-stdio-env-variables/",
|
||||
"Mcp\\Example\\DependenciesStdioExample\\": "examples/06-custom-dependencies-stdio/",
|
||||
"Mcp\\Example\\ComplexSchemaHttpExample\\": "examples/07-complex-tool-schema-http/",
|
||||
"Mcp\\Example\\SchemaShowcaseExample\\": "examples/08-schema-showcase-streamable/",
|
||||
"Mcp\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
|
|
|
|||
149
examples/01-discovery-stdio-calculator/McpElements.php
Normal file
149
examples/01-discovery-stdio-calculator/McpElements.php
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
<?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\StdioCalculatorExample;
|
||||
|
||||
use Mcp\Capability\Attribute\McpResource;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
|
||||
/**
|
||||
* @phpstan-type Config array{precision: int, allow_negative: bool}
|
||||
*/
|
||||
class McpElements
|
||||
{
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
private array $config = [
|
||||
'precision' => 2,
|
||||
'allow_negative' => true,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private readonly LoggerInterface $logger = new NullLogger(),
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a calculation based on the operation.
|
||||
*
|
||||
* Supports 'add', 'subtract', 'multiply', 'divide'.
|
||||
* Obeys the 'precision' and 'allow_negative' settings from the config resource.
|
||||
*
|
||||
* @param float $a the first operand
|
||||
* @param float $b the second operand
|
||||
* @param string $operation the operation ('add', 'subtract', 'multiply', 'divide')
|
||||
*
|
||||
* @return float|string the result of the calculation, or an error message string
|
||||
*/
|
||||
#[McpTool(name: 'calculate')]
|
||||
public function calculate(float $a, float $b, string $operation): float|string
|
||||
{
|
||||
$this->logger->info(\sprintf('Calculating: %f %s %f', $a, $operation, $b));
|
||||
|
||||
$op = strtolower($operation);
|
||||
|
||||
switch ($op) {
|
||||
case 'add':
|
||||
$result = $a + $b;
|
||||
break;
|
||||
case 'subtract':
|
||||
$result = $a - $b;
|
||||
break;
|
||||
case 'multiply':
|
||||
$result = $a * $b;
|
||||
break;
|
||||
case 'divide':
|
||||
if (0 == $b) {
|
||||
return 'Error: Division by zero.';
|
||||
}
|
||||
$result = $a / $b;
|
||||
break;
|
||||
default:
|
||||
return "Error: Unknown operation '{$operation}'. Supported: add, subtract, multiply, divide.";
|
||||
}
|
||||
|
||||
if (!$this->config['allow_negative'] && $result < 0) {
|
||||
return 'Error: Negative results are disabled.';
|
||||
}
|
||||
|
||||
return round($result, $this->config['precision']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the current calculator configuration.
|
||||
* Can be read by clients to understand precision etc.
|
||||
*
|
||||
* @return Config the configuration array
|
||||
*/
|
||||
#[McpResource(
|
||||
uri: 'config://calculator/settings',
|
||||
name: 'calculator_config',
|
||||
description: 'Current settings for the calculator tool (precision, allow_negative).',
|
||||
mimeType: 'application/json',
|
||||
)]
|
||||
public function getConfiguration(): array
|
||||
{
|
||||
$this->logger->info('Resource config://calculator/settings read.');
|
||||
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a specific configuration setting.
|
||||
* Note: This requires more robust validation in a real app.
|
||||
*
|
||||
* @param string $setting the setting key ('precision' or 'allow_negative')
|
||||
* @param mixed $value the new value (int for precision, bool for allow_negative)
|
||||
*
|
||||
* @return array{
|
||||
* success: bool,
|
||||
* error?: string,
|
||||
* message?: string
|
||||
* } success message or error
|
||||
*/
|
||||
#[McpTool(name: 'update_setting')]
|
||||
public function updateSetting(string $setting, mixed $value): array
|
||||
{
|
||||
$this->logger->info(\sprintf('Setting tool called: setting=%s, value=%s', $setting, var_export($value, true)));
|
||||
if (!\array_key_exists($setting, $this->config)) {
|
||||
return ['success' => false, 'error' => "Unknown setting '{$setting}'."];
|
||||
}
|
||||
|
||||
if ('precision' === $setting) {
|
||||
if (!\is_int($value) || $value < 0 || $value > 10) {
|
||||
return ['success' => false, 'error' => 'Invalid precision value. Must be integer between 0 and 10.'];
|
||||
}
|
||||
$this->config['precision'] = $value;
|
||||
|
||||
// In real app, notify subscribers of config://calculator/settings change
|
||||
// $registry->notifyResourceChanged('config://calculator/settings');
|
||||
return ['success' => true, 'message' => "Precision updated to {$value}."];
|
||||
}
|
||||
|
||||
if (!\is_bool($value)) {
|
||||
// Attempt basic cast for flexibility
|
||||
if (\in_array(strtolower((string) $value), ['true', '1', 'yes', 'on'])) {
|
||||
$value = true;
|
||||
} elseif (\in_array(strtolower((string) $value), ['false', '0', 'no', 'off'])) {
|
||||
$value = false;
|
||||
} else {
|
||||
return ['success' => false, 'error' => 'Invalid allow_negative value. Must be boolean (true/false).'];
|
||||
}
|
||||
}
|
||||
$this->config['allow_negative'] = $value;
|
||||
|
||||
// $registry->notifyResourceChanged('config://calculator/settings');
|
||||
return ['success' => true, 'message' => 'Allow negative results set to '.($value ? 'true' : 'false').'.'];
|
||||
}
|
||||
}
|
||||
29
examples/01-discovery-stdio-calculator/server.php
Normal file
29
examples/01-discovery-stdio-calculator/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\Transport\StdioTransport;
|
||||
|
||||
logger()->info('Starting MCP Stdio Calculator Server...');
|
||||
|
||||
Server::make()
|
||||
->withServerInfo('Stdio Calculator', '1.1.0', 'Basic Calculator over STDIO transport.')
|
||||
->withContainer(container())
|
||||
->withLogger(logger())
|
||||
->withDiscovery(__DIR__, ['.'])
|
||||
->build()
|
||||
->connect(new StdioTransport(logger: logger()));
|
||||
|
||||
logger()->info('Server listener stopped gracefully.');
|
||||
141
examples/02-discovery-http-userprofile/McpElements.php
Normal file
141
examples/02-discovery-http-userprofile/McpElements.php
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
<?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\HttpUserProfileExample;
|
||||
|
||||
use Mcp\Capability\Attribute\CompletionProvider;
|
||||
use Mcp\Capability\Attribute\McpPrompt;
|
||||
use Mcp\Capability\Attribute\McpResource;
|
||||
use Mcp\Capability\Attribute\McpResourceTemplate;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class McpElements
|
||||
{
|
||||
// Simulate a simple user database
|
||||
private array $users = [
|
||||
'101' => ['name' => 'Alice', 'email' => 'alice@example.com', 'role' => 'admin'],
|
||||
'102' => ['name' => 'Bob', 'email' => 'bob@example.com', 'role' => 'user'],
|
||||
'103' => ['name' => 'Charlie', 'email' => 'charlie@example.com', 'role' => 'user'],
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private LoggerInterface $logger,
|
||||
) {
|
||||
$this->logger->debug('HttpUserProfileExample McpElements instantiated.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the profile data for a specific user.
|
||||
*
|
||||
* @param string $userId the ID of the user (from URI)
|
||||
*
|
||||
* @return array user profile data
|
||||
*
|
||||
* @throws McpServerException if the user is not found
|
||||
*/
|
||||
#[McpResourceTemplate(
|
||||
uriTemplate: 'user://{userId}/profile',
|
||||
name: 'user_profile',
|
||||
description: 'Get profile information for a specific user ID.',
|
||||
mimeType: 'application/json'
|
||||
)]
|
||||
public function getUserProfile(
|
||||
#[CompletionProvider(values: ['101', '102', '103'])]
|
||||
string $userId,
|
||||
): array {
|
||||
$this->logger->info('Reading resource: user profile', ['userId' => $userId]);
|
||||
if (!isset($this->users[$userId])) {
|
||||
// Throwing an exception that Processor can turn into an error response
|
||||
throw McpServerException::invalidParams("User profile not found for ID: {$userId}");
|
||||
}
|
||||
|
||||
return $this->users[$userId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of all known user IDs.
|
||||
*
|
||||
* @return array list of user IDs
|
||||
*/
|
||||
#[McpResource(
|
||||
uri: 'user://list/ids',
|
||||
name: 'user_id_list',
|
||||
description: 'Provides a list of all available user IDs.',
|
||||
mimeType: 'application/json'
|
||||
)]
|
||||
public function listUserIds(): array
|
||||
{
|
||||
$this->logger->info('Reading resource: user ID list');
|
||||
|
||||
return array_keys($this->users);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a welcome message to a user.
|
||||
* (This is a placeholder - in a real app, it might queue an email).
|
||||
*
|
||||
* @param string $userId the ID of the user to message
|
||||
* @param string|null $customMessage an optional custom message part
|
||||
*
|
||||
* @return array status of the operation
|
||||
*/
|
||||
#[McpTool(name: 'send_welcome')]
|
||||
public function sendWelcomeMessage(string $userId, ?string $customMessage = null): array
|
||||
{
|
||||
$this->logger->info('Executing tool: send_welcome', ['userId' => $userId]);
|
||||
if (!isset($this->users[$userId])) {
|
||||
return ['success' => false, 'error' => "User ID {$userId} not found."];
|
||||
}
|
||||
$user = $this->users[$userId];
|
||||
$message = "Welcome, {$user['name']}!";
|
||||
if ($customMessage) {
|
||||
$message .= ' '.$customMessage;
|
||||
}
|
||||
// Simulate sending
|
||||
$this->logger->info("Simulated sending message to {$user['email']}: {$message}");
|
||||
|
||||
return ['success' => true, 'message_sent' => $message];
|
||||
}
|
||||
|
||||
#[McpTool(name: 'test_tool_without_params')]
|
||||
public function testToolWithoutParams(): array
|
||||
{
|
||||
return ['success' => true, 'message' => 'Test tool without params'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a prompt to write a bio for a user.
|
||||
*
|
||||
* @param string $userId the user ID to generate the bio for
|
||||
* @param string $tone Desired tone (e.g., 'formal', 'casual').
|
||||
*
|
||||
* @return array prompt messages
|
||||
*
|
||||
* @throws McpServerException if user not found
|
||||
*/
|
||||
#[McpPrompt(name: 'generate_bio_prompt')]
|
||||
public function generateBio(
|
||||
#[CompletionProvider(provider: UserIdCompletionProvider::class)]
|
||||
string $userId,
|
||||
string $tone = 'professional',
|
||||
): array {
|
||||
$this->logger->info('Executing prompt: generate_bio', ['userId' => $userId, 'tone' => $tone]);
|
||||
if (!isset($this->users[$userId])) {
|
||||
throw McpServerException::invalidParams("User not found for bio prompt: {$userId}");
|
||||
}
|
||||
$user = $this->users[$userId];
|
||||
|
||||
return [
|
||||
['role' => 'user', 'content' => "Write a short, {$tone} biography for {$user['name']} (Role: {$user['role']}, Email: {$user['email']}). Highlight their role within the system."],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?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\HttpUserProfileExample;
|
||||
|
||||
use Mcp\Capability\Prompt\Completion\ProviderInterface;
|
||||
|
||||
class UserIdCompletionProvider implements ProviderInterface
|
||||
{
|
||||
public function getCompletions(string $currentValue): array
|
||||
{
|
||||
$availableUserIds = ['101', '102', '103'];
|
||||
|
||||
return array_filter($availableUserIds, fn (string $userId) => str_contains($userId, $currentValue));
|
||||
}
|
||||
}
|
||||
76
examples/02-discovery-http-userprofile/server.php
Normal file
76
examples/02-discovery-http-userprofile/server.php
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#!/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\Capability\Registry\Container;
|
||||
use Mcp\Server;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
logger()->info('Starting MCP HTTP User Profile Server...');
|
||||
|
||||
// --- Setup DI Container for DI in McpElements class ---
|
||||
$container = new Container();
|
||||
$container->set(LoggerInterface::class, logger());
|
||||
|
||||
Server::make()
|
||||
->withServerInfo('HTTP User Profiles', '1.0.0')
|
||||
->withLogger(logger())
|
||||
->withContainer($container)
|
||||
->withDiscovery(__DIR__, ['.'])
|
||||
->withTool(
|
||||
function (float $a, float $b, string $operation = 'add'): array {
|
||||
$result = match ($operation) {
|
||||
'add' => $a + $b,
|
||||
'subtract' => $a - $b,
|
||||
'multiply' => $a * $b,
|
||||
'divide' => 0 != $b ? $a / $b : throw new InvalidArgumentException('Cannot divide by zero'),
|
||||
default => throw new InvalidArgumentException("Unknown operation: {$operation}"),
|
||||
};
|
||||
|
||||
return [
|
||||
'operation' => $operation,
|
||||
'operands' => [$a, $b],
|
||||
'result' => $result,
|
||||
];
|
||||
},
|
||||
name: 'calculator',
|
||||
description: 'Perform basic math operations (add, subtract, multiply, divide)'
|
||||
)
|
||||
->withResource(
|
||||
function (): array {
|
||||
$memoryUsage = memory_get_usage(true);
|
||||
$memoryPeak = memory_get_peak_usage(true);
|
||||
$uptime = time() - $_SERVER['REQUEST_TIME_FLOAT'] ?? time();
|
||||
$serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'CLI';
|
||||
|
||||
return [
|
||||
'server_time' => date('Y-m-d H:i:s'),
|
||||
'uptime_seconds' => $uptime,
|
||||
'memory_usage_mb' => round($memoryUsage / 1024 / 1024, 2),
|
||||
'memory_peak_mb' => round($memoryPeak / 1024 / 1024, 2),
|
||||
'php_version' => \PHP_VERSION,
|
||||
'server_software' => $serverSoftware,
|
||||
'operating_system' => \PHP_OS_FAMILY,
|
||||
'status' => 'healthy',
|
||||
];
|
||||
},
|
||||
uri: 'system://status',
|
||||
name: 'system_status',
|
||||
description: 'Current system status and runtime information',
|
||||
mimeType: 'application/json'
|
||||
)
|
||||
->build()
|
||||
->connect(new StreamableHttpServerTransport('127.0.0.1', 8080, 'mcp'));
|
||||
|
||||
logger()->info('Server listener stopped gracefully.');
|
||||
81
examples/03-manual-registration-stdio/SimpleHandlers.php
Normal file
81
examples/03-manual-registration-stdio/SimpleHandlers.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\ManualStdioExample;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class SimpleHandlers
|
||||
{
|
||||
private string $appVersion = '1.0-manual';
|
||||
|
||||
public function __construct(
|
||||
private readonly LoggerInterface $logger,
|
||||
) {
|
||||
$this->logger->info('SimpleHandlers instantiated for manual registration example.');
|
||||
}
|
||||
|
||||
/**
|
||||
* A manually registered tool to echo input.
|
||||
*
|
||||
* @param string $text the text to echo
|
||||
*
|
||||
* @return string the echoed text
|
||||
*/
|
||||
public function echoText(string $text): string
|
||||
{
|
||||
$this->logger->info("Manual tool 'echo_text' called.", ['text' => $text]);
|
||||
|
||||
return 'Echo: '.$text;
|
||||
}
|
||||
|
||||
/**
|
||||
* A manually registered resource providing app version.
|
||||
*
|
||||
* @return string the application version
|
||||
*/
|
||||
public function getAppVersion(): string
|
||||
{
|
||||
$this->logger->info("Manual resource 'app://version' read.");
|
||||
|
||||
return $this->appVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* A manually registered prompt template.
|
||||
*
|
||||
* @param string $userName the name of the user
|
||||
*
|
||||
* @return array the prompt messages
|
||||
*/
|
||||
public function greetingPrompt(string $userName): array
|
||||
{
|
||||
$this->logger->info("Manual prompt 'personalized_greeting' called.", ['userName' => $userName]);
|
||||
|
||||
return [
|
||||
['role' => 'user', 'content' => "Craft a personalized greeting for {$userName}."],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* A manually registered resource template.
|
||||
*
|
||||
* @param string $itemId the ID of the item
|
||||
*
|
||||
* @return array item details
|
||||
*/
|
||||
public function getItemDetails(string $itemId): array
|
||||
{
|
||||
$this->logger->info("Manual template 'item://{itemId}' resolved.", ['itemId' => $itemId]);
|
||||
|
||||
return ['id' => $itemId, 'name' => "Item {$itemId}", 'description' => "Details for item {$itemId} from manual template."];
|
||||
}
|
||||
}
|
||||
33
examples/03-manual-registration-stdio/server.php
Normal file
33
examples/03-manual-registration-stdio/server.php
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#!/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\ManualStdioExample\SimpleHandlers;
|
||||
use Mcp\Server;
|
||||
use Mcp\Server\Transport\StdioTransport;
|
||||
|
||||
logger()->info('Starting MCP Manual Registration (Stdio) Server...');
|
||||
|
||||
Server::make()
|
||||
->withServerInfo('Manual Reg Server', '1.0.0')
|
||||
->withLogger(logger())
|
||||
->withContainer(container())
|
||||
->withTool([SimpleHandlers::class, 'echoText'], 'echo_text')
|
||||
->withResource([SimpleHandlers::class, 'getAppVersion'], 'app://version', 'application_version', mimeType: 'text/plain')
|
||||
->withPrompt([SimpleHandlers::class, 'greetingPrompt'], 'personalized_greeting')
|
||||
->withResourceTemplate([SimpleHandlers::class, 'getItemDetails'], 'item://{itemId}/details', 'get_item_details', mimeType: 'application/json')
|
||||
->build()
|
||||
->connect(new StdioTransport(logger: logger()));
|
||||
|
||||
logger()->info('Server listener stopped gracefully.');
|
||||
|
|
@ -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\CombinedHttpExample;
|
||||
|
||||
use Mcp\Capability\Attribute\McpResource;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
|
||||
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/04-combined-registration-http/ManualHandlers.php
Normal file
48
examples/04-combined-registration-http/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\CombinedHttpExample;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class ManualHandlers
|
||||
{
|
||||
public function __construct(
|
||||
private 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/04-combined-registration-http/server.php
Normal file
36
examples/04-combined-registration-http/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\CombinedHttpExample\Manual\ManualHandlers;
|
||||
use Mcp\Server;
|
||||
use Mcp\Server\Transports\HttpServerTransport;
|
||||
|
||||
logger()->info('Starting MCP Combined Registration (HTTP) Server...');
|
||||
|
||||
Server::make()
|
||||
->withServerInfo('Combined HTTP Server', '1.0.0')
|
||||
->withLogger(logger())
|
||||
->withContainer(container())
|
||||
->withDiscovery(__DIR__, ['.'])
|
||||
->withTool([ManualHandlers::class, 'manualGreeter'])
|
||||
->withResource(
|
||||
[ManualHandlers::class, 'getPriorityConfigManual'],
|
||||
'config://priority',
|
||||
'priority_config_manual',
|
||||
)
|
||||
->build()
|
||||
->connect(new HttpServerTransport('127.0.0.1', 8081, 'mcp_combined'));
|
||||
|
||||
logger()->info('Server listener stopped gracefully.');
|
||||
51
examples/05-stdio-env-variables/EnvToolHandler.php
Normal file
51
examples/05-stdio-env-variables/EnvToolHandler.php
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?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\StdioEnvVariables;
|
||||
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
|
||||
class EnvToolHandler
|
||||
{
|
||||
/**
|
||||
* Performs an action that can be modified by an environment variable.
|
||||
* The MCP client should set 'APP_MODE' in its 'env' config for this server.
|
||||
*
|
||||
* @param string $input some input data
|
||||
*
|
||||
* @return array the result, varying by APP_MODE
|
||||
*/
|
||||
#[McpTool(name: 'process_data_by_mode')]
|
||||
public function processData(string $input): array
|
||||
{
|
||||
$appMode = getenv('APP_MODE'); // Read from environment
|
||||
|
||||
if ('debug' === $appMode) {
|
||||
return [
|
||||
'mode' => 'debug',
|
||||
'processed_input' => strtoupper($input),
|
||||
'message' => 'Processed in DEBUG mode.',
|
||||
];
|
||||
} elseif ('production' === $appMode) {
|
||||
return [
|
||||
'mode' => 'production',
|
||||
'processed_input_length' => \strlen($input),
|
||||
'message' => 'Processed in PRODUCTION mode (summary only).',
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'mode' => $appMode ?: 'default',
|
||||
'original_input' => $input,
|
||||
'message' => 'Processed in default mode (APP_MODE not recognized or not set).',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
59
examples/05-stdio-env-variables/server.php
Normal file
59
examples/05-stdio-env-variables/server.php
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#!/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\Transport\StdioTransport;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| MCP Stdio Environment Variable Example Server
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This server demonstrates how to use environment variables to modify tool
|
||||
| behavior. The MCP client can set the APP_MODE environment variable to
|
||||
| control the server's behavior.
|
||||
|
|
||||
| Configure your MCP Client (eg. Cursor) for this server like this:
|
||||
|
|
||||
| {
|
||||
| "mcpServers": {
|
||||
| "my-php-env-server": {
|
||||
| "command": "php",
|
||||
| "args": ["/full/path/to/examples/05-stdio-env-variables/server.php"],
|
||||
| "env": {
|
||||
| "APP_MODE": "debug" // or "production", or leave it out
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
| }
|
||||
|
|
||||
| The server will read the APP_MODE environment variable and use it to
|
||||
| modify the behavior of the tools.
|
||||
|
|
||||
| If the APP_MODE environment variable is not set, the server will use the
|
||||
| default behavior.
|
||||
|
|
||||
*/
|
||||
|
||||
logger()->info('Starting MCP Stdio Environment Variable Example Server...');
|
||||
|
||||
Server::make()
|
||||
->withServerInfo('Env Var Server', '1.0.0')
|
||||
->withLogger(logger())
|
||||
->withDiscovery(__DIR__, ['.'])
|
||||
->build()
|
||||
->connect(new StdioTransport(logger: logger()));
|
||||
|
||||
logger()->info('Server listener stopped gracefully.');
|
||||
89
examples/06-custom-dependencies-stdio/McpTaskHandlers.php
Normal file
89
examples/06-custom-dependencies-stdio/McpTaskHandlers.php
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
<?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\DependenciesStdioExample;
|
||||
|
||||
use Mcp\Capability\Attribute\McpResource;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Mcp\DependenciesStdioExample\Services\StatsServiceInterface;
|
||||
use Mcp\DependenciesStdioExample\Services\TaskRepositoryInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class McpTaskHandlers
|
||||
{
|
||||
public function __construct(
|
||||
private TaskRepositoryInterface $taskRepo,
|
||||
private StatsServiceInterface $statsService,
|
||||
private 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 array 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 array 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 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 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,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\DependenciesStdioExample\Service;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class InMemoryTaskRepository implements TaskRepositoryInterface
|
||||
{
|
||||
private array $tasks = [];
|
||||
|
||||
private int $nextTaskId = 1;
|
||||
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public function __construct(LoggerInterface $logger)
|
||||
{
|
||||
$this->logger = $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,17 @@
|
|||
<?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\DependenciesStdioExample\Service;
|
||||
|
||||
interface StatsServiceInterface
|
||||
{
|
||||
public function getSystemStats(): array;
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?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\DependenciesStdioExample\Service;
|
||||
|
||||
class SystemStatsService implements StatsServiceInterface
|
||||
{
|
||||
private TaskRepositoryInterface $taskRepository;
|
||||
|
||||
public function __construct(TaskRepositoryInterface $taskRepository)
|
||||
{
|
||||
$this->taskRepository = $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,23 @@
|
|||
<?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\DependenciesStdioExample\Service;
|
||||
|
||||
interface TaskRepositoryInterface
|
||||
{
|
||||
public function addTask(string $userId, string $description): array;
|
||||
|
||||
public function getTasksForUser(string $userId): array;
|
||||
|
||||
public function getAllTasks(): array;
|
||||
|
||||
public function completeTask(int $taskId): bool;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue