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\RequestParams;
15:
16: use Nexus\Assert\Assert;
17: use Nexus\Mcp\Core\Schema\Elicitation\ElicitResult;
18: use Nexus\Mcp\Core\Schema\MetaObject;
19: use Nexus\Mcp\Core\Schema\MetaObject\RequestMetaObject;
20: use Nexus\Mcp\Core\Schema\Result\InputResponse;
21:
22: /**
23: * Parameters for a `resources/read` request.
24: *
25: * @extends ResourceRequestParams<array{
26: * _meta: template-type<RequestMetaObject, MetaObject, 'T'>,
27: * uri: string,
28: * inputResponses?: array<int|non-empty-string, array<string, mixed>>,
29: * requestState?: string,
30: * }>
31: *
32: * @see https://modelcontextprotocol.io/specification/2026-07-28/schema#readresourcerequestparams
33: */
34: final readonly class ReadResourceRequestParams extends ResourceRequestParams implements InputResponseCarrierInterface
35: {
36: /**
37: * @var null|array<int|non-empty-string, InputResponse>
38: */
39: public ?array $inputResponses;
40:
41: /**
42: * @param null|array<int|non-empty-string, InputResponse> $inputResponses
43: */
44: public function __construct(
45: string $uri,
46: RequestMetaObject $meta,
47: ?array $inputResponses = null,
48: public ?string $requestState = null,
49: ) {
50: $inputResponses = [] === $inputResponses ? null : $inputResponses;
51:
52: if (null !== $inputResponses) {
53: Assert::that($inputResponses)
54: ->keys()
55: ->isIntOrNonEmptyString('each "params.inputResponses" key must be an int or non-empty string.')
56: ;
57: Assert::that($inputResponses)
58: ->values()
59: ->isInstanceOf(InputResponse::class, 'each "params.inputResponses" entry must be an InputResponse, {type} given.')
60: ;
61: }
62:
63: $this->inputResponses = $inputResponses;
64:
65: parent::__construct(uri: $uri, meta: $meta);
66: }
67:
68: #[\Override]
69: public function getInputResponses(): ?array
70: {
71: return $this->inputResponses;
72: }
73:
74: #[\Override]
75: public function getRequestState(): ?string
76: {
77: return $this->requestState;
78: }
79:
80: #[\Override]
81: public static function fromArray(array $data): static
82: {
83: Assert::that($data)->hasOffset('uri', '"params" is missing the required "uri" key.');
84: $uri = $data['uri'];
85: Assert::that($uri)->isNonEmptyString('"params.uri" must be a non-empty string, {type} given.');
86:
87: $inputResponses = null;
88:
89: if (\array_key_exists('inputResponses', $data)) {
90: Assert::that($data['inputResponses'])
91: ->isArray('"params.inputResponses" must be an object, {type} given.')
92: ->keys()->isIntOrNonEmptyString('each "params.inputResponses" key must be an int or non-empty string.')
93: ;
94: Assert::that($data['inputResponses'])
95: ->values()
96: ->isArray('each "params.inputResponses" entry must be an object, {type} given.')
97: ->isMap('each "params.inputResponses" entry must be a string-keyed object.')
98: ;
99: $inputResponses = array_map(ElicitResult::fromArray(...), $data['inputResponses']);
100: }
101:
102: $requestState = null;
103:
104: if (\array_key_exists('requestState', $data)) {
105: Assert::that($data['requestState'])->isString('"params.requestState" must be a string, {type} given.');
106: $requestState = $data['requestState'];
107: }
108:
109: Assert::that($data)->hasOffset('_meta', '"params" is missing the required "_meta" key.');
110: Assert::that($data['_meta'])
111: ->isArray('"params._meta" must be an object, {type} given.')
112: ->not()->isNonEmptyList('"params._meta" must be a string-keyed object.')
113: ;
114: $meta = RequestMetaObject::fromArray($data['_meta']);
115:
116: return new self(uri: $uri, meta: $meta, inputResponses: $inputResponses, requestState: $requestState);
117: }
118:
119: #[\Override]
120: public function toArray(): array
121: {
122: $data = [
123: '_meta' => $this->meta->toArray(),
124: 'uri' => $this->uri,
125: ];
126:
127: if (null !== $this->inputResponses) {
128: $data['inputResponses'] = array_map(
129: static fn(InputResponse $response): array => $response->toArray(),
130: $this->inputResponses,
131: );
132: }
133:
134: if (null !== $this->requestState) {
135: $data['requestState'] = $this->requestState;
136: }
137:
138: return $data;
139: }
140:
141: #[\Override]
142: public function jsonSerialize(): array
143: {
144: $data = parent::jsonSerialize();
145:
146: if (null !== $this->inputResponses) {
147: $data['inputResponses'] = array_map(
148: static fn(InputResponse $response): array|\stdClass => $response->jsonSerialize(),
149: $this->inputResponses,
150: );
151:
152: if (array_is_list($this->inputResponses)) {
153: $data['inputResponses'] = (object) $data['inputResponses'];
154: }
155: }
156:
157: return $data;
158: }
159: }
160: