[Server] Fix: standardize error handling across handlers with MCP specificatio… (#124)
* fix: standardize error handling across handlers with MCP specification compliance * Update mcp-elements.md Co-authored-by: Christopher Hertel <mail@christopher-hertel.de> * docs: clarify error handling behavior for all element types * docs: remove unnecessary whitespace in mcp-elements.md * fix: simplify exception signatures to accept simple messages * refactor: use specific exceptions in examples for better error handling * feat: throw exceptions from reference provider instead of returning null --------- Co-authored-by: Christopher Hertel <mail@christopher-hertel.de>
This commit is contained in:
parent
8885d29a4e
commit
ca18caf29d
4 changed files with 278 additions and 185 deletions
|
|
@ -14,6 +14,7 @@ chdir(__DIR__);
|
|||
|
||||
use Http\Discovery\Psr17Factory;
|
||||
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
|
||||
use Mcp\Exception\ToolCallException;
|
||||
use Mcp\Schema\Content\TextContent;
|
||||
use Mcp\Schema\Enum\LoggingLevel;
|
||||
use Mcp\Schema\JsonRpc\Error as JsonRpcError;
|
||||
|
|
@ -64,7 +65,7 @@ $server = Server::builder()
|
|||
);
|
||||
|
||||
if ($response instanceof JsonRpcError) {
|
||||
throw new RuntimeException(sprintf('Sampling request failed (%d): %s', $response->code, $response->message));
|
||||
throw new ToolCallException(sprintf('Sampling request failed (%d): %s', $response->code, $response->message));
|
||||
}
|
||||
|
||||
$result = $response->result;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ use Mcp\Capability\Attribute\McpPrompt;
|
|||
use Mcp\Capability\Attribute\McpResource;
|
||||
use Mcp\Capability\Attribute\McpResourceTemplate;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Mcp\Exception\InvalidArgumentException;
|
||||
use Mcp\Exception\PromptGetException;
|
||||
use Mcp\Exception\ResourceReadException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
|
|
@ -48,7 +49,7 @@ final class McpElements
|
|||
*
|
||||
* @return User user profile data
|
||||
*
|
||||
* @throws InvalidArgumentException if the user is not found
|
||||
* @throws ResourceReadException if the user is not found
|
||||
*/
|
||||
#[McpResourceTemplate(
|
||||
uriTemplate: 'user://{userId}/profile',
|
||||
|
|
@ -62,7 +63,7 @@ final class McpElements
|
|||
): array {
|
||||
$this->logger->info('Reading resource: user profile', ['userId' => $userId]);
|
||||
if (!isset($this->users[$userId])) {
|
||||
throw new InvalidArgumentException("User profile not found for ID: {$userId}");
|
||||
throw new ResourceReadException("User not found for ID: {$userId}");
|
||||
}
|
||||
|
||||
return $this->users[$userId];
|
||||
|
|
@ -130,7 +131,7 @@ final class McpElements
|
|||
*
|
||||
* @return array<string, string>[] prompt messages
|
||||
*
|
||||
* @throws InvalidArgumentException if user not found
|
||||
* @throws PromptGetException if user not found
|
||||
*/
|
||||
#[McpPrompt(name: 'generate_bio_prompt')]
|
||||
public function generateBio(
|
||||
|
|
@ -140,7 +141,7 @@ final class McpElements
|
|||
): array {
|
||||
$this->logger->info('Executing prompt: generate_bio', ['userId' => $userId, 'tone' => $tone]);
|
||||
if (!isset($this->users[$userId])) {
|
||||
throw new InvalidArgumentException("User not found for bio prompt: {$userId}");
|
||||
throw new PromptGetException("User not found for bio prompt: {$userId}");
|
||||
}
|
||||
$user = $this->users[$userId];
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ declare(strict_types=1);
|
|||
namespace Mcp\Example\StdioCachedDiscovery;
|
||||
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Mcp\Exception\ToolCallException;
|
||||
|
||||
/**
|
||||
* Example MCP elements for demonstrating cached discovery.
|
||||
|
|
@ -39,7 +40,7 @@ class CachedCalculatorElements
|
|||
public function divide(int $a, int $b): float
|
||||
{
|
||||
if (0 === $b) {
|
||||
throw new \InvalidArgumentException('Division by zero is not allowed');
|
||||
throw new ToolCallException('Division by zero is not allowed');
|
||||
}
|
||||
|
||||
return $a / $b;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace Mcp\Example\StdioDiscoveryCalculator;
|
|||
|
||||
use Mcp\Capability\Attribute\McpResource;
|
||||
use Mcp\Capability\Attribute\McpTool;
|
||||
use Mcp\Exception\ToolCallException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
|
||||
|
|
@ -44,10 +45,10 @@ final class McpElements
|
|||
* @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
|
||||
* @return float the result of the calculation
|
||||
*/
|
||||
#[McpTool(name: 'calculate')]
|
||||
public function calculate(float $a, float $b, string $operation): float|string
|
||||
public function calculate(float $a, float $b, string $operation): float
|
||||
{
|
||||
$this->logger->info(\sprintf('Calculating: %f %s %f', $a, $operation, $b));
|
||||
|
||||
|
|
@ -65,16 +66,16 @@ final class McpElements
|
|||
break;
|
||||
case 'divide':
|
||||
if (0 == $b) {
|
||||
return 'Error: Division by zero.';
|
||||
throw new ToolCallException('Division by zero is not allowed.');
|
||||
}
|
||||
$result = $a / $b;
|
||||
break;
|
||||
default:
|
||||
return "Error: Unknown operation '{$operation}'. Supported: add, subtract, multiply, divide.";
|
||||
throw new ToolCallException("Unknown operation '{$operation}'. Supported: add, subtract, multiply, divide.");
|
||||
}
|
||||
|
||||
if (!$this->config['allow_negative'] && $result < 0) {
|
||||
return 'Error: Negative results are disabled.';
|
||||
throw new ToolCallException('Negative results are disabled.');
|
||||
}
|
||||
|
||||
return round($result, $this->config['precision']);
|
||||
|
|
|
|||
Loading…
Reference in a new issue