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