[Server] Simplify server extensibility with pluggable method handlers (#100)
* refactor: use a custom method handler registration system * refactor: remove redundant Completer and CompleterInterface * docs: update server-builder documentation to reflect custom method handler changes
This commit is contained in:
parent
e94fd82061
commit
fcf605d5d7
1 changed files with 896 additions and 2865 deletions
|
|
@ -11,12 +11,16 @@
|
|||
|
||||
namespace Mcp\Server\Handler\Request;
|
||||
|
||||
use Mcp\Capability\Tool\ToolCallerInterface;
|
||||
use Mcp\Capability\Registry\ReferenceHandlerInterface;
|
||||
use Mcp\Capability\Registry\ReferenceProviderInterface;
|
||||
use Mcp\Exception\ExceptionInterface;
|
||||
use Mcp\Exception\ToolCallException;
|
||||
use Mcp\Exception\ToolNotFoundException;
|
||||
use Mcp\Schema\JsonRpc\Error;
|
||||
use Mcp\Schema\JsonRpc\HasMethodInterface;
|
||||
use Mcp\Schema\JsonRpc\Response;
|
||||
use Mcp\Schema\Request\CallToolRequest;
|
||||
use Mcp\Schema\Result\CallToolResult;
|
||||
use Mcp\Server\Handler\MethodHandlerInterface;
|
||||
use Mcp\Server\Session\SessionInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
|
@ -29,7 +33,8 @@ use Psr\Log\NullLogger;
|
|||
final class CallToolHandler implements MethodHandlerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ToolCallerInterface $toolCaller,
|
||||
private readonly ReferenceProviderInterface $referenceProvider,
|
||||
private readonly ReferenceHandlerInterface $referenceHandler,
|
||||
private readonly LoggerInterface $logger = new NullLogger(),
|
||||
) {
|
||||
}
|
||||
|
|
@ -43,20 +48,44 @@ final class CallToolHandler implements MethodHandlerInterface
|
|||
{
|
||||
\assert($message instanceof CallToolRequest);
|
||||
|
||||
$toolName = $message->name;
|
||||
$arguments = $message->arguments ?? [];
|
||||
|
||||
$this->logger->debug('Executing tool', ['name' => $toolName, 'arguments' => $arguments]);
|
||||
|
||||
try {
|
||||
$content = $this->toolCaller->call($message);
|
||||
} catch (ExceptionInterface $exception) {
|
||||
$this->logger->error(
|
||||
\sprintf('Error while executing tool "%s": "%s".', $message->name, $exception->getMessage()),
|
||||
[
|
||||
'tool' => $message->name,
|
||||
'arguments' => $message->arguments,
|
||||
],
|
||||
);
|
||||
$reference = $this->referenceProvider->getTool($toolName);
|
||||
if (null === $reference) {
|
||||
throw new ToolNotFoundException($message);
|
||||
}
|
||||
|
||||
$result = $this->referenceHandler->handle($reference, $arguments);
|
||||
$formatted = $reference->formatResult($result);
|
||||
|
||||
$this->logger->debug('Tool executed successfully', [
|
||||
'name' => $toolName,
|
||||
'result_type' => \gettype($result),
|
||||
]);
|
||||
|
||||
return new Response($message->getId(), new CallToolResult($formatted));
|
||||
} catch (ToolNotFoundException $e) {
|
||||
$this->logger->error('Tool not found', ['name' => $toolName]);
|
||||
|
||||
return new Error($message->getId(), Error::METHOD_NOT_FOUND, $e->getMessage());
|
||||
} catch (ToolCallException|ExceptionInterface $e) {
|
||||
$this->logger->error(\sprintf('Error while executing tool "%s": "%s".', $toolName, $e->getMessage()), [
|
||||
'tool' => $toolName,
|
||||
'arguments' => $arguments,
|
||||
]);
|
||||
|
||||
return Error::forInternalError('Error while executing tool', $message->getId());
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->error('Unhandled error during tool execution', [
|
||||
'name' => $toolName,
|
||||
'exception' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return Error::forInternalError('Error while executing tool', $message->getId());
|
||||
}
|
||||
|
||||
return new Response($message->getId(), $content);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue