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