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\Prompt;
15:
16: use Nexus\Assert\Assert;
17: use Nexus\Mcp\Core\JsonRpc\ContentBlockDispatcher;
18: use Nexus\Mcp\Core\Schema\Arrayable;
19: use Nexus\Mcp\Core\Schema\ContentBlock\AudioContent;
20: use Nexus\Mcp\Core\Schema\ContentBlock\EmbeddedResource;
21: use Nexus\Mcp\Core\Schema\ContentBlock\ImageContent;
22: use Nexus\Mcp\Core\Schema\ContentBlock\ResourceLink;
23: use Nexus\Mcp\Core\Schema\ContentBlock\TextContent;
24: use Nexus\Mcp\Core\Schema\Enum\Role;
25:
26: /**
27: * Describes a message returned as part of a prompt.
28: *
29: * This is similar to `SamplingMessage`, but also supports the embedding of resources from the MCP server.
30: *
31: * @implements Arrayable<array{
32: * content: template-type<AudioContent, Arrayable, 'T'>|template-type<EmbeddedResource, Arrayable, 'T'>|template-type<ImageContent, Arrayable, 'T'>|template-type<ResourceLink, Arrayable, 'T'>|template-type<TextContent, Arrayable, 'T'>,
33: * role: 'assistant'|'user',
34: * }>
35: *
36: * @see https://modelcontextprotocol.io/specification/2026-07-28/schema#promptmessage
37: */
38: final readonly class PromptMessage implements Arrayable
39: {
40: public function __construct(
41: public Role $role,
42: public AudioContent|EmbeddedResource|ImageContent|ResourceLink|TextContent $content,
43: ) {
44: }
45:
46: #[\Override]
47: public static function fromArray(array $data): static
48: {
49: Assert::that($data)->hasOffset('role', 'prompt message is missing the required "role" key.');
50: Assert::that($data['role'])->isOneOf(array_column(Role::cases(), 'value'), 'prompt message "role" must be one of {choices}, {value} given.');
51: $role = Role::from($data['role']);
52:
53: Assert::that($data)->hasOffset('content', 'prompt message is missing the required "content" key.');
54: Assert::that($data['content'])
55: ->isArray('prompt message "content" must be an object, {type} given.')
56: ->isMap('prompt message "content" must be a string-keyed object.')
57: ;
58:
59: return new self(
60: role: $role,
61: content: ContentBlockDispatcher::fromArray($data['content'], 'prompt message "content"'),
62: );
63: }
64:
65: #[\Override]
66: public function toArray(): array
67: {
68: return [
69: 'content' => $this->content->toArray(),
70: 'role' => $this->role->value,
71: ];
72: }
73:
74: #[\Override]
75: public function jsonSerialize(): array
76: {
77: return $this->toArray();
78: }
79: }
80: