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\Extension\Skills\Schema\Result;
15:
16: use Nexus\Assert\Assert;
17: use Nexus\Mcp\Core\Schema\Arrayable;
18: use Nexus\Mcp\Core\Schema\Enum\CacheScope;
19: use Nexus\Mcp\Core\Schema\Enum\ResultType;
20: use Nexus\Mcp\Core\Schema\MetaObject;
21: use Nexus\Mcp\Core\Schema\MetaObject\GenericResultMetaObject;
22: use Nexus\Mcp\Core\Schema\MetaObject\ResultMetaObject;
23: use Nexus\Mcp\Core\Schema\Result\CacheableResult;
24: use Nexus\Mcp\Core\Schema\Result\ServerResult;
25: use Nexus\Mcp\Extension\Skills\Schema\Skill;
26:
27: /**
28: * The result returned by the server for a `skills/get` request.
29: *
30: * @extends CacheableResult<array{
31: * _meta?: template-type<ResultMetaObject, MetaObject, 'T'>,
32: * resultType: non-empty-string,
33: * skill: template-type<Skill, Arrayable, 'T'>,
34: * ttlMs: int,
35: * cacheScope: value-of<CacheScope>,
36: * }>
37: *
38: * @see https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/seps/2640-skills-extension.md
39: */
40: final readonly class GetSkillResult extends CacheableResult implements ServerResult
41: {
42: public function __construct(
43: public Skill $skill,
44: int $ttlMs,
45: CacheScope $cacheScope,
46: ResultMetaObject $meta = new GenericResultMetaObject(),
47: ) {
48: parent::__construct(ttlMs: $ttlMs, cacheScope: $cacheScope, meta: $meta);
49: }
50:
51: #[\Override]
52: public static function fromArray(array $data): static
53: {
54: Assert::that($data)->hasOffset('skill', '"result" is missing the required "skill" key.');
55: Assert::that($data['skill'])
56: ->isArray('"result.skill" must be an object, {type} given.')
57: ->isMap('"result.skill" must be a string-keyed object.')
58: ;
59: $skill = Skill::fromArray($data['skill']);
60:
61: Assert::that($data)->hasOffset('ttlMs', '"result" is missing the required "ttlMs" key.');
62: $ttlMs = $data['ttlMs'];
63: Assert::that($ttlMs)->isInt('"result.ttlMs" must be an integer, {type} given.');
64:
65: Assert::that($data)->hasOffset('cacheScope', '"result" is missing the required "cacheScope" key.');
66: Assert::that($data['cacheScope'])->isOneOf(array_column(CacheScope::cases(), 'value'), '"result.cacheScope" must be one of {choices}, {value} given.');
67: $cacheScope = CacheScope::from($data['cacheScope']);
68:
69: $meta = new GenericResultMetaObject();
70:
71: if (\array_key_exists('_meta', $data)) {
72: Assert::that($data['_meta'])
73: ->isArray('"result._meta" must be an object, {type} given.')
74: ->not()->isNonEmptyList('"result._meta" must be a string-keyed object.')
75: ;
76: $meta = GenericResultMetaObject::fromArray($data['_meta']);
77: }
78:
79: return new self(skill: $skill, ttlMs: $ttlMs, cacheScope: $cacheScope, meta: $meta);
80: }
81:
82: #[\Override]
83: public function toArray(): array
84: {
85: $data = [];
86: $meta = $this->meta->toArray();
87:
88: if ([] !== $meta) {
89: $data['_meta'] = $meta;
90: }
91:
92: $data['resultType'] = self::getResultType();
93: $data['skill'] = $this->skill->toArray();
94: $data['ttlMs'] = $this->ttlMs;
95: $data['cacheScope'] = $this->cacheScope->value;
96:
97: return $data;
98: }
99:
100: #[\Override]
101: public function rebuildWithMeta(ResultMetaObject $meta): static
102: {
103: return new self(skill: $this->skill, ttlMs: $this->ttlMs, cacheScope: $this->cacheScope, meta: $meta);
104: }
105:
106: #[\Override]
107: protected function getResultType(): string
108: {
109: return ResultType::Complete->value;
110: }
111: }
112: