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/2026-07-28/schema#unsupportedprotocolversionerror
32: */
33: final readonly class UnsupportedProtocolVersionError extends Error
34: {
35: public const string DEFAULT_MESSAGE = 'Unsupported protocol version';
36:
37: /**
38: * @param list<string> $supported
39: * @param non-empty-string $message
40: */
41: public function __construct(
42: public string $requested,
43: public array $supported,
44: string $message = self::DEFAULT_MESSAGE,
45: ) {
46: parent::__construct(
47: code: ProtocolErrorCode::UnsupportedProtocolVersion,
48: message: $message,
49: data: ['supported' => $supported, 'requested' => $requested],
50: );
51: }
52:
53: #[\Override]
54: public static function fromArray(array $data): static
55: {
56: $message = $data['message'] ?? self::DEFAULT_MESSAGE;
57: Assert::that($message)->isNonEmptyString('error "message" must be a non-empty string, {type} given.');
58:
59: $payload = $data['data'] ?? null;
60: Assert::that($payload)
61: ->isArray('error "data" must be an object, {type} given.')
62: ->isMap('error "data" must be a string-keyed object.')
63: ;
64:
65: Assert::that($payload)->hasOffset('requested', 'error "data" is missing the required "requested" key.');
66: Assert::that($payload['requested'])->isString('error "data.requested" must be a string, {type} given.');
67: $requested = $payload['requested'];
68:
69: Assert::that($payload)->hasOffset('supported', 'error "data" is missing the required "supported" key.');
70: Assert::that($payload['supported'])
71: ->isList('error "data.supported" must be a list of strings, {type} given.')
72: ->values()->isString('each error "data.supported" must be a string, {type} given.')
73: ;
74: $supported = $payload['supported'];
75:
76: return new self(requested: $requested, supported: $supported, message: $message);
77: }
78:
79: #[\Override]
80: public function toArray(): array
81: {
82: return [
83: 'code' => ProtocolErrorCode::UnsupportedProtocolVersion->value,
84: 'message' => $this->message,
85: 'data' => ['supported' => $this->supported, 'requested' => $this->requested],
86: ];
87: }
88: }
89: