@modelcontextprotocol/server 2.0.0-beta.1 → 2.0.0-beta.2

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 (41) hide show
  1. package/dist/ajvProvider-D2kRT_qB.d.cts +1030 -0
  2. package/dist/ajvProvider-D2kRT_qB.d.cts.map +1 -0
  3. package/dist/ajvProvider-DhTNkgm1.cjs +7059 -0
  4. package/dist/ajvProvider-DhTNkgm1.cjs.map +1 -0
  5. package/dist/cfWorkerProvider-Djgwc46-.cjs +976 -0
  6. package/dist/cfWorkerProvider-Djgwc46-.cjs.map +1 -0
  7. package/dist/cfWorkerProvider-RncldJmy.d.cts +59 -0
  8. package/dist/cfWorkerProvider-RncldJmy.d.cts.map +1 -0
  9. package/dist/chunk-Bnu9O96Y.cjs +60 -0
  10. package/dist/createMcpHandler-Du3hjXvf.d.mts.map +1 -1
  11. package/dist/createMcpHandler-DyxapqGO.d.cts +11190 -0
  12. package/dist/createMcpHandler-DyxapqGO.d.cts.map +1 -0
  13. package/dist/index.cjs +1657 -0
  14. package/dist/index.cjs.map +1 -0
  15. package/dist/index.d.cts +1745 -0
  16. package/dist/index.d.cts.map +1 -0
  17. package/dist/index.mjs +3 -2
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/{mcp-JttQJlI9.mjs → mcp-C2tFGfLG.mjs} +18 -5
  20. package/dist/mcp-C2tFGfLG.mjs.map +1 -0
  21. package/dist/mcp-DVEehpM2.cjs +10545 -0
  22. package/dist/mcp-DVEehpM2.cjs.map +1 -0
  23. package/dist/shimsNode.cjs +12 -0
  24. package/dist/shimsNode.d.cts +3 -0
  25. package/dist/shimsWorkerd.cjs +23 -0
  26. package/dist/shimsWorkerd.cjs.map +1 -0
  27. package/dist/shimsWorkerd.d.cts +11 -0
  28. package/dist/shimsWorkerd.d.cts.map +1 -0
  29. package/dist/stdio.cjs +563 -0
  30. package/dist/stdio.cjs.map +1 -0
  31. package/dist/stdio.d.cts +107 -0
  32. package/dist/stdio.d.cts.map +1 -0
  33. package/dist/stdio.mjs +1 -1
  34. package/dist/types-Pc2fJzyM.d.cts +1099 -0
  35. package/dist/types-Pc2fJzyM.d.cts.map +1 -0
  36. package/dist/validators/ajv.cjs +10 -0
  37. package/dist/validators/ajv.d.cts +2 -0
  38. package/dist/validators/cfWorker.cjs +3 -0
  39. package/dist/validators/cfWorker.d.cts +2 -0
  40. package/package.json +68 -19
  41. package/dist/mcp-JttQJlI9.mjs.map +0 -1
@@ -0,0 +1,12 @@
1
+ const require_chunk = require('./chunk-Bnu9O96Y.cjs');
2
+ const require_ajvProvider = require('./ajvProvider-DhTNkgm1.cjs');
3
+ let node_process = require("node:process");
4
+ node_process = require_chunk.__toESM(node_process);
5
+
6
+ exports.DefaultJsonSchemaValidator = require_ajvProvider.AjvJsonSchemaValidator;
7
+ Object.defineProperty(exports, 'process', {
8
+ enumerable: true,
9
+ get: function () {
10
+ return node_process.default;
11
+ }
12
+ });
@@ -0,0 +1,3 @@
1
+ import { t as AjvJsonSchemaValidator } from "./ajvProvider-D2kRT_qB.cjs";
2
+ import process from "node:process";
3
+ export { AjvJsonSchemaValidator as DefaultJsonSchemaValidator, process };
@@ -0,0 +1,23 @@
1
+ const require_cfWorkerProvider = require('./cfWorkerProvider-Djgwc46-.cjs');
2
+
3
+ //#region src/shimsWorkerd.ts
4
+ /**
5
+ * Stub process object for non-Node.js environments.
6
+ * StdioServerTransport is not supported in Cloudflare Workers/browser environments.
7
+ */
8
+ function notSupported() {
9
+ throw new Error("StdioServerTransport is not supported in this environment. Use StreamableHTTPServerTransport instead.");
10
+ }
11
+ const process = {
12
+ get stdin() {
13
+ return notSupported();
14
+ },
15
+ get stdout() {
16
+ return notSupported();
17
+ }
18
+ };
19
+
20
+ //#endregion
21
+ exports.DefaultJsonSchemaValidator = require_cfWorkerProvider.CfWorkerJsonSchemaValidator;
22
+ exports.process = process;
23
+ //# sourceMappingURL=shimsWorkerd.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shimsWorkerd.cjs","names":[],"sources":["../src/shimsWorkerd.ts"],"sourcesContent":["/**\n * Cloudflare Workers runtime shims for server package\n *\n * This file is selected via package.json export conditions when running in workerd.\n */\nexport { CfWorkerJsonSchemaValidator as DefaultJsonSchemaValidator } from '@modelcontextprotocol/core-internal/validators/cfWorker';\n\n/**\n * Stub process object for non-Node.js environments.\n * StdioServerTransport is not supported in Cloudflare Workers/browser environments.\n */\nfunction notSupported(): never {\n throw new Error('StdioServerTransport is not supported in this environment. Use StreamableHTTPServerTransport instead.');\n}\n\nexport const process = {\n get stdin(): never {\n return notSupported();\n },\n get stdout(): never {\n return notSupported();\n }\n};\n"],"mappings":";;;;;;;AAWA,SAAS,eAAsB;AAC3B,OAAM,IAAI,MAAM,wGAAwG;;AAG5H,MAAa,UAAU;CACnB,IAAI,QAAe;AACf,SAAO,cAAc;;CAEzB,IAAI,SAAgB;AAChB,SAAO,cAAc;;CAE5B"}
@@ -0,0 +1,11 @@
1
+ import { t as CfWorkerJsonSchemaValidator } from "./cfWorkerProvider-RncldJmy.cjs";
2
+
3
+ //#region src/shimsWorkerd.d.ts
4
+
5
+ declare const process: {
6
+ readonly stdin: never;
7
+ readonly stdout: never;
8
+ };
9
+ //#endregion
10
+ export { CfWorkerJsonSchemaValidator as DefaultJsonSchemaValidator, process };
11
+ //# sourceMappingURL=shimsWorkerd.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shimsWorkerd.d.cts","names":[],"sources":["../src/shimsWorkerd.ts"],"sourcesContent":[],"mappings":";;;;cAea"}
package/dist/stdio.cjs ADDED
@@ -0,0 +1,563 @@
1
+ const require_chunk = require('./chunk-Bnu9O96Y.cjs');
2
+ const require_mcp = require('./mcp-DVEehpM2.cjs');
3
+ let _modelcontextprotocol_server__shims = require("@modelcontextprotocol/server/_shims");
4
+
5
+ //#region src/server/stdio.ts
6
+ /**
7
+ * Server transport for stdio: this communicates with an MCP client by reading from the current process' `stdin` and writing to `stdout`.
8
+ *
9
+ * This transport is only available in Node.js environments.
10
+ *
11
+ * @example
12
+ * ```ts source="./stdio.examples.ts#StdioServerTransport_basicUsage"
13
+ * const server = new McpServer({ name: 'my-server', version: '1.0.0' });
14
+ * const transport = new StdioServerTransport();
15
+ * await server.connect(transport);
16
+ * ```
17
+ */
18
+ var StdioServerTransport = class {
19
+ _readBuffer;
20
+ _started = false;
21
+ _closed = false;
22
+ constructor(_stdin = _modelcontextprotocol_server__shims.process.stdin, _stdout = _modelcontextprotocol_server__shims.process.stdout, options) {
23
+ this._stdin = _stdin;
24
+ this._stdout = _stdout;
25
+ this._readBuffer = new require_mcp.ReadBuffer({ maxBufferSize: options?.maxBufferSize });
26
+ }
27
+ onclose;
28
+ onerror;
29
+ onmessage;
30
+ _ondata = (chunk) => {
31
+ try {
32
+ this._readBuffer.append(chunk);
33
+ this.processReadBuffer();
34
+ } catch (error) {
35
+ this.onerror?.(error);
36
+ this.close().catch(() => {});
37
+ }
38
+ };
39
+ _onerror = (error) => {
40
+ this.onerror?.(error);
41
+ };
42
+ _onstdouterror = (error) => {
43
+ this.onerror?.(error);
44
+ this.close().catch(() => {});
45
+ };
46
+ /**
47
+ * Starts listening for messages on `stdin`.
48
+ */
49
+ async start() {
50
+ if (this._started) throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
51
+ this._started = true;
52
+ this._stdin.on("data", this._ondata);
53
+ this._stdin.on("error", this._onerror);
54
+ this._stdout.on("error", this._onstdouterror);
55
+ }
56
+ processReadBuffer() {
57
+ while (true) try {
58
+ const message = this._readBuffer.readMessage();
59
+ if (message === null) break;
60
+ this.onmessage?.(message);
61
+ } catch (error) {
62
+ this.onerror?.(error);
63
+ }
64
+ }
65
+ async close() {
66
+ if (this._closed) return;
67
+ this._closed = true;
68
+ this._stdin.off("data", this._ondata);
69
+ this._stdin.off("error", this._onerror);
70
+ this._stdout.off("error", this._onstdouterror);
71
+ if (this._stdin.listenerCount("data") === 0) this._stdin.pause();
72
+ this._readBuffer.clear();
73
+ this.onclose?.();
74
+ }
75
+ send(message) {
76
+ if (this._closed) return Promise.reject(/* @__PURE__ */ new Error("StdioServerTransport is closed"));
77
+ return new Promise((resolve, reject) => {
78
+ const json = require_mcp.serializeMessage(message);
79
+ let settled = false;
80
+ const onError = (error) => {
81
+ if (settled) return;
82
+ settled = true;
83
+ this._stdout.off("error", onError);
84
+ this._stdout.off("drain", onDrain);
85
+ reject(error);
86
+ };
87
+ const onDrain = () => {
88
+ if (settled) return;
89
+ settled = true;
90
+ this._stdout.off("error", onError);
91
+ this._stdout.off("drain", onDrain);
92
+ resolve();
93
+ };
94
+ this._stdout.once("error", onError);
95
+ if (this._stdout.write(json)) {
96
+ if (settled) return;
97
+ settled = true;
98
+ this._stdout.off("error", onError);
99
+ resolve();
100
+ } else if (!settled) this._stdout.once("drain", onDrain);
101
+ });
102
+ }
103
+ };
104
+
105
+ //#endregion
106
+ //#region src/server/serveStdio.ts
107
+ /**
108
+ * How long the probe-discard path waits for the probe instance to answer the
109
+ * requests it was delivered before closing it. The wait normally settles as
110
+ * soon as the DiscoverResult is handed to the wire (or immediately, when a
111
+ * delivered cancellation already settled the probe); the bound is a backstop
112
+ * so no edge can ever hold the connection's inbound pump indefinitely behind
113
+ * the discard.
114
+ */
115
+ const DISCARD_ANSWER_TIMEOUT_MS = 3e3;
116
+ /**
117
+ * The transport a pinned instance is connected to: a thin channel that writes
118
+ * through to the entry-owned wire transport and receives the messages the
119
+ * entry forwards. The wire transport itself is never handed to an instance —
120
+ * that is what lets the entry discard an optimistic probe instance (close the
121
+ * channel) without tearing down the connection.
122
+ */
123
+ var StdioConnectionChannel = class {
124
+ onclose;
125
+ onerror;
126
+ onmessage;
127
+ _closed = false;
128
+ /** Request ids the entry delivered to the instance that the instance has not yet answered. */
129
+ _pendingRequests = /* @__PURE__ */ new Set();
130
+ _drainWaiters = [];
131
+ constructor(_wire, _onInstanceClose, _outboundIntercept) {
132
+ this._wire = _wire;
133
+ this._onInstanceClose = _onInstanceClose;
134
+ this._outboundIntercept = _outboundIntercept;
135
+ }
136
+ async start() {}
137
+ async send(message, options) {
138
+ if (require_mcp.isJSONRPCResultResponse(message) || require_mcp.isJSONRPCErrorResponse(message)) {
139
+ const { id } = message;
140
+ if (id !== void 0) this._settle(id);
141
+ }
142
+ if (this._closed) return;
143
+ if (this._outboundIntercept?.(message) === "handled") return;
144
+ return this._wire.send(message, options);
145
+ }
146
+ setProtocolVersion = (version) => {
147
+ this._wire.setProtocolVersion?.(version);
148
+ };
149
+ /** Forwards one inbound message to the connected instance. */
150
+ deliver(message, extra) {
151
+ if (this._closed) return;
152
+ if (require_mcp.isJSONRPCRequest(message)) this._pendingRequests.add(message.id);
153
+ else if (require_mcp.isJSONRPCNotification(message) && message.method === "notifications/cancelled") {
154
+ const cancelledId = message.params?.requestId;
155
+ if (cancelledId !== void 0) this._settle(cancelledId);
156
+ }
157
+ this.onmessage?.(message, extra);
158
+ }
159
+ /**
160
+ * Resolves once every request delivered to the instance has been answered
161
+ * through {@linkcode send}, settled by a delivered cancellation, or the
162
+ * channel has been closed and nothing further can be answered. The wait is
163
+ * bounded by `timeoutMs` as a backstop so no edge can hold the caller
164
+ * indefinitely; resolves `false` only when the bound elapsed with requests
165
+ * still unanswered. Used by the probe-discard path so a probe request the
166
+ * entry accepted is never silently dropped.
167
+ */
168
+ async whenRequestsAnswered(timeoutMs) {
169
+ if (this._closed || this._pendingRequests.size === 0) return true;
170
+ return await new Promise((resolve) => {
171
+ const waiter = () => {
172
+ clearTimeout(timer);
173
+ resolve(true);
174
+ };
175
+ const timer = setTimeout(() => {
176
+ this._drainWaiters = this._drainWaiters.filter((pending) => pending !== waiter);
177
+ resolve(false);
178
+ }, timeoutMs);
179
+ this._drainWaiters.push(waiter);
180
+ });
181
+ }
182
+ async close() {
183
+ if (this._closed) return;
184
+ this._closed = true;
185
+ this._pendingRequests.clear();
186
+ this._releaseDrainWaiters();
187
+ try {
188
+ this._onInstanceClose();
189
+ } finally {
190
+ this.onclose?.();
191
+ }
192
+ }
193
+ _settle(id) {
194
+ this._pendingRequests.delete(id);
195
+ if (this._pendingRequests.size === 0) this._releaseDrainWaiters();
196
+ }
197
+ _releaseDrainWaiters() {
198
+ const waiters = this._drainWaiters;
199
+ this._drainWaiters = [];
200
+ for (const waiter of waiters) waiter();
201
+ }
202
+ };
203
+ /**
204
+ * Classifies one message of the opening exchange with the same body-primary
205
+ * rules the HTTP entry applies per request: `initialize` is the legacy
206
+ * handshake unless it carries a valid modern envelope claim; a present claim
207
+ * is validated (never silently ignored); a claim-less message is 2025-era
208
+ * traffic. There is no header layer on stdio, so the body is the only signal.
209
+ */
210
+ function classifyOpeningMessage(message) {
211
+ const params = message.params;
212
+ if (message.method === "initialize" && !require_mcp.carriesValidModernEnvelopeClaim(params)) {
213
+ const requestedVersion = params !== null && typeof params === "object" && typeof params.protocolVersion === "string" ? params.protocolVersion : void 0;
214
+ return {
215
+ kind: "legacy",
216
+ reason: "initialize",
217
+ ...requestedVersion !== void 0 && { requestedVersion }
218
+ };
219
+ }
220
+ if (!require_mcp.hasEnvelopeClaim(params)) return {
221
+ kind: "legacy",
222
+ reason: "no-claim"
223
+ };
224
+ const meta = require_mcp.requestMetaOf(params);
225
+ const firstIssue = (meta === void 0 ? [] : require_mcp.validateEnvelopeMeta(meta))[0];
226
+ if (firstIssue !== void 0) return {
227
+ kind: "invalid-envelope",
228
+ issue: firstIssue
229
+ };
230
+ const claimedVersion = require_mcp.envelopeClaimVersion(params);
231
+ if (claimedVersion === void 0 || !require_mcp.SUPPORTED_MODERN_PROTOCOL_VERSIONS.includes(claimedVersion)) return {
232
+ kind: "unsupported-revision",
233
+ requested: claimedVersion ?? "unknown"
234
+ };
235
+ return {
236
+ kind: "modern",
237
+ revision: claimedVersion,
238
+ classification: {
239
+ era: "modern",
240
+ revision: claimedVersion
241
+ }
242
+ };
243
+ }
244
+ /**
245
+ * Serves MCP over stdio from a server factory, owning the era decision for
246
+ * the connection: the opening exchange selects the era, ONE instance from the
247
+ * factory is pinned for the connection lifetime, and everything after passes
248
+ * straight through to it. See the module documentation for the opening rules.
249
+ *
250
+ * ```ts
251
+ * import { serveStdio } from '@modelcontextprotocol/server/stdio';
252
+ *
253
+ * serveStdio(() => {
254
+ * const server = new McpServer({ name: 'my-server', version: '1.0.0' }, { capabilities: { tools: {} } });
255
+ * // register tools/resources/prompts once — the same factory serves both eras
256
+ * return server;
257
+ * });
258
+ * ```
259
+ */
260
+ function serveStdio(factory, options = {}) {
261
+ const legacyMode = options.legacy ?? "serve";
262
+ const wire = options.transport ?? new StdioServerTransport();
263
+ let state = { phase: "opening" };
264
+ /** Channel currently being discarded (its close must not tear the connection down). */
265
+ let discarding;
266
+ let closing = false;
267
+ /**
268
+ * Whether the connection has been torn down (`handle.close()` or the wire
269
+ * closing). The opening arms re-check this after every await: a close can
270
+ * race factory construction, and the continuation must neither resurrect
271
+ * the connection state nor keep a late-resolved instance around.
272
+ */
273
+ const isTornDown = () => closing || state.phase === "closed";
274
+ const reportError = (error) => {
275
+ try {
276
+ options.onerror?.(error);
277
+ } catch {}
278
+ };
279
+ const writeErrorResponse = (id, code, message, data) => wire.send({
280
+ jsonrpc: "2.0",
281
+ id,
282
+ error: {
283
+ code,
284
+ message,
285
+ ...data !== void 0 && { data }
286
+ }
287
+ }).catch((error) => reportError(toError(error)));
288
+ /**
289
+ * Entry-handled `subscriptions/listen` for this connection: holds the
290
+ * active subscriptions, serves inbound listen / cancelled-of-listen
291
+ * before the pinned instance is consulted, and rewrites the instance's
292
+ * outbound change notifications onto the active subscriptions. Only
293
+ * consulted on a modern-pinned connection — on a legacy connection
294
+ * change notifications pass straight through (the 2025 unsolicited
295
+ * delivery model is unchanged).
296
+ */
297
+ const listenRouter = new require_mcp.StdioListenRouter(options.maxSubscriptions ?? require_mcp.DEFAULT_MAX_SUBSCRIPTIONS);
298
+ /** Outbound intercept installed on a modern instance's channel. */
299
+ const modernOutboundIntercept = (message) => {
300
+ if (!require_mcp.isJSONRPCNotification(message)) return void 0;
301
+ const routed = listenRouter.routeOutbound(message);
302
+ if (routed === "passthrough") return void 0;
303
+ for (const stamped of routed) wire.send({
304
+ jsonrpc: "2.0",
305
+ ...stamped
306
+ }).catch((error) => reportError(toError(error)));
307
+ return "handled";
308
+ };
309
+ /**
310
+ * Entry-handled inbound listen routing for a modern-pinned connection.
311
+ * Returns `true` when the message was served at the entry and must NOT
312
+ * be delivered to the pinned instance.
313
+ */
314
+ const tryServeListen = async (message) => {
315
+ if (require_mcp.isJSONRPCRequest(message) && message.method === "subscriptions/listen") {
316
+ const meta = require_mcp.requestMetaOf(message.params);
317
+ const issue = require_mcp.hasEnvelopeClaim(message.params) ? (meta === void 0 ? [] : require_mcp.validateEnvelopeMeta(meta))[0] : {
318
+ key: "_meta",
319
+ problem: "the per-request envelope is required on protocol revision 2026-07-28"
320
+ };
321
+ const claimedVersion = require_mcp.envelopeClaimVersion(message.params);
322
+ let reply;
323
+ if (issue !== void 0) reply = {
324
+ jsonrpc: "2.0",
325
+ id: message.id,
326
+ error: {
327
+ code: -32602,
328
+ message: `Invalid _meta envelope: ${issue.key}: ${issue.problem}`
329
+ }
330
+ };
331
+ else if (claimedVersion === void 0 || !require_mcp.SUPPORTED_MODERN_PROTOCOL_VERSIONS.includes(claimedVersion)) {
332
+ const error = new require_mcp.UnsupportedProtocolVersionError({
333
+ supported: [...require_mcp.SUPPORTED_MODERN_PROTOCOL_VERSIONS],
334
+ requested: claimedVersion ?? "unknown"
335
+ });
336
+ reply = {
337
+ jsonrpc: "2.0",
338
+ id: message.id,
339
+ error: {
340
+ code: error.code,
341
+ message: error.message,
342
+ data: error.data
343
+ }
344
+ };
345
+ } else reply = listenRouter.serve(message);
346
+ await wire.send("error" in reply ? reply : {
347
+ jsonrpc: "2.0",
348
+ method: reply.method,
349
+ params: reply.params
350
+ }).catch((error) => reportError(toError(error)));
351
+ return true;
352
+ }
353
+ if (require_mcp.isJSONRPCNotification(message) && message.method === "notifications/cancelled") {
354
+ const cancelledId = message.params?.requestId;
355
+ if (cancelledId !== void 0 && listenRouter.cancel(cancelledId)) return true;
356
+ }
357
+ return false;
358
+ };
359
+ /** Answers a 2025-era request the entry will not serve (the modern-only rejection cells). */
360
+ const answerLegacyRejection = (request, reason, requestedVersion) => {
361
+ const rejection = require_mcp.modernOnlyStrictRejection({
362
+ kind: "legacy",
363
+ reason,
364
+ ...requestedVersion !== void 0 && { requestedVersion }
365
+ }, require_mcp.SUPPORTED_MODERN_PROTOCOL_VERSIONS);
366
+ if (rejection === void 0) return Promise.resolve();
367
+ reportError(/* @__PURE__ */ new Error(`Rejected 2025-era request on a modern-only stdio connection (${rejection.cell}): ${rejection.message}`));
368
+ return writeErrorResponse(request.id, rejection.code, rejection.message, rejection.data);
369
+ };
370
+ const onInstanceClosed = (channel) => {
371
+ if (closing || channel === discarding) return;
372
+ closeAll();
373
+ };
374
+ const connectInstance = async (era, revision) => {
375
+ const product = await factory({ era });
376
+ const server = product instanceof require_mcp.McpServer ? product.server : product;
377
+ if (era === "modern") {
378
+ require_mcp.setNegotiatedProtocolVersion(server, revision);
379
+ require_mcp.installModernOnlyHandlers(server, require_mcp.SUPPORTED_MODERN_PROTOCOL_VERSIONS);
380
+ listenRouter.setServerCapabilities(server.getCapabilities());
381
+ }
382
+ const channel = new StdioConnectionChannel(wire, () => onInstanceClosed(channel), era === "modern" ? modernOutboundIntercept : void 0);
383
+ await product.connect(channel);
384
+ return {
385
+ product,
386
+ channel
387
+ };
388
+ };
389
+ /** Closes an instance whose factory resolved only after the connection was torn down. */
390
+ const disposeLateInstance = (instance) => instance.product.close().catch((error) => reportError(toError(error)));
391
+ const discardProbeInstance = async (instance) => {
392
+ discarding = instance.channel;
393
+ try {
394
+ if (!await instance.channel.whenRequestsAnswered(DISCARD_ANSWER_TIMEOUT_MS)) reportError(/* @__PURE__ */ new Error(`Discarded the probe instance with requests still unanswered after ${DISCARD_ANSWER_TIMEOUT_MS}ms; continuing with the fallback`));
395
+ await instance.product.close();
396
+ } catch (error) {
397
+ reportError(toError(error));
398
+ } finally {
399
+ discarding = void 0;
400
+ }
401
+ };
402
+ const processMessage = async (message) => {
403
+ if (state.phase === "closed") return;
404
+ if (state.phase === "pinned") {
405
+ if (state.era === "modern" && require_mcp.isJSONRPCRequest(message) && message.method === "initialize" && !require_mcp.carriesValidModernEnvelopeClaim(message.params)) {
406
+ await answerLegacyRejection(message, "initialize", message.params !== null && typeof message.params === "object" && typeof message.params.protocolVersion === "string" ? message.params.protocolVersion : void 0);
407
+ return;
408
+ }
409
+ if (state.era === "modern" && await tryServeListen(message)) return;
410
+ state.instance.channel.deliver(message);
411
+ return;
412
+ }
413
+ if (!require_mcp.isJSONRPCRequest(message) && !require_mcp.isJSONRPCNotification(message)) {
414
+ reportError(/* @__PURE__ */ new Error("Discarded a JSON-RPC response received before the connection negotiated an era"));
415
+ return;
416
+ }
417
+ const opening = classifyOpeningMessage(message);
418
+ switch (opening.kind) {
419
+ case "invalid-envelope": {
420
+ const detail = `Invalid _meta envelope for protocol revision 2026-07-28: ${opening.issue.key}: ${opening.issue.problem}`;
421
+ if (require_mcp.isJSONRPCRequest(message)) await writeErrorResponse(message.id, require_mcp.ProtocolErrorCode.InvalidParams, detail, { envelope: opening.issue });
422
+ else reportError(/* @__PURE__ */ new Error(`Discarded a notification with a malformed envelope: ${detail}`));
423
+ return;
424
+ }
425
+ case "unsupported-revision":
426
+ if (require_mcp.isJSONRPCRequest(message)) {
427
+ const error = new require_mcp.UnsupportedProtocolVersionError({
428
+ supported: [...require_mcp.SUPPORTED_MODERN_PROTOCOL_VERSIONS],
429
+ requested: opening.requested
430
+ });
431
+ reportError(error);
432
+ await writeErrorResponse(message.id, error.code, error.message, error.data);
433
+ } else reportError(/* @__PURE__ */ new Error(`Discarded a notification claiming unsupported protocol revision ${opening.requested}`));
434
+ return;
435
+ case "modern":
436
+ if (require_mcp.isJSONRPCRequest(message) && message.method === "server/discover") {
437
+ if (state.phase === "probe") {
438
+ state.instance.channel.deliver(message, { classification: opening.classification });
439
+ return;
440
+ }
441
+ const instance = await connectInstance("modern", opening.revision);
442
+ if (isTornDown()) {
443
+ await disposeLateInstance(instance);
444
+ return;
445
+ }
446
+ state = {
447
+ phase: "probe",
448
+ instance
449
+ };
450
+ instance.channel.deliver(message, { classification: opening.classification });
451
+ return;
452
+ }
453
+ if (state.phase === "probe") {
454
+ if (require_mcp.isJSONRPCNotification(message)) {
455
+ state.instance.channel.deliver(message, { classification: opening.classification });
456
+ return;
457
+ }
458
+ state = {
459
+ phase: "pinned",
460
+ era: "modern",
461
+ instance: state.instance
462
+ };
463
+ } else {
464
+ const instance = await connectInstance("modern", opening.revision);
465
+ if (isTornDown()) {
466
+ await disposeLateInstance(instance);
467
+ return;
468
+ }
469
+ state = {
470
+ phase: "pinned",
471
+ era: "modern",
472
+ instance
473
+ };
474
+ }
475
+ if (await tryServeListen(message)) return;
476
+ state.instance.channel.deliver(message, { classification: opening.classification });
477
+ return;
478
+ case "legacy": {
479
+ if (legacyMode === "reject") {
480
+ if (require_mcp.isJSONRPCRequest(message)) await answerLegacyRejection(message, opening.reason, opening.requestedVersion);
481
+ return;
482
+ }
483
+ if (state.phase === "probe") {
484
+ await discardProbeInstance(state.instance);
485
+ if (isTornDown()) return;
486
+ state = { phase: "opening" };
487
+ }
488
+ const instance = await connectInstance("legacy");
489
+ if (isTornDown()) {
490
+ await disposeLateInstance(instance);
491
+ return;
492
+ }
493
+ state = {
494
+ phase: "pinned",
495
+ era: "legacy",
496
+ instance
497
+ };
498
+ state.instance.channel.deliver(message);
499
+ return;
500
+ }
501
+ }
502
+ };
503
+ const queue = [];
504
+ let pumping = false;
505
+ const pump = async () => {
506
+ if (pumping) return;
507
+ pumping = true;
508
+ try {
509
+ while (queue.length > 0) {
510
+ const message = queue.shift();
511
+ try {
512
+ await processMessage(message);
513
+ } catch (error) {
514
+ if (require_mcp.isJSONRPCRequest(message)) await writeErrorResponse(message.id, require_mcp.ProtocolErrorCode.InternalError, "Internal server error");
515
+ reportError(toError(error));
516
+ }
517
+ }
518
+ } finally {
519
+ pumping = false;
520
+ }
521
+ };
522
+ const closeAll = async () => {
523
+ if (closing || state.phase === "closed") return;
524
+ closing = true;
525
+ const current = state;
526
+ state = { phase: "closed" };
527
+ for (const result of listenRouter.teardownAll()) await wire.send(result).catch((error) => reportError(toError(error)));
528
+ if (current.phase === "probe" || current.phase === "pinned") await current.instance.product.close().catch((error) => reportError(toError(error)));
529
+ await wire.close().catch((error) => reportError(toError(error)));
530
+ };
531
+ wire.onmessage = (message) => {
532
+ queue.push(message);
533
+ pump();
534
+ };
535
+ wire.onerror = (error) => {
536
+ reportError(error);
537
+ if (state.phase === "probe" || state.phase === "pinned") state.instance.channel.onerror?.(error);
538
+ };
539
+ wire.onclose = () => {
540
+ if (closing || state.phase === "closed") return;
541
+ closing = true;
542
+ const current = state;
543
+ state = { phase: "closed" };
544
+ if (current.phase === "probe" || current.phase === "pinned") current.instance.product.close().catch((error) => reportError(toError(error)));
545
+ };
546
+ const started = wire.start().catch((error) => {
547
+ reportError(toError(error));
548
+ throw error;
549
+ });
550
+ started.catch(() => {});
551
+ return { close: async () => {
552
+ await started.catch(() => {});
553
+ await closeAll();
554
+ } };
555
+ }
556
+ function toError(value) {
557
+ return value instanceof Error ? value : new Error(String(value));
558
+ }
559
+
560
+ //#endregion
561
+ exports.StdioServerTransport = StdioServerTransport;
562
+ exports.serveStdio = serveStdio;
563
+ //# sourceMappingURL=stdio.cjs.map