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\Enum\ProtocolErrorCode;
18: use Nexus\Mcp\Core\Schema\Error;
19:
20: /**
21: * Returned when the request's protocol version is unknown to the server or unsupported (e.g., a known
22: * experimental or draft version the server has chosen not to implement). For HTTP, the response status
23: * code MUST be `400 Bad Request`.
24: *
25: * @extends Error<array{
26: * code: -32022,
27: * message: non-empty-string,
28: * data: array{supported: list<string>, requested: string},
29: * }>
30: *
31: * @see https://modelcontextprotocol.io/specification/draft/schema#unsupportedprotocolversionerror
32: */
33: final readonly class UnsupportedProtocolVersionError extends Error
34: {
35: public const string DEFAULT_MESSAGE = 'Unsupported protocol version';
36:
37: /**
38: * @var list<string>
39: */
40: public array $supported;
41:
42: /**
43: * @param list<string> $supported
44: */
45: public function __construct(
46: public string $requested,
47: array $supported,
48: string $message = self::DEFAULT_MESSAGE,
49: ) {
50: $this->supported = $supported;
51:
52: parent::__construct(
53: code: ProtocolErrorCode::UnsupportedProtocolVersion,
54: message: $message,
55: data: ['supported' => $supported, 'requested' => $requested],
56: );
57: }
58:
59: #[\Override]
60: public static function fromArray(array $data): static
61: {
62: $message = $data['message'] ?? self::DEFAULT_MESSAGE;
63: Assert::that($message)->isString('error "message" must be a string, {type} given.');
64:
65: $payload = $data['data'] ?? null;
66: Assert::that($payload)
67: ->isArray('error "data" must be an object, {type} given.')
68: ->isMap('error "data" must be a string-keyed object.')
69: ;
70:
71: Assert::that($payload)->hasOffset('requested', 'error "data" is missing the required "requested" key.');
72: Assert::that($payload['requested'])->isString('error "data.requested" must be a string, {type} given.');
73: $requested = $payload['requested'];
74:
75: Assert::that($payload)->hasOffset('supported', 'error "data" is missing the required "supported" key.');
76: Assert::that($payload['supported'])
77: ->isList('error "data.supported" must be a list of strings, {type} given.')
78: ->values()->isString('each error "data.supported" must be a string, {type} given.')
79: ;
80: $supported = $payload['supported'];
81:
82: return new self(requested: $requested, supported: $supported, message: $message);
83: }
84:
85: #[\Override]
86: public function toArray(): array
87: {
88: return [
89: 'code' => ProtocolErrorCode::UnsupportedProtocolVersion->value,
90: 'message' => $this->message,
91: 'data' => ['supported' => $this->supported, 'requested' => $this->requested],
92: ];
93: }
94: }
95: