[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:
Kyrian Obikwelu 2025-10-30 17:30:58 +01:00 • committed by GitHub
commit ca18caf29d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -13,10 +13,14 @@ namespace Mcp\Server\Handler\Request;
use Mcp\Capability\Completion\ProviderInterface;
use Mcp\Capability\Registry\ReferenceProviderInterface;
use Mcp\Exception\PromptNotFoundException;
use Mcp\Exception\ResourceNotFoundException;
use Mcp\Schema\JsonRpc\Error;
use Mcp\Schema\JsonRpc\Request;
use Mcp\Schema\JsonRpc\Response;
use Mcp\Schema\PromptReference;
use Mcp\Schema\Request\CompletionCompleteRequest;
use Mcp\Schema\ResourceReference;
use Mcp\Schema\Result\CompletionCompleteResult;
use Mcp\Server\Session\SessionInterface;
use Psr\Container\ContainerInterface;
@ -51,41 +55,38 @@ final class CompletionCompleteHandler implements RequestHandlerInterface
$name = $request->argument['name'] ?? '';
$value = $request->argument['value'] ?? '';
$reference = match ($request->ref->type) {
'ref/prompt' => $this->referenceProvider->getPrompt($request->ref->name),
'ref/resource' => $this->referenceProvider->getResourceTemplate($request->ref->uri),
default => null,
};
if (null === $reference) {
return new Response($request->getId(), new CompletionCompleteResult([]));
}
$providers = $reference->completionProviders;
$provider = $providers[$name] ?? null;
if (null === $provider) {
return new Response($request->getId(), new CompletionCompleteResult([]));
}
if (\is_string($provider)) {
if (!class_exists($provider)) {
return Error::forInternalError('Invalid completion provider', $request->getId());
}
$provider = $this->container?->has($provider) ? $this->container->get($provider) : new $provider();
}
if (!$provider instanceof ProviderInterface) {
return Error::forInternalError('Invalid completion provider type', $request->getId());
}
try {
$reference = match (true) {
$request->ref instanceof PromptReference => $this->referenceProvider->getPrompt($request->ref->name),
$request->ref instanceof ResourceReference => $this->referenceProvider->getResource($request->ref->uri),
};
$providers = $reference->completionProviders;
$provider = $providers[$name] ?? null;
if (null === $provider) {
return new Response($request->getId(), new CompletionCompleteResult([]));
}
if (\is_string($provider)) {
if (!class_exists($provider)) {
return Error::forInternalError('Invalid completion provider', $request->getId());
}
$provider = $this->container?->has($provider) ? $this->container->get($provider) : new $provider();
}
if (!$provider instanceof ProviderInterface) {
return Error::forInternalError('Invalid completion provider type', $request->getId());
}
$completions = $provider->getCompletions($value);
$total = \count($completions);
$hasMore = $total > 100;
$paged = \array_slice($completions, 0, 100);
return new Response($request->getId(), new CompletionCompleteResult($paged, $total, $hasMore));
} catch (\Throwable) {
} catch (PromptNotFoundException|ResourceNotFoundException $e) {
return Error::forResourceNotFound($e->getMessage(), $request->getId());
} catch (\Throwable $e) {
return Error::forInternalError('Error while handling completion request', $request->getId());
}
}