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\Result;
15:
16: use Nexus\Assert\Assert;
17: use Nexus\Mcp\Core\Schema\Arrayable;
18: use Nexus\Mcp\Core\Schema\Enum\ResultType;
19: use Nexus\Mcp\Core\Schema\MetaObject;
20: use Nexus\Mcp\Core\Schema\Result;
21:
22: /**
23: * The result returned by the server for a `completion/complete` request.
24: *
25: * @extends Result<array{
26: * _meta?: template-type<MetaObject, Arrayable, 'T'>,
27: * resultType: non-empty-string,
28: * completion: array{values: list<string>, total?: int, hasMore?: bool},
29: * }>
30: *
31: * @see https://modelcontextprotocol.io/specification/draft/schema#completeresult
32: */
33: final readonly class CompleteResult extends Result implements ServerResult
34: {
35: /**
36: * @var array{values: list<string>, total?: int, hasMore?: bool}
37: */
38: public array $completion;
39:
40: /**
41: * @param array{values: list<string>, total?: int, hasMore?: bool} $completion
42: */
43: public function __construct(array $completion, MetaObject $meta = new MetaObject())
44: {
45: Assert::that($completion['values'])
46: ->isList('"result.completion.values" must be a list, non-list array given.')
47: ->values()->isString('each "result.completion.values" must be a string, {type} given.')
48: ;
49:
50: $normalized = ['values' => $completion['values']];
51:
52: if (\array_key_exists('total', $completion)) {
53: Assert::that($completion['total'])->isInt('"result.completion.total" must be an int, {type} given.');
54: $normalized['total'] = $completion['total'];
55: }
56:
57: if (\array_key_exists('hasMore', $completion)) {
58: Assert::that($completion['hasMore'])->isBool('"result.completion.hasMore" must be a bool, {type} given.');
59: $normalized['hasMore'] = $completion['hasMore'];
60: }
61:
62: $this->completion = $normalized;
63:
64: parent::__construct(meta: $meta);
65: }
66:
67: #[\Override]
68: public static function fromArray(array $data): static
69: {
70: Assert::that($data)->hasOffset('completion', '"result" is missing the required "completion" key.');
71: Assert::that($data['completion'])
72: ->isArray('"result.completion" must be an object, {type} given.')
73: ->isMap('"result.completion" must be a string-keyed object.')
74: ;
75:
76: Assert::that($data['completion'])->hasOffset('values', '"result" is missing the required "completion.values" key.');
77: Assert::that($data['completion']['values'])
78: ->isList('"result.completion.values" must be a list, {type} given.')
79: ->values()->isString('each "result.completion.values" must be a string, {type} given.')
80: ;
81:
82: $completion = ['values' => $data['completion']['values']];
83:
84: if (\array_key_exists('total', $data['completion'])) {
85: Assert::that($data['completion']['total'])->isInt('"result.completion.total" must be an int, {type} given.');
86: $completion['total'] = $data['completion']['total'];
87: }
88:
89: if (\array_key_exists('hasMore', $data['completion'])) {
90: Assert::that($data['completion']['hasMore'])->isBool('"result.completion.hasMore" must be a bool, {type} given.');
91: $completion['hasMore'] = $data['completion']['hasMore'];
92: }
93:
94: $meta = new MetaObject();
95:
96: if (\array_key_exists('_meta', $data)) {
97: Assert::that($data['_meta'])
98: ->isArray('"result._meta" must be an object, {type} given.')
99: ->isMap('"result._meta" must be a string-keyed object.')
100: ;
101: $meta = MetaObject::fromArray($data['_meta']);
102: }
103:
104: return new self(completion: $completion, meta: $meta);
105: }
106:
107: #[\Override]
108: public function toArray(): array
109: {
110: $data = [];
111: $meta = $this->meta->toArray();
112:
113: if ([] !== $meta) {
114: $data['_meta'] = $meta;
115: }
116:
117: $data['resultType'] = self::getResultType();
118: $data['completion'] = $this->completion;
119:
120: return $data;
121: }
122:
123: #[\Override]
124: protected function getResultType(): string
125: {
126: return ResultType::Complete->value;
127: }
128: }
129: