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