[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:
Kyrian Obikwelu 2025-10-07 23:25:06 +01:00 • committed by GitHub
commit fcf605d5d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -11,15 +11,19 @@
namespace Mcp\Server\Handler\Request;
use Mcp\Capability\Resource\ResourceReaderInterface;
use Mcp\Exception\ExceptionInterface;
use Mcp\Capability\Registry\ReferenceHandlerInterface;
use Mcp\Capability\Registry\ReferenceProviderInterface;
use Mcp\Capability\Registry\ResourceTemplateReference;
use Mcp\Exception\ResourceNotFoundException;
use Mcp\Schema\JsonRpc\Error;
use Mcp\Schema\JsonRpc\HasMethodInterface;
use Mcp\Schema\JsonRpc\Response;
use Mcp\Schema\Request\ReadResourceRequest;
use Mcp\Schema\Result\ReadResourceResult;
use Mcp\Server\Handler\MethodHandlerInterface;
use Mcp\Server\Session\SessionInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
@ -27,7 +31,9 @@ use Mcp\Server\Session\SessionInterface;
final class ReadResourceHandler implements MethodHandlerInterface
{
public function __construct(
private readonly ResourceReaderInterface $resourceReader,
private readonly ReferenceProviderInterface $referenceProvider,
private readonly ReferenceHandlerInterface $referenceHandler,
private readonly LoggerInterface $logger = new NullLogger(),
) {
}
@ -40,14 +46,33 @@ final class ReadResourceHandler implements MethodHandlerInterface
{
\assert($message instanceof ReadResourceRequest);
$uri = $message->uri;
$this->logger->debug('Reading resource', ['uri' => $uri]);
try {
$contents = $this->resourceReader->read($message);
$reference = $this->referenceProvider->getResource($uri);
if (null === $reference) {
throw new ResourceNotFoundException($message);
}
$result = $this->referenceHandler->handle($reference, ['uri' => $uri]);
if ($reference instanceof ResourceTemplateReference) {
$formatted = $reference->formatResult($result, $uri, $reference->resourceTemplate->mimeType);
} else {
$formatted = $reference->formatResult($result, $uri, $reference->schema->mimeType);
}
return new Response($message->getId(), new ReadResourceResult($formatted));
} catch (ResourceNotFoundException $e) {
$this->logger->error('Resource not found', ['uri' => $uri]);
return new Error($message->getId(), Error::RESOURCE_NOT_FOUND, $e->getMessage());
} catch (ExceptionInterface) {
} catch (\Throwable $e) {
$this->logger->error(\sprintf('Error while reading resource "%s": "%s".', $uri, $e->getMessage()));
return Error::forInternalError('Error while reading resource', $message->getId());
}
return new Response($message->getId(), $contents);
}
}