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\ContentBlock;
15:
16: use Nexus\Assert\Assert;
17: use Nexus\Mcp\Core\JsonRpc\ResourceContentsDispatcher;
18: use Nexus\Mcp\Core\Schema\Annotations;
19: use Nexus\Mcp\Core\Schema\Arrayable;
20: use Nexus\Mcp\Core\Schema\ContentBlock;
21: use Nexus\Mcp\Core\Schema\MetaObject;
22: use Nexus\Mcp\Core\Schema\Resource\BlobResourceContents;
23: use Nexus\Mcp\Core\Schema\Resource\ResourceContents;
24: use Nexus\Mcp\Core\Schema\Resource\TextResourceContents;
25:
26: /**
27: * The contents of a resource, embedded into a prompt or tool call result.
28: *
29: * It is up to the client how best to render embedded resources for the benefit of the LLM and/or the user.
30: *
31: * @implements Arrayable<array{
32: * resource: template-type<ResourceContents, Arrayable, 'T'>,
33: * type: 'resource',
34: * annotations?: template-type<Annotations, Arrayable, 'T'>,
35: * _meta?: template-type<MetaObject, Arrayable, 'T'>,
36: * }>
37: *
38: * @see https://modelcontextprotocol.io/specification/2025-11-25/schema#embeddedresource
39: */
40: final readonly class EmbeddedResource implements Arrayable, ContentBlock
41: {
42: public const string TYPE = 'resource';
43:
44: public function __construct(
45: public BlobResourceContents|TextResourceContents $resource,
46: public Annotations $annotations = new Annotations(),
47: public MetaObject $meta = new MetaObject(),
48: ) {
49: }
50:
51: /**
52: * @param array<string, mixed> $data
53: */
54: #[\Override]
55: public static function fromArray(array $data): static
56: {
57: Assert::that($data)->hasOffset('type', 'embedded resource missing the required "type" key.');
58: $type = $data['type'];
59: Assert::that($type)->isIdentical(self::TYPE, 'embedded resource "type" must be {other}, {value} given.');
60:
61: Assert::that($data)->hasOffset('resource', 'embedded resource missing the required "resource" key.');
62: Assert::that($data['resource'])
63: ->isArray('embedded resource "resource" must be an object, {type} given.')
64: ->isMap('embedded resource "resource" must be a string-keyed object.')
65: ;
66: $resource = ResourceContentsDispatcher::fromArray($data['resource'], 'EmbeddedResource resource');
67:
68: $annotations = new Annotations();
69:
70: if (\array_key_exists('annotations', $data)) {
71: Assert::that($data['annotations'])
72: ->isArray('embedded resource "annotations" must be an object, {type} given.')
73: ->isMap('embedded resource "annotations" must be a string-keyed object.')
74: ;
75: $annotations = Annotations::fromArray($data['annotations']);
76: }
77:
78: $meta = new MetaObject();
79:
80: if (\array_key_exists('_meta', $data)) {
81: Assert::that($data['_meta'])
82: ->isArray('embedded resource "_meta" must be an object, {type} given.')
83: ->isMap('embedded resource "_meta" must be a string-keyed object.')
84: ;
85: $meta = MetaObject::fromArray($data['_meta']);
86: }
87:
88: return new self($resource, $annotations, $meta);
89: }
90:
91: #[\Override]
92: public function toArray(): array
93: {
94: $data = [
95: 'resource' => $this->resource->toArray(),
96: 'type' => self::TYPE,
97: ];
98:
99: $annotations = $this->annotations->toArray();
100:
101: if ([] !== $annotations) {
102: $data['annotations'] = $annotations;
103: }
104:
105: $meta = $this->meta->toArray();
106:
107: if ([] !== $meta) {
108: $data['_meta'] = $meta;
109: }
110:
111: return $data;
112: }
113:
114: #[\Override]
115: public function jsonSerialize(): array
116: {
117: return $this->toArray();
118: }
119: }
120: