1: <?php
2:
3: declare(strict_types=1);
4:
5: /**
6: * This file is part of the Nexus MCP SDK package.
7: *
8: * (c) 2026 John Paul E. Balandan, CPA <paulbalandan@gmail.com>
9: *
10: * For the full copyright and license information, please view
11: * the LICENSE file that was distributed with this source code.
12: */
13:
14: namespace Nexus\Mcp\Core\Schema\Error;
15:
16: use Nexus\Assert\Assert;
17: use Nexus\Mcp\Core\Schema\Arrayable;
18: use Nexus\Mcp\Core\Schema\ClientCapabilities;
19: use Nexus\Mcp\Core\Schema\Enum\ProtocolErrorCode;
20: use Nexus\Mcp\Core\Schema\Error;
21:
22: /**
23: * Returned when processing a request requires a capability the client did not declare in
24: * `clientCapabilities`. For HTTP, the response status code MUST be `400 Bad Request`.
25: *
26: * @extends Error<array{
27: * code: -32021,
28: * message: non-empty-string,
29: * data: array{requiredCapabilities: template-type<ClientCapabilities, Arrayable, 'T'>},
30: * }>
31: *
32: * @see https://modelcontextprotocol.io/specification/draft/schema#missingrequiredclientcapabilityerror
33: */
34: final readonly class MissingRequiredClientCapabilityError extends Error
35: {
36: public const string DEFAULT_MESSAGE = 'Missing required client capability';
37:
38: public function __construct(public ClientCapabilities $requiredCapabilities, string $message = self::DEFAULT_MESSAGE)
39: {
40: parent::__construct(
41: code: ProtocolErrorCode::MissingRequiredClientCapability,
42: message: $message,
43: data: ['requiredCapabilities' => $requiredCapabilities->toArray()],
44: );
45: }
46:
47: #[\Override]
48: public static function fromArray(array $data): static
49: {
50: $message = $data['message'] ?? self::DEFAULT_MESSAGE;
51: Assert::that($message)->isString('error "message" must be a string, {type} given.');
52:
53: $payload = $data['data'] ?? null;
54: Assert::that($payload)
55: ->isArray('error "data" must be an object, {type} given.')
56: ->isMap('error "data" must be a string-keyed object.')
57: ;
58:
59: Assert::that($payload)->hasOffset('requiredCapabilities', 'error "data" is missing the required "requiredCapabilities" key.');
60: Assert::that($payload['requiredCapabilities'])
61: ->isArray('error "data.requiredCapabilities" must be an object, {type} given.')
62: ->isMap('error "data.requiredCapabilities" must be a string-keyed object.')
63: ;
64:
65: return new self(
66: requiredCapabilities: ClientCapabilities::fromArray($payload['requiredCapabilities']),
67: message: $message,
68: );
69: }
70:
71: #[\Override]
72: public function toArray(): array
73: {
74: return [
75: 'code' => ProtocolErrorCode::MissingRequiredClientCapability->value,
76: 'message' => $this->message,
77: 'data' => ['requiredCapabilities' => $this->requiredCapabilities->toArray()],
78: ];
79: }
80:
81: #[\Override]
82: public function jsonSerialize(): array
83: {
84: return [
85: 'code' => ProtocolErrorCode::MissingRequiredClientCapability->value,
86: 'message' => $this->message,
87: 'data' => ['requiredCapabilities' => $this->requiredCapabilities->jsonSerialize()],
88: ];
89: }
90: }
91: