@ni-c/mcp-hub 0.9.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/CHANGELOG.md +619 -0
  2. package/README.md +189 -64
  3. package/dist/admin.js +232 -8
  4. package/dist/admin.js.map +1 -1
  5. package/dist/auth/address.js +89 -0
  6. package/dist/auth/address.js.map +1 -0
  7. package/dist/auth/api-tokens.js +27 -0
  8. package/dist/auth/api-tokens.js.map +1 -0
  9. package/dist/auth/cimd.js +367 -0
  10. package/dist/auth/cimd.js.map +1 -0
  11. package/dist/auth/consent-page.js +4 -7
  12. package/dist/auth/consent-page.js.map +1 -1
  13. package/dist/auth/headers.js +22 -1
  14. package/dist/auth/headers.js.map +1 -1
  15. package/dist/auth/login-page.js +4 -7
  16. package/dist/auth/login-page.js.map +1 -1
  17. package/dist/auth/oidc/adapter.js +135 -0
  18. package/dist/auth/oidc/adapter.js.map +1 -0
  19. package/dist/auth/oidc/interactions.js +187 -0
  20. package/dist/auth/oidc/interactions.js.map +1 -0
  21. package/dist/auth/oidc/mount.js +144 -0
  22. package/dist/auth/oidc/mount.js.map +1 -0
  23. package/dist/auth/oidc/provider.js +440 -0
  24. package/dist/auth/oidc/provider.js.map +1 -0
  25. package/dist/auth/oidc/quirks.js +234 -0
  26. package/dist/auth/oidc/quirks.js.map +1 -0
  27. package/dist/auth/oidc/verifier.js +121 -0
  28. package/dist/auth/oidc/verifier.js.map +1 -0
  29. package/dist/auth/page.js +22 -0
  30. package/dist/auth/page.js.map +1 -1
  31. package/dist/auth/pinned-fetch.js +129 -0
  32. package/dist/auth/pinned-fetch.js.map +1 -0
  33. package/dist/auth/protected-resource.js +41 -0
  34. package/dist/auth/protected-resource.js.map +1 -0
  35. package/dist/auth/rate-limit.js +156 -0
  36. package/dist/auth/rate-limit.js.map +1 -0
  37. package/dist/auth/redirect-uri.js +90 -0
  38. package/dist/auth/redirect-uri.js.map +1 -0
  39. package/dist/auth/registration.js +146 -0
  40. package/dist/auth/registration.js.map +1 -0
  41. package/dist/auth/session.js +43 -0
  42. package/dist/auth/session.js.map +1 -0
  43. package/dist/auth/signed-token.js +49 -0
  44. package/dist/auth/signed-token.js.map +1 -0
  45. package/dist/auth/store.js +516 -5
  46. package/dist/auth/store.js.map +1 -1
  47. package/dist/auth/text.js +51 -0
  48. package/dist/auth/text.js.map +1 -0
  49. package/dist/config.js +184 -5
  50. package/dist/config.js.map +1 -1
  51. package/dist/docker-proxy/policy.js +1 -0
  52. package/dist/docker-proxy/policy.js.map +1 -1
  53. package/dist/docker-proxy/server.js +1 -0
  54. package/dist/docker-proxy/server.js.map +1 -1
  55. package/dist/elicitation.js +0 -0
  56. package/dist/elicitation.js.map +1 -0
  57. package/dist/forward.js +0 -0
  58. package/dist/forward.js.map +1 -0
  59. package/dist/health.js +22 -3
  60. package/dist/health.js.map +1 -1
  61. package/dist/hub.js +337 -29
  62. package/dist/hub.js.map +1 -1
  63. package/dist/index.js +218 -23
  64. package/dist/index.js.map +1 -1
  65. package/dist/limits.js +13 -1
  66. package/dist/limits.js.map +1 -1
  67. package/dist/mcp-limits.js +14 -2
  68. package/dist/mcp-limits.js.map +1 -1
  69. package/dist/proxy.js +262 -34
  70. package/dist/proxy.js.map +1 -1
  71. package/dist/stdio.js +97 -7
  72. package/dist/stdio.js.map +1 -1
  73. package/dist/subscriptions.js +236 -0
  74. package/dist/subscriptions.js.map +1 -0
  75. package/dist/supervisor.js +472 -28
  76. package/dist/supervisor.js.map +1 -1
  77. package/dist/timings.js +61 -0
  78. package/dist/timings.js.map +1 -0
  79. package/dist/tool-filter.js +59 -0
  80. package/dist/tool-filter.js.map +1 -0
  81. package/dist/transports/docker.js.map +1 -1
  82. package/dist/transports/stream.js +33 -20
  83. package/dist/transports/stream.js.map +1 -1
  84. package/dist/upstream/auth.js +435 -0
  85. package/dist/upstream/auth.js.map +1 -0
  86. package/dist/upstream/login.js +94 -0
  87. package/dist/upstream/login.js.map +1 -0
  88. package/dist/upstream/provider.js +286 -0
  89. package/dist/upstream/provider.js.map +1 -0
  90. package/dist/upstream/routes.js +97 -0
  91. package/dist/upstream/routes.js.map +1 -0
  92. package/package.json +23 -7
  93. package/dist/auth/provider.js +0 -380
  94. package/dist/auth/provider.js.map +0 -1
  95. package/dist/auth/routes.js +0 -223
  96. package/dist/auth/routes.js.map +0 -1
@@ -0,0 +1,236 @@
1
+ import { booleanEnv, nonNegativeIntegerEnv, positiveIntegerEnv } from './mcp-limits.js';
2
+ /**
3
+ * Carrying a child's change notifications to the client that asked for them.
4
+ *
5
+ * On the 2026-07-28 revision a client opens a notification stream with
6
+ * `subscriptions/listen` and names the event types it wants. The state that
7
+ * makes this work is the open HTTP response, not a session table — which is the
8
+ * only reason a stateless gateway can carry it at all. It is the same property
9
+ * that made elicitation forwardable: nothing is remembered between requests
10
+ * that is not held by a socket somebody is still holding open.
11
+ *
12
+ * The SDK owns the downstream wire: acknowledgment first, per-stream filtering,
13
+ * `subscriptionId` stamping, teardown, keepalive. What this module owns is the
14
+ * half the SDK cannot know about — that behind one route sits a child process
15
+ * the hub may have put to sleep, and that several clients share it.
16
+ *
17
+ * Two consequences shape everything below:
18
+ *
19
+ * 1. The event bus is publish-only. It never reports which URIs a stream asked
20
+ * for, so the hub reads the filter off the request itself and keeps its own
21
+ * book. That book is what tells the upstream side which URIs to subscribe to.
22
+ * 2. One bus serves a whole route. A `publish` reaches every open stream on it,
23
+ * not just the one that prompted it — so a resync meant for a client that
24
+ * just connected also nudges the others. Harmless (they re-read a list they
25
+ * already had) and damped by the debounce, but it is a real property and it
26
+ * is documented rather than hidden.
27
+ */
28
+ /** Global off switch, mirroring `MCP_ELICITATION`. */
29
+ export const SUBSCRIPTIONS_ENABLED = booleanEnv('MCP_SUBSCRIPTIONS', true);
30
+ /** Open listen streams one route will serve before refusing the next. */
31
+ export const MAX_SUBSCRIPTIONS = positiveIntegerEnv('MCP_MAX_SUBSCRIPTIONS', 1024);
32
+ /**
33
+ * URIs one `subscriptions/listen` filter may name.
34
+ *
35
+ * Every URI becomes an upstream subscription the hub has to hold and reconcile,
36
+ * so an unbounded list is a way to make the hub do unbounded work on one
37
+ * request. Well above what a resource picker produces, far below what a script
38
+ * could ask for.
39
+ */
40
+ export const MAX_SUBSCRIPTION_URIS = positiveIntegerEnv('MCP_SUBSCRIPTION_MAX_URIS', 64);
41
+ /** SSE comment-frame keepalive for listen streams; `0` disables it. */
42
+ export const KEEPALIVE_MS = nonNegativeIntegerEnv('MCP_SUBSCRIPTION_KEEPALIVE_MS', 15_000);
43
+ /**
44
+ * How long change events are collected before they are delivered.
45
+ *
46
+ * A child that rewrites its resource list in a loop would otherwise turn one
47
+ * upstream storm into one downstream storm per connected client. Within the
48
+ * window, repeats of the same event collapse to one — which is exactly what the
49
+ * notification means anyway: "read it again", not "here is what changed".
50
+ * `0` disables the window and delivers each event as it arrives.
51
+ */
52
+ export const DEBOUNCE_MS = nonNegativeIntegerEnv('MCP_SUBSCRIPTION_DEBOUNCE_MS', 250);
53
+ /**
54
+ * How long one listen stream may stay open; `0` lets it live as long as the
55
+ * socket does.
56
+ *
57
+ * This is a reaper for clients that go away without closing anything, not a
58
+ * protocol feature. The specification's graceful closure — answering the
59
+ * original request with a completion result before closing — is not reachable
60
+ * from here: the SDK owns the stream and exposes no hook for it. So the stream
61
+ * simply ends, which the client sees as the same unexpected disconnect it would
62
+ * see from an HTTP timeout, and reconnects. Spec-legal, and named as what it is
63
+ * rather than dressed up as the graceful variant.
64
+ */
65
+ export const MAX_STREAM_MS = nonNegativeIntegerEnv('MCP_SUBSCRIPTION_MAX_MS', 30 * 60_000);
66
+ /** True unless an operator said otherwise, globally or for this server. */
67
+ export function subscriptionsAllowed(config) {
68
+ return SUBSCRIPTIONS_ENABLED && config.subscriptions !== 'off';
69
+ }
70
+ /** Whether a parsed request body is a `subscriptions/listen` call. */
71
+ export function isListenRequest(body) {
72
+ if (Array.isArray(body))
73
+ return body.some(entry => isListenRequest(entry));
74
+ return typeof body === 'object' && body !== null && body.method === 'subscriptions/listen';
75
+ }
76
+ /**
77
+ * Reads the filter off a parsed request body.
78
+ *
79
+ * Deliberately forgiving: this is not the validator. The SDK parses the request
80
+ * properly a moment later and owns every error answer, so anything malformed
81
+ * here just yields an empty demand and is rejected downstream with the SDK's
82
+ * own message. Reading it early buys one thing only — knowing which URIs to
83
+ * hold upstream before the stream opens.
84
+ */
85
+ export function parseListenFilter(body) {
86
+ const empty = {
87
+ toolsListChanged: false,
88
+ promptsListChanged: false,
89
+ resourcesListChanged: false,
90
+ resourceSubscriptions: []
91
+ };
92
+ if (Array.isArray(body)) {
93
+ // A batch may not contain a listen at all; if it does, take the first.
94
+ const listen = body.find(entry => isListenRequest(entry));
95
+ return listen ? parseListenFilter(listen) : empty;
96
+ }
97
+ if (typeof body !== 'object' || body === null)
98
+ return empty;
99
+ const params = body.params;
100
+ if (typeof params !== 'object' || params === null)
101
+ return empty;
102
+ const notifications = params.notifications;
103
+ if (typeof notifications !== 'object' || notifications === null)
104
+ return empty;
105
+ const filter = notifications;
106
+ const uris = Array.isArray(filter.resourceSubscriptions)
107
+ ? filter.resourceSubscriptions.filter((uri) => typeof uri === 'string')
108
+ : [];
109
+ return {
110
+ toolsListChanged: filter.toolsListChanged === true,
111
+ promptsListChanged: filter.promptsListChanged === true,
112
+ resourcesListChanged: filter.resourcesListChanged === true,
113
+ // Deduplicated here so the cap counts distinct resources, not repetitions
114
+ // of one, and so the upstream union below never holds the same URI twice.
115
+ resourceSubscriptions: [...new Set(uris)]
116
+ };
117
+ }
118
+ export class SubscriptionRegistry {
119
+ notifier;
120
+ options;
121
+ leases = new Map();
122
+ pending = new Map();
123
+ nextId = 1;
124
+ timer;
125
+ closed = false;
126
+ constructor(notifier, options = {}) {
127
+ this.notifier = notifier;
128
+ this.options = options;
129
+ }
130
+ /** Open listen streams on this route, for the `maxSubscriptions` ceiling. */
131
+ get size() {
132
+ return this.leases.size;
133
+ }
134
+ /** The union of every live lease. Empty when nobody is listening. */
135
+ demand() {
136
+ const uris = new Set();
137
+ let tools = false;
138
+ let prompts = false;
139
+ let resources = false;
140
+ for (const filter of this.leases.values()) {
141
+ tools ||= filter.toolsListChanged;
142
+ prompts ||= filter.promptsListChanged;
143
+ resources ||= filter.resourcesListChanged;
144
+ for (const uri of filter.resourceSubscriptions)
145
+ uris.add(uri);
146
+ }
147
+ return { toolsListChanged: tools, promptsListChanged: prompts, resourcesListChanged: resources, uris: [...uris] };
148
+ }
149
+ acquire(filter) {
150
+ const id = this.nextId++;
151
+ this.leases.set(id, filter);
152
+ this.options.onDemandChange?.();
153
+ return {
154
+ release: () => {
155
+ // Idempotent: the release is wired to both `finish` and `close` on the
156
+ // response, and a stream that ends normally fires both.
157
+ if (!this.leases.delete(id))
158
+ return;
159
+ this.options.onDemandChange?.();
160
+ }
161
+ };
162
+ }
163
+ /** Hand one upstream change event to every stream that opted in. */
164
+ publish(event) {
165
+ if (this.closed)
166
+ return;
167
+ const debounceMs = this.options.debounceMs ?? DEBOUNCE_MS;
168
+ if (debounceMs <= 0) {
169
+ this.deliver(event);
170
+ return;
171
+ }
172
+ this.pending.set(event.kind === 'resource_updated' ? `${event.kind}:${event.uri}` : event.kind, event);
173
+ if (this.timer)
174
+ return;
175
+ this.timer = setTimeout(() => this.flush(), debounceMs);
176
+ // Never a reason to hold the process open for a coalescing window.
177
+ this.timer.unref();
178
+ }
179
+ /**
180
+ * Tell every listener to read everything it is watching again.
181
+ *
182
+ * Sent after a child wakes or restarts, because the hub held no upstream
183
+ * subscription while it was gone and cannot know what happened in the
184
+ * meantime. A `resources/updated` here does not claim the resource changed —
185
+ * it says "read it again", which is all the notification ever meant, and it
186
+ * is the only way to make the gap at the end of a nap recoverable rather than
187
+ * silent.
188
+ */
189
+ resync() {
190
+ const demand = this.demand();
191
+ if (demand.toolsListChanged)
192
+ this.publish({ kind: 'tools_list_changed' });
193
+ if (demand.promptsListChanged)
194
+ this.publish({ kind: 'prompts_list_changed' });
195
+ if (demand.resourcesListChanged)
196
+ this.publish({ kind: 'resources_list_changed' });
197
+ for (const uri of demand.uris)
198
+ this.publish({ kind: 'resource_updated', uri });
199
+ }
200
+ flush() {
201
+ this.timer = undefined;
202
+ const events = [...this.pending.values()];
203
+ this.pending.clear();
204
+ for (const event of events)
205
+ this.deliver(event);
206
+ }
207
+ deliver(event) {
208
+ if (this.closed)
209
+ return;
210
+ // No filtering here on purpose: the SDK delivers each event only to the
211
+ // streams whose filter asked for it. Publishing one nobody wants costs a
212
+ // function call and reaches no client.
213
+ switch (event.kind) {
214
+ case 'tools_list_changed':
215
+ this.notifier.toolsChanged();
216
+ return;
217
+ case 'prompts_list_changed':
218
+ this.notifier.promptsChanged();
219
+ return;
220
+ case 'resources_list_changed':
221
+ this.notifier.resourcesChanged();
222
+ return;
223
+ case 'resource_updated':
224
+ this.notifier.resourceUpdated(event.uri);
225
+ return;
226
+ }
227
+ }
228
+ close() {
229
+ this.closed = true;
230
+ clearTimeout(this.timer);
231
+ this.timer = undefined;
232
+ this.pending.clear();
233
+ this.leases.clear();
234
+ }
235
+ }
236
+ //# sourceMappingURL=subscriptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscriptions.js","sourceRoot":"","sources":["../src/subscriptions.ts"],"names":[],"mappings":"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"}