php-sdk/examples/server/discovery-calculator/McpElements.php
Tobias Nyholm 6dac6f3b4a
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
2025-12-27 15:39:05 +01:00

155 lines
5.3 KiB
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.
*/
namespace Mcp\Example\Server\DiscoveryCalculator;
use Mcp\Capability\Attribute\McpResource;
use Mcp\Capability\Attribute\McpTool;
use Mcp\Exception\ToolCallException;
use Mcp\Schema\Icon;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
/**
* @phpstan-type Config array{precision: int, allow_negative: bool}
*/
final 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 the result of the calculation
*/
#[McpTool(
name: 'calculate',
icons: [new Icon('https://www.svgrepo.com/show/530644/calculator.svg', 'image/svg+xml', ['any'])],
)]
public function calculate(float $a, float $b, string $operation): float
{
$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) {
throw new ToolCallException('Division by zero is not allowed.');
}
$result = $a / $b;
break;
default:
throw new ToolCallException("Unknown operation '{$operation}'. Supported: add, subtract, multiply, divide.");
}
if (!$this->config['allow_negative'] && $result < 0) {
throw new ToolCallException('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',
icons: [new Icon('https://www.svgrepo.com/show/529867/settings.svg', 'image/svg+xml', ['any'])],
)]
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').'.'];
}
}