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;
15:
16: use Nexus\Assert\Assert;
17:
18: /**
19: * The set of notification types a client may opt in to on a `subscriptions/listen` request.
20: *
21: * Each notification type is **opt-in**; the server **MUST NOT** send notification types the
22: * client has not explicitly requested here.
23: *
24: * @implements Arrayable<array{
25: * toolsListChanged?: bool,
26: * promptsListChanged?: bool,
27: * resourcesListChanged?: bool,
28: * resourceSubscriptions?: list<string>,
29: * }>
30: *
31: * @see https://modelcontextprotocol.io/specification/draft/schema#subscriptionfilter
32: */
33: final readonly class SubscriptionFilter implements Arrayable
34: {
35: /**
36: * @param null|list<string> $resourceSubscriptions
37: */
38: public function __construct(
39: public ?bool $toolsListChanged = null,
40: public ?bool $promptsListChanged = null,
41: public ?bool $resourcesListChanged = null,
42: public ?array $resourceSubscriptions = null,
43: ) {
44: if (null !== $this->resourceSubscriptions) {
45: Assert::that($this->resourceSubscriptions)
46: ->isList('"notifications.resourceSubscriptions" must be a list, non-list array given.')
47: ->values()->isString('each "notifications.resourceSubscriptions" must be a string, {type} given.')
48: ;
49: }
50: }
51:
52: #[\Override]
53: public static function fromArray(array $data): static
54: {
55: $toolsListChanged = $data['toolsListChanged'] ?? null;
56: Assert::that($toolsListChanged)
57: ->nullOr()
58: ->isBool('"notifications.toolsListChanged" must be a bool, {type} given.')
59: ;
60:
61: $promptsListChanged = $data['promptsListChanged'] ?? null;
62: Assert::that($promptsListChanged)
63: ->nullOr()
64: ->isBool('"notifications.promptsListChanged" must be a bool, {type} given.')
65: ;
66:
67: $resourcesListChanged = $data['resourcesListChanged'] ?? null;
68: Assert::that($resourcesListChanged)
69: ->nullOr()
70: ->isBool('"notifications.resourcesListChanged" must be a bool, {type} given.')
71: ;
72:
73: $resourceSubscriptions = $data['resourceSubscriptions'] ?? null;
74:
75: if (null !== $resourceSubscriptions) {
76: Assert::that($resourceSubscriptions)
77: ->isList('"notifications.resourceSubscriptions" must be a list, {type} given.')
78: ->values()->isString('each "notifications.resourceSubscriptions" must be a string, {type} given.')
79: ;
80: }
81:
82: return new self(
83: toolsListChanged: $toolsListChanged,
84: promptsListChanged: $promptsListChanged,
85: resourcesListChanged: $resourcesListChanged,
86: resourceSubscriptions: $resourceSubscriptions,
87: );
88: }
89:
90: #[\Override]
91: public function toArray(): array
92: {
93: $data = [];
94:
95: if (null !== $this->toolsListChanged) {
96: $data['toolsListChanged'] = $this->toolsListChanged;
97: }
98:
99: if (null !== $this->promptsListChanged) {
100: $data['promptsListChanged'] = $this->promptsListChanged;
101: }
102:
103: if (null !== $this->resourcesListChanged) {
104: $data['resourcesListChanged'] = $this->resourcesListChanged;
105: }
106:
107: if (null !== $this->resourceSubscriptions) {
108: $data['resourceSubscriptions'] = $this->resourceSubscriptions;
109: }
110:
111: return $data;
112: }
113:
114: #[\Override]
115: public function jsonSerialize(): array|\stdClass
116: {
117: $data = $this->toArray();
118:
119: return [] === $data ? new \stdClass() : $data;
120: }
121: }
122: