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