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\Elicitation;
15:
16: use Nexus\Assert\Assert;
17:
18: /**
19: * Schema for single-selection enumeration without display titles for options.
20: *
21: * @implements SingleSelectEnumSchema<array{
22: * type: 'string',
23: * enum: list<non-empty-string>,
24: * title?: non-empty-string,
25: * description?: non-empty-string,
26: * default?: string,
27: * }>
28: *
29: * @see https://modelcontextprotocol.io/specification/draft/schema#untitledsingleselectenumschema
30: */
31: final readonly class UntitledSingleSelectEnumSchema implements SingleSelectEnumSchema
32: {
33: public const string TYPE = 'string';
34:
35: /**
36: * @var list<non-empty-string>
37: */
38: public array $enum;
39:
40: /**
41: * @var null|non-empty-string
42: */
43: public ?string $title;
44:
45: /**
46: * @var null|non-empty-string
47: */
48: public ?string $description;
49:
50: /**
51: * @param list<string> $enum
52: */
53: public function __construct(
54: array $enum,
55: ?string $title = null,
56: ?string $description = null,
57: public ?string $default = null,
58: ) {
59: Assert::that($enum)
60: ->isList('untitled single select enum schema "enum" must be a list, non-list array given.')
61: ->values()->isNonEmptyString('each untitled single select enum schema "enum" must be a non-empty string.')
62: ;
63: Assert::that($title)
64: ->nullOr()
65: ->isNonEmptyString('untitled single select enum schema "title" must be a non-empty string or null.')
66: ;
67: Assert::that($description)
68: ->nullOr()
69: ->isNonEmptyString('untitled single select enum schema "description" must be a non-empty string or null.')
70: ;
71:
72: $this->enum = $enum;
73: $this->title = $title;
74: $this->description = $description;
75: }
76:
77: #[\Override]
78: public static function fromArray(array $data): static
79: {
80: Assert::that($data)->hasOffset('type', 'untitled single select enum schema is missing the required "type" key.');
81: $type = $data['type'];
82: Assert::that($type)->isIdentical(self::TYPE, 'untitled single select enum schema "type" must be {other}, {value} given.');
83:
84: Assert::that($data)->hasOffset('enum', 'untitled single select enum schema is missing the required "enum" key.');
85: Assert::that($data['enum'])
86: ->isList('untitled single select enum schema "enum" must be a list, non-list array given.')
87: ->values()->isString('each untitled single select enum schema "enum" must be a string, {type} given.')
88: ;
89: $enum = $data['enum'];
90:
91: $title = $data['title'] ?? null;
92: Assert::that($title)->nullOr()->isString('untitled single select enum schema "title" must be a string or null, {type} given.');
93:
94: $description = $data['description'] ?? null;
95: Assert::that($description)->nullOr()->isString('untitled single select enum schema "description" must be a string or null, {type} given.');
96:
97: $default = $data['default'] ?? null;
98: Assert::that($default)->nullOr()->isString('untitled single select enum schema "default" must be a string or null, {type} given.');
99:
100: return new self(enum: $enum, title: $title, description: $description, default: $default);
101: }
102:
103: #[\Override]
104: public function toArray(): array
105: {
106: $data = [
107: 'type' => self::TYPE,
108: 'enum' => $this->enum,
109: ];
110:
111: if (null !== $this->title) {
112: $data['title'] = $this->title;
113: }
114:
115: if (null !== $this->description) {
116: $data['description'] = $this->description;
117: }
118:
119: if (null !== $this->default) {
120: $data['default'] = $this->default;
121: }
122:
123: return $data;
124: }
125:
126: #[\Override]
127: public function jsonSerialize(): array
128: {
129: return $this->toArray();
130: }
131: }
132: