reasonix 0.32.0 → 0.33.1

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 (118) hide show
  1. package/dist/cli/chat-Q5ZCVIOO.js +39 -0
  2. package/dist/cli/chunk-2AWTGJ2C.js +110 -0
  3. package/dist/cli/chunk-2AWTGJ2C.js.map +1 -0
  4. package/dist/cli/chunk-3Q3C4W66.js +30 -0
  5. package/dist/cli/chunk-3Q3C4W66.js.map +1 -0
  6. package/dist/cli/chunk-4DCHFFEY.js +149 -0
  7. package/dist/cli/chunk-4DCHFFEY.js.map +1 -0
  8. package/dist/cli/chunk-5X7LZJDE.js +36 -0
  9. package/dist/cli/chunk-5X7LZJDE.js.map +1 -0
  10. package/dist/cli/chunk-63KAV5DX.js +106 -0
  11. package/dist/cli/chunk-63KAV5DX.js.map +1 -0
  12. package/dist/cli/chunk-6TMHAK5D.js +576 -0
  13. package/dist/cli/chunk-6TMHAK5D.js.map +1 -0
  14. package/dist/cli/chunk-APPB3ZPQ.js +43 -0
  15. package/dist/cli/chunk-APPB3ZPQ.js.map +1 -0
  16. package/dist/cli/chunk-BQNUJJN7.js +42 -0
  17. package/dist/cli/chunk-BQNUJJN7.js.map +1 -0
  18. package/dist/cli/chunk-CPOV2O73.js +39 -0
  19. package/dist/cli/chunk-CPOV2O73.js.map +1 -0
  20. package/dist/cli/chunk-D5DKXIP5.js +368 -0
  21. package/dist/cli/chunk-D5DKXIP5.js.map +1 -0
  22. package/dist/cli/chunk-DFP4YSVM.js +247 -0
  23. package/dist/cli/chunk-DFP4YSVM.js.map +1 -0
  24. package/dist/cli/chunk-DULSP7JH.js +410 -0
  25. package/dist/cli/chunk-DULSP7JH.js.map +1 -0
  26. package/dist/cli/chunk-FM57FNPJ.js +46 -0
  27. package/dist/cli/chunk-FM57FNPJ.js.map +1 -0
  28. package/dist/cli/chunk-FWGEHRB7.js +54 -0
  29. package/dist/cli/chunk-FWGEHRB7.js.map +1 -0
  30. package/dist/cli/chunk-FXGQ5NHE.js +513 -0
  31. package/dist/cli/chunk-FXGQ5NHE.js.map +1 -0
  32. package/dist/cli/chunk-G3XNWSFN.js +53 -0
  33. package/dist/cli/chunk-G3XNWSFN.js.map +1 -0
  34. package/dist/cli/chunk-I6YIAK6C.js +757 -0
  35. package/dist/cli/chunk-I6YIAK6C.js.map +1 -0
  36. package/dist/cli/chunk-J5VLP23S.js +94 -0
  37. package/dist/cli/chunk-J5VLP23S.js.map +1 -0
  38. package/dist/cli/chunk-KMWKGPFZ.js +303 -0
  39. package/dist/cli/chunk-KMWKGPFZ.js.map +1 -0
  40. package/dist/cli/chunk-MDHVWCJ4.js +14965 -0
  41. package/dist/cli/chunk-MDHVWCJ4.js.map +1 -0
  42. package/dist/cli/chunk-MHDNZXJJ.js +48 -0
  43. package/dist/cli/chunk-MHDNZXJJ.js.map +1 -0
  44. package/dist/cli/chunk-ORM6PK57.js +140 -0
  45. package/dist/cli/chunk-ORM6PK57.js.map +1 -0
  46. package/dist/cli/chunk-Q6YFXW7H.js +4986 -0
  47. package/dist/cli/chunk-Q6YFXW7H.js.map +1 -0
  48. package/dist/cli/chunk-QGE6AF76.js +1467 -0
  49. package/dist/cli/chunk-QGE6AF76.js.map +1 -0
  50. package/dist/cli/chunk-RFX7TYVV.js +28 -0
  51. package/dist/cli/chunk-RFX7TYVV.js.map +1 -0
  52. package/dist/cli/chunk-RZILUXUC.js +940 -0
  53. package/dist/cli/chunk-RZILUXUC.js.map +1 -0
  54. package/dist/cli/chunk-SDE5U32Z.js +535 -0
  55. package/dist/cli/chunk-SDE5U32Z.js.map +1 -0
  56. package/dist/cli/chunk-SOZE7V7V.js +340 -0
  57. package/dist/cli/chunk-SOZE7V7V.js.map +1 -0
  58. package/dist/cli/chunk-U3V2ZQ5J.js +479 -0
  59. package/dist/cli/chunk-U3V2ZQ5J.js.map +1 -0
  60. package/dist/cli/chunk-W4LDFAZ6.js +1544 -0
  61. package/dist/cli/chunk-W4LDFAZ6.js.map +1 -0
  62. package/dist/cli/chunk-WBDE4IRI.js +208 -0
  63. package/dist/cli/chunk-WBDE4IRI.js.map +1 -0
  64. package/dist/cli/chunk-XHQIK7B6.js +189 -0
  65. package/dist/cli/chunk-XHQIK7B6.js.map +1 -0
  66. package/dist/cli/chunk-XJLZ4HKU.js +307 -0
  67. package/dist/cli/chunk-XJLZ4HKU.js.map +1 -0
  68. package/dist/cli/chunk-ZPTSJGX5.js +88 -0
  69. package/dist/cli/chunk-ZPTSJGX5.js.map +1 -0
  70. package/dist/cli/chunk-ZTLZO42A.js +231 -0
  71. package/dist/cli/chunk-ZTLZO42A.js.map +1 -0
  72. package/dist/cli/code-DLR77NPZ.js +151 -0
  73. package/dist/cli/code-DLR77NPZ.js.map +1 -0
  74. package/dist/cli/commands-JWT2MWVH.js +352 -0
  75. package/dist/cli/commands-JWT2MWVH.js.map +1 -0
  76. package/dist/cli/commit-RPZBOZS2.js +288 -0
  77. package/dist/cli/commit-RPZBOZS2.js.map +1 -0
  78. package/dist/cli/diff-NTEHCSDW.js +145 -0
  79. package/dist/cli/diff-NTEHCSDW.js.map +1 -0
  80. package/dist/cli/doctor-3TGB2NZN.js +19 -0
  81. package/dist/cli/doctor-3TGB2NZN.js.map +1 -0
  82. package/dist/cli/events-P27CX7LN.js +338 -0
  83. package/dist/cli/events-P27CX7LN.js.map +1 -0
  84. package/dist/cli/index.js +80 -33693
  85. package/dist/cli/index.js.map +1 -1
  86. package/dist/cli/mcp-ARTNQ24O.js +266 -0
  87. package/dist/cli/mcp-ARTNQ24O.js.map +1 -0
  88. package/dist/cli/mcp-browse-HLO2ENDL.js +163 -0
  89. package/dist/cli/mcp-browse-HLO2ENDL.js.map +1 -0
  90. package/dist/cli/mcp-inspect-T2HBR22P.js +103 -0
  91. package/dist/cli/mcp-inspect-T2HBR22P.js.map +1 -0
  92. package/dist/cli/{prompt-XHICFAYN.js → prompt-V47QKSAR.js} +3 -2
  93. package/dist/cli/prompt-V47QKSAR.js.map +1 -0
  94. package/dist/cli/prune-sessions-ERL6B4G5.js +42 -0
  95. package/dist/cli/prune-sessions-ERL6B4G5.js.map +1 -0
  96. package/dist/cli/replay-Q43DSMG6.js +273 -0
  97. package/dist/cli/replay-Q43DSMG6.js.map +1 -0
  98. package/dist/cli/run-JMEOTQCG.js +215 -0
  99. package/dist/cli/run-JMEOTQCG.js.map +1 -0
  100. package/dist/cli/server-SYC3OVOP.js +2967 -0
  101. package/dist/cli/server-SYC3OVOP.js.map +1 -0
  102. package/dist/cli/sessions-MOJAALJI.js +102 -0
  103. package/dist/cli/sessions-MOJAALJI.js.map +1 -0
  104. package/dist/cli/setup-CCJZAWTY.js +404 -0
  105. package/dist/cli/setup-CCJZAWTY.js.map +1 -0
  106. package/dist/cli/stats-5RJCATCE.js +12 -0
  107. package/dist/cli/stats-5RJCATCE.js.map +1 -0
  108. package/dist/cli/update-4TJWRUIN.js +90 -0
  109. package/dist/cli/update-4TJWRUIN.js.map +1 -0
  110. package/dist/cli/version-3MYFE4G6.js +29 -0
  111. package/dist/cli/version-3MYFE4G6.js.map +1 -0
  112. package/dist/index.d.ts +13 -2
  113. package/dist/index.js +493 -89
  114. package/dist/index.js.map +1 -1
  115. package/package.json +1 -1
  116. package/dist/cli/chunk-VWFJNLIK.js +0 -1031
  117. package/dist/cli/chunk-VWFJNLIK.js.map +0 -1
  118. /package/dist/cli/{prompt-XHICFAYN.js.map → chat-Q5ZCVIOO.js.map} +0 -0
@@ -0,0 +1,757 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ VERSION
4
+ } from "./chunk-2AWTGJ2C.js";
5
+
6
+ // src/mcp/types.ts
7
+ var MCP_PROTOCOL_VERSION = "2024-11-05";
8
+ function isJsonRpcError(msg) {
9
+ return "error" in msg;
10
+ }
11
+
12
+ // src/mcp/client.ts
13
+ var McpClient = class {
14
+ transport;
15
+ clientInfo;
16
+ requestTimeoutMs;
17
+ pending = /* @__PURE__ */ new Map();
18
+ nextId = 1;
19
+ readerStarted = false;
20
+ initialized = false;
21
+ _serverCapabilities = {};
22
+ _serverInfo = { name: "", version: "" };
23
+ _protocolVersion = "";
24
+ _instructions;
25
+ // Progress-token → handler for notifications/progress routing. Tokens
26
+ // are minted per call when the caller supplies an onProgress
27
+ // callback; cleared when the final response lands (or the pending
28
+ // request rejects). No leaks — the `try/finally` in callTool
29
+ // guarantees cleanup even on timeout.
30
+ progressHandlers = /* @__PURE__ */ new Map();
31
+ nextProgressToken = 1;
32
+ constructor(opts) {
33
+ this.transport = opts.transport;
34
+ this.clientInfo = opts.clientInfo ?? { name: "reasonix", version: VERSION };
35
+ this.requestTimeoutMs = opts.requestTimeoutMs ?? 6e4;
36
+ }
37
+ /** Server's advertised capabilities, available after initialize(). */
38
+ get serverCapabilities() {
39
+ return this._serverCapabilities;
40
+ }
41
+ /** Server's self-reported name + version, available after initialize(). */
42
+ get serverInfo() {
43
+ return this._serverInfo;
44
+ }
45
+ /** Protocol version the server agreed to during the handshake. */
46
+ get protocolVersion() {
47
+ return this._protocolVersion;
48
+ }
49
+ /** Optional free-form instructions the server provides at handshake. */
50
+ get serverInstructions() {
51
+ return this._instructions;
52
+ }
53
+ /** Compliant servers reject other methods until this completes. */
54
+ async initialize() {
55
+ if (this.initialized) throw new Error("MCP client already initialized");
56
+ this.startReaderIfNeeded();
57
+ const result = await this.request("initialize", {
58
+ protocolVersion: MCP_PROTOCOL_VERSION,
59
+ // Advertise every method the client can consume so servers know
60
+ // they can send listChanged notifications etc. Sub-feature flags
61
+ // (e.g. `resources.subscribe`) are omitted — we don't implement
62
+ // those yet and the empty object means "method-level support, no
63
+ // sub-features."
64
+ capabilities: { tools: {}, resources: {}, prompts: {} },
65
+ clientInfo: this.clientInfo
66
+ });
67
+ this._serverCapabilities = result.capabilities ?? {};
68
+ this._serverInfo = result.serverInfo ?? { name: "", version: "" };
69
+ this._protocolVersion = result.protocolVersion ?? "";
70
+ this._instructions = result.instructions;
71
+ await this.transport.send({
72
+ jsonrpc: "2.0",
73
+ method: "notifications/initialized"
74
+ });
75
+ this.initialized = true;
76
+ return result;
77
+ }
78
+ /** List tools the server exposes. */
79
+ async listTools() {
80
+ this.assertInitialized();
81
+ return this.request("tools/list", {});
82
+ }
83
+ /** Abort sends `notifications/cancelled` and rejects immediately; late server responses are dropped. */
84
+ async callTool(name, args, opts = {}) {
85
+ this.assertInitialized();
86
+ const params = { name, arguments: args ?? {} };
87
+ let token;
88
+ if (opts.onProgress) {
89
+ token = this.nextProgressToken++;
90
+ this.progressHandlers.set(token, opts.onProgress);
91
+ params._meta = { progressToken: token };
92
+ }
93
+ try {
94
+ return await this.request("tools/call", params, opts.signal);
95
+ } finally {
96
+ if (token !== void 0) this.progressHandlers.delete(token);
97
+ }
98
+ }
99
+ /** Throws on method-not-found; callers should gate on `serverCapabilities.resources` first. */
100
+ async listResources(cursor) {
101
+ this.assertInitialized();
102
+ return this.request("resources/list", {
103
+ ...cursor ? { cursor } : {}
104
+ });
105
+ }
106
+ /** Read the contents of a resource by URI. */
107
+ async readResource(uri) {
108
+ this.assertInitialized();
109
+ return this.request("resources/read", {
110
+ uri
111
+ });
112
+ }
113
+ /** List prompt templates the server exposes. */
114
+ async listPrompts(cursor) {
115
+ this.assertInitialized();
116
+ return this.request("prompts/list", {
117
+ ...cursor ? { cursor } : {}
118
+ });
119
+ }
120
+ async getPrompt(name, args) {
121
+ this.assertInitialized();
122
+ return this.request("prompts/get", {
123
+ name,
124
+ ...args ? { arguments: args } : {}
125
+ });
126
+ }
127
+ /** Close the transport and reject any outstanding requests. */
128
+ async close() {
129
+ for (const [, pending] of this.pending) {
130
+ clearTimeout(pending.timeout);
131
+ pending.reject(new Error("MCP client closed"));
132
+ }
133
+ this.pending.clear();
134
+ await this.transport.close();
135
+ }
136
+ assertInitialized() {
137
+ if (!this.initialized) throw new Error("MCP client not initialized \u2014 call initialize() first");
138
+ }
139
+ async request(method, params, signal) {
140
+ const id = this.nextId++;
141
+ const frame = { jsonrpc: "2.0", id, method, params };
142
+ let abortHandler = null;
143
+ const promise = new Promise((resolve, reject) => {
144
+ const timeout = setTimeout(() => {
145
+ this.pending.delete(id);
146
+ if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
147
+ reject(
148
+ new Error(`MCP request ${method} (id=${id}) timed out after ${this.requestTimeoutMs}ms`)
149
+ );
150
+ }, this.requestTimeoutMs);
151
+ this.pending.set(id, {
152
+ resolve,
153
+ reject,
154
+ timeout
155
+ });
156
+ if (signal) {
157
+ if (signal.aborted) {
158
+ this.pending.delete(id);
159
+ clearTimeout(timeout);
160
+ reject(new Error(`MCP request ${method} (id=${id}) aborted before send`));
161
+ return;
162
+ }
163
+ abortHandler = () => {
164
+ this.pending.delete(id);
165
+ clearTimeout(timeout);
166
+ void this.transport.send({
167
+ jsonrpc: "2.0",
168
+ method: "notifications/cancelled",
169
+ params: { requestId: id, reason: "aborted by user" }
170
+ }).catch(() => {
171
+ });
172
+ reject(new Error(`MCP request ${method} (id=${id}) aborted by user`));
173
+ };
174
+ signal.addEventListener("abort", abortHandler, { once: true });
175
+ }
176
+ });
177
+ promise.catch(() => void 0);
178
+ try {
179
+ await Promise.race([this.transport.send(frame), promise.then(() => void 0)]);
180
+ } catch (err) {
181
+ const pending = this.pending.get(id);
182
+ if (pending) clearTimeout(pending.timeout);
183
+ this.pending.delete(id);
184
+ if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
185
+ throw err;
186
+ }
187
+ try {
188
+ return await promise;
189
+ } finally {
190
+ if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
191
+ }
192
+ }
193
+ startReaderIfNeeded() {
194
+ if (this.readerStarted) return;
195
+ this.readerStarted = true;
196
+ void this.readLoop();
197
+ }
198
+ async readLoop() {
199
+ try {
200
+ for await (const msg of this.transport.messages()) {
201
+ this.dispatch(msg);
202
+ }
203
+ } catch (err) {
204
+ for (const [, pending] of this.pending) {
205
+ clearTimeout(pending.timeout);
206
+ pending.reject(err);
207
+ }
208
+ this.pending.clear();
209
+ }
210
+ }
211
+ dispatch(msg) {
212
+ if (!("id" in msg) || msg.id === null || msg.id === void 0) {
213
+ if ("method" in msg && msg.method === "notifications/progress") {
214
+ const p = msg.params;
215
+ if (!p || p.progressToken === void 0) return;
216
+ const handler = this.progressHandlers.get(p.progressToken);
217
+ if (!handler) return;
218
+ handler({ progress: p.progress, total: p.total, message: p.message });
219
+ }
220
+ return;
221
+ }
222
+ if (!("result" in msg) && !("error" in msg)) return;
223
+ const pending = this.pending.get(msg.id);
224
+ if (!pending) return;
225
+ this.pending.delete(msg.id);
226
+ clearTimeout(pending.timeout);
227
+ const resp = msg;
228
+ if (isJsonRpcError(resp)) {
229
+ pending.reject(new Error(`MCP ${resp.error.code}: ${resp.error.message}`));
230
+ } else {
231
+ pending.resolve(resp.result);
232
+ }
233
+ }
234
+ };
235
+
236
+ // src/mcp/stdio.ts
237
+ import { spawn } from "child_process";
238
+ var StdioTransport = class {
239
+ child;
240
+ queue = [];
241
+ waiters = [];
242
+ closed = false;
243
+ stdoutBuffer = "";
244
+ constructor(opts) {
245
+ const env = opts.replaceEnv ? { ...opts.env ?? {} } : { ...process.env, ...opts.env ?? {} };
246
+ const shell = opts.shell ?? process.platform === "win32";
247
+ if (shell) {
248
+ const line = [
249
+ opts.command,
250
+ ...(opts.args ?? []).map((a) => quoteArg(a, process.platform === "win32"))
251
+ ].join(" ");
252
+ this.child = spawn(line, [], {
253
+ env,
254
+ cwd: opts.cwd,
255
+ stdio: ["pipe", "pipe", "inherit"],
256
+ shell: true
257
+ });
258
+ } else {
259
+ this.child = spawn(opts.command, opts.args ?? [], {
260
+ env,
261
+ cwd: opts.cwd,
262
+ stdio: ["pipe", "pipe", "inherit"]
263
+ });
264
+ }
265
+ this.child.stdout.setEncoding("utf8");
266
+ this.child.stdout.on("data", (chunk) => this.onStdout(chunk));
267
+ this.child.on("close", () => this.onClose());
268
+ this.child.on("error", (err) => {
269
+ this.push({
270
+ jsonrpc: "2.0",
271
+ id: null,
272
+ error: { code: -32e3, message: `transport error: ${err.message}` }
273
+ });
274
+ });
275
+ }
276
+ async send(message) {
277
+ if (this.closed) throw new Error("MCP transport is closed");
278
+ return new Promise((resolve, reject) => {
279
+ const line = `${JSON.stringify(message)}
280
+ `;
281
+ this.child.stdin.write(line, "utf8", (err) => {
282
+ if (err) reject(err);
283
+ else resolve();
284
+ });
285
+ });
286
+ }
287
+ async *messages() {
288
+ while (true) {
289
+ if (this.queue.length > 0) {
290
+ yield this.queue.shift();
291
+ continue;
292
+ }
293
+ if (this.closed) return;
294
+ const next = await new Promise((resolve) => {
295
+ this.waiters.push(resolve);
296
+ });
297
+ if (next === null) return;
298
+ yield next;
299
+ }
300
+ }
301
+ async close() {
302
+ if (this.closed) return;
303
+ this.closed = true;
304
+ while (this.waiters.length > 0) this.waiters.shift()(null);
305
+ try {
306
+ this.child.stdin.end();
307
+ } catch {
308
+ }
309
+ if (this.child.exitCode === null && !this.child.killed) {
310
+ try {
311
+ this.child.kill(process.platform === "win32" ? void 0 : "SIGTERM");
312
+ } catch {
313
+ }
314
+ }
315
+ }
316
+ /** Parse incoming stdout chunks into NDJSON messages. */
317
+ onStdout(chunk) {
318
+ this.stdoutBuffer += chunk;
319
+ let newlineIdx;
320
+ while ((newlineIdx = this.stdoutBuffer.indexOf("\n")) !== -1) {
321
+ const line = this.stdoutBuffer.slice(0, newlineIdx).trim();
322
+ this.stdoutBuffer = this.stdoutBuffer.slice(newlineIdx + 1);
323
+ if (!line) continue;
324
+ try {
325
+ const msg = JSON.parse(line);
326
+ this.push(msg);
327
+ } catch {
328
+ }
329
+ }
330
+ }
331
+ onClose() {
332
+ this.closed = true;
333
+ while (this.waiters.length > 0) this.waiters.shift()(null);
334
+ }
335
+ push(msg) {
336
+ const waiter = this.waiters.shift();
337
+ if (waiter) waiter(msg);
338
+ else this.queue.push(msg);
339
+ }
340
+ };
341
+ function quoteArg(s, windows) {
342
+ if (!windows) {
343
+ return `'${s.replace(/'/g, "'\\''")}'`;
344
+ }
345
+ return `"${s.replace(/"/g, '""')}"`;
346
+ }
347
+
348
+ // src/mcp/sse.ts
349
+ import { createParser } from "eventsource-parser";
350
+ var SseTransport = class {
351
+ url;
352
+ headers;
353
+ queue = [];
354
+ waiters = [];
355
+ controller = new AbortController();
356
+ closed = false;
357
+ postUrl = null;
358
+ endpointReady;
359
+ resolveEndpoint;
360
+ rejectEndpoint;
361
+ constructor(opts) {
362
+ this.url = opts.url;
363
+ this.headers = opts.headers ?? {};
364
+ this.endpointReady = new Promise((resolve, reject) => {
365
+ this.resolveEndpoint = resolve;
366
+ this.rejectEndpoint = reject;
367
+ });
368
+ this.endpointReady.catch(() => void 0);
369
+ void this.runStream();
370
+ }
371
+ async send(message) {
372
+ if (this.closed) throw new Error("MCP SSE transport is closed");
373
+ const postUrl = await this.endpointReady;
374
+ const res = await fetch(postUrl, {
375
+ method: "POST",
376
+ headers: { "content-type": "application/json", ...this.headers },
377
+ body: JSON.stringify(message),
378
+ signal: this.controller.signal
379
+ });
380
+ await res.arrayBuffer().catch(() => void 0);
381
+ if (!res.ok) {
382
+ throw new Error(`MCP SSE POST ${postUrl} failed: ${res.status} ${res.statusText}`);
383
+ }
384
+ }
385
+ async *messages() {
386
+ while (true) {
387
+ if (this.queue.length > 0) {
388
+ yield this.queue.shift();
389
+ continue;
390
+ }
391
+ if (this.closed) return;
392
+ const next = await new Promise((resolve) => {
393
+ this.waiters.push(resolve);
394
+ });
395
+ if (next === null) return;
396
+ yield next;
397
+ }
398
+ }
399
+ async close() {
400
+ if (this.closed) return;
401
+ this.closed = true;
402
+ while (this.waiters.length > 0) this.waiters.shift()(null);
403
+ this.rejectEndpoint(new Error("MCP SSE transport closed before endpoint was ready"));
404
+ try {
405
+ this.controller.abort();
406
+ } catch {
407
+ }
408
+ }
409
+ async runStream() {
410
+ let res;
411
+ try {
412
+ res = await fetch(this.url, {
413
+ method: "GET",
414
+ headers: { accept: "text/event-stream", ...this.headers },
415
+ signal: this.controller.signal
416
+ });
417
+ } catch (err) {
418
+ this.failHandshake(`SSE connect to ${this.url} failed: ${err.message}`);
419
+ return;
420
+ }
421
+ if (!res.ok || !res.body) {
422
+ await res.body?.cancel().catch(() => void 0);
423
+ this.failHandshake(`SSE handshake ${this.url} \u2192 ${res.status} ${res.statusText}`);
424
+ return;
425
+ }
426
+ const parser = createParser({
427
+ onEvent: (ev) => this.handleEvent(ev.event ?? "message", ev.data)
428
+ });
429
+ const decoder = new TextDecoder();
430
+ try {
431
+ for await (const chunk of res.body) {
432
+ parser.feed(decoder.decode(chunk, { stream: true }));
433
+ }
434
+ } catch (err) {
435
+ if (!this.closed) {
436
+ this.pushError(`SSE stream error: ${err.message}`);
437
+ }
438
+ } finally {
439
+ this.markClosed();
440
+ }
441
+ }
442
+ handleEvent(type, data) {
443
+ if (type === "endpoint") {
444
+ if (this.postUrl) return;
445
+ try {
446
+ this.postUrl = new URL(data, this.url).toString();
447
+ this.resolveEndpoint(this.postUrl);
448
+ } catch (err) {
449
+ this.failHandshake(`SSE endpoint event had bad URL "${data}": ${err.message}`);
450
+ }
451
+ return;
452
+ }
453
+ if (type === "message") {
454
+ try {
455
+ const parsed = JSON.parse(data);
456
+ this.pushMessage(parsed);
457
+ } catch {
458
+ }
459
+ return;
460
+ }
461
+ }
462
+ failHandshake(reason) {
463
+ this.rejectEndpoint(new Error(reason));
464
+ this.pushError(reason);
465
+ this.markClosed();
466
+ }
467
+ pushMessage(msg) {
468
+ const waiter = this.waiters.shift();
469
+ if (waiter) waiter(msg);
470
+ else this.queue.push(msg);
471
+ }
472
+ pushError(message) {
473
+ this.pushMessage({
474
+ jsonrpc: "2.0",
475
+ id: null,
476
+ error: { code: -32e3, message }
477
+ });
478
+ }
479
+ markClosed() {
480
+ if (this.closed) return;
481
+ this.closed = true;
482
+ while (this.waiters.length > 0) this.waiters.shift()(null);
483
+ }
484
+ };
485
+
486
+ // src/mcp/streamable-http.ts
487
+ import { createParser as createParser2 } from "eventsource-parser";
488
+ var SESSION_HEADER = "mcp-session-id";
489
+ var StreamableHttpTransport = class {
490
+ url;
491
+ extraHeaders;
492
+ queue = [];
493
+ waiters = [];
494
+ controller = new AbortController();
495
+ /** Session id minted by server on (typically) the initialize response. */
496
+ sessionId = null;
497
+ closed = false;
498
+ /** Background SSE read-loops kicked off by send(); awaited on close(). */
499
+ streams = /* @__PURE__ */ new Set();
500
+ constructor(opts) {
501
+ this.url = opts.url;
502
+ this.extraHeaders = opts.headers ?? {};
503
+ }
504
+ async send(message) {
505
+ if (this.closed) throw new Error("MCP Streamable HTTP transport is closed");
506
+ const headers = {
507
+ "content-type": "application/json",
508
+ // Both accepted — server picks. application/json first signals a
509
+ // mild preference for the simpler shape when the response is a
510
+ // single message.
511
+ accept: "application/json, text/event-stream",
512
+ ...this.extraHeaders
513
+ };
514
+ if (this.sessionId !== null) headers["mcp-session-id"] = this.sessionId;
515
+ let res;
516
+ try {
517
+ res = await fetch(this.url, {
518
+ method: "POST",
519
+ headers,
520
+ body: JSON.stringify(message),
521
+ signal: this.controller.signal
522
+ });
523
+ } catch (err) {
524
+ throw new Error(`MCP Streamable HTTP POST ${this.url} failed: ${err.message}`);
525
+ }
526
+ const serverSessionId = res.headers.get(SESSION_HEADER);
527
+ if (serverSessionId && this.sessionId === null) {
528
+ this.sessionId = serverSessionId;
529
+ }
530
+ if (res.status === 404 && this.sessionId !== null) {
531
+ await res.body?.cancel().catch(() => void 0);
532
+ throw new Error(
533
+ `MCP Streamable HTTP session expired (server returned 404 with Mcp-Session-Id "${this.sessionId}"). Reinitialize the client.`
534
+ );
535
+ }
536
+ if (!res.ok) {
537
+ const body = await res.text().catch(() => "");
538
+ throw new Error(
539
+ `MCP Streamable HTTP POST ${this.url} \u2192 ${res.status} ${res.statusText}${body ? `: ${body}` : ""}`
540
+ );
541
+ }
542
+ if (res.status === 202) {
543
+ await res.body?.cancel().catch(() => void 0);
544
+ return;
545
+ }
546
+ const ct = (res.headers.get("content-type") ?? "").toLowerCase();
547
+ if (ct.includes("application/json")) {
548
+ let parsed;
549
+ try {
550
+ parsed = await res.json();
551
+ } catch (err) {
552
+ throw new Error(`MCP Streamable HTTP body wasn't valid JSON: ${err.message}`);
553
+ }
554
+ if (Array.isArray(parsed)) {
555
+ for (const item of parsed) this.pushMessage(item);
556
+ } else {
557
+ this.pushMessage(parsed);
558
+ }
559
+ return;
560
+ }
561
+ if (ct.includes("text/event-stream")) {
562
+ if (!res.body) {
563
+ throw new Error("MCP Streamable HTTP SSE response had no body");
564
+ }
565
+ const stream = this.consumeStream(res.body);
566
+ this.streams.add(stream);
567
+ stream.finally(() => this.streams.delete(stream));
568
+ return;
569
+ }
570
+ await res.body?.cancel().catch(() => void 0);
571
+ }
572
+ async *messages() {
573
+ while (true) {
574
+ if (this.queue.length > 0) {
575
+ yield this.queue.shift();
576
+ continue;
577
+ }
578
+ if (this.closed) return;
579
+ const next = await new Promise((resolve) => {
580
+ this.waiters.push(resolve);
581
+ });
582
+ if (next === null) return;
583
+ yield next;
584
+ }
585
+ }
586
+ async close() {
587
+ if (this.closed) return;
588
+ this.closed = true;
589
+ while (this.waiters.length > 0) this.waiters.shift()(null);
590
+ try {
591
+ this.controller.abort();
592
+ } catch {
593
+ }
594
+ await Promise.allSettled(Array.from(this.streams));
595
+ }
596
+ /** Visible for tests — confirm session header round-trip. */
597
+ getSessionId() {
598
+ return this.sessionId;
599
+ }
600
+ async consumeStream(body) {
601
+ const parser = createParser2({
602
+ onEvent: (ev) => {
603
+ const type = ev.event ?? "message";
604
+ if (type !== "message") return;
605
+ try {
606
+ const parsed = JSON.parse(ev.data);
607
+ this.pushMessage(parsed);
608
+ } catch {
609
+ }
610
+ }
611
+ });
612
+ const decoder = new TextDecoder();
613
+ try {
614
+ for await (const chunk of body) {
615
+ if (this.closed) break;
616
+ parser.feed(decoder.decode(chunk, { stream: true }));
617
+ }
618
+ } catch (err) {
619
+ if (!this.closed) {
620
+ this.pushMessage({
621
+ jsonrpc: "2.0",
622
+ id: null,
623
+ error: {
624
+ code: -32e3,
625
+ message: `Streamable HTTP stream error: ${err.message}`
626
+ }
627
+ });
628
+ }
629
+ }
630
+ }
631
+ pushMessage(msg) {
632
+ const waiter = this.waiters.shift();
633
+ if (waiter) waiter(msg);
634
+ else this.queue.push(msg);
635
+ }
636
+ };
637
+
638
+ // src/mcp/shell-split.ts
639
+ function shellSplit(input) {
640
+ const tokens = [];
641
+ let cur = "";
642
+ let quote = null;
643
+ let i = 0;
644
+ const s = input;
645
+ while (i < s.length) {
646
+ const ch = s[i];
647
+ if (quote) {
648
+ if (ch === quote) {
649
+ quote = null;
650
+ i++;
651
+ continue;
652
+ }
653
+ if (ch === "\\" && quote === '"' && i + 1 < s.length) {
654
+ cur += s[i + 1];
655
+ i += 2;
656
+ continue;
657
+ }
658
+ cur += ch;
659
+ i++;
660
+ continue;
661
+ }
662
+ if (ch === '"' || ch === "'") {
663
+ quote = ch;
664
+ i++;
665
+ continue;
666
+ }
667
+ if (ch === " " || ch === " ") {
668
+ if (cur.length > 0) {
669
+ tokens.push(cur);
670
+ cur = "";
671
+ }
672
+ i++;
673
+ continue;
674
+ }
675
+ cur += ch;
676
+ i++;
677
+ }
678
+ if (quote) {
679
+ throw new Error(
680
+ `shellSplit: unterminated ${quote === '"' ? "double" : "single"} quote in input`
681
+ );
682
+ }
683
+ if (cur.length > 0) tokens.push(cur);
684
+ return tokens;
685
+ }
686
+
687
+ // src/mcp/spec.ts
688
+ var NAME_PREFIX = /^([a-zA-Z_][a-zA-Z0-9_-]*)=(.*)$/;
689
+ var HTTP_URL = /^https?:\/\//i;
690
+ var STREAMABLE_PREFIX = /^streamable\+(https?:\/\/.+)$/i;
691
+ function parseMcpSpec(input) {
692
+ const trimmed = input.trim();
693
+ if (!trimmed) {
694
+ throw new Error("empty MCP spec");
695
+ }
696
+ const nameMatch = NAME_PREFIX.exec(trimmed);
697
+ const name = nameMatch ? nameMatch[1] : null;
698
+ const body = (nameMatch ? nameMatch[2] : trimmed).trim();
699
+ if (!body) {
700
+ throw new Error(`MCP spec has name but no command: ${input}`);
701
+ }
702
+ const streamMatch = STREAMABLE_PREFIX.exec(body);
703
+ if (streamMatch) {
704
+ return { transport: "streamable-http", name, url: streamMatch[1] };
705
+ }
706
+ if (HTTP_URL.test(body)) {
707
+ return { transport: "sse", name, url: body };
708
+ }
709
+ const argv = shellSplit(body);
710
+ if (argv.length === 0) {
711
+ throw new Error(`MCP spec has name but no command: ${input}`);
712
+ }
713
+ const [command, ...args] = argv;
714
+ return { transport: "stdio", name, command, args };
715
+ }
716
+
717
+ // src/mcp/inspect.ts
718
+ async function inspectMcpServer(client) {
719
+ const t0 = Date.now();
720
+ const tools = await trySection(() => client.listTools().then((r) => r.tools));
721
+ const resources = await trySection(
722
+ () => client.listResources().then((r) => r.resources)
723
+ );
724
+ const prompts = await trySection(() => client.listPrompts().then((r) => r.prompts));
725
+ return {
726
+ protocolVersion: client.protocolVersion || "(unknown)",
727
+ serverInfo: client.serverInfo,
728
+ capabilities: client.serverCapabilities ?? {},
729
+ instructions: client.serverInstructions,
730
+ tools,
731
+ resources,
732
+ prompts,
733
+ elapsedMs: Date.now() - t0
734
+ };
735
+ }
736
+ async function trySection(load) {
737
+ try {
738
+ const items = await load();
739
+ return { supported: true, items };
740
+ } catch (err) {
741
+ const msg = err.message ?? String(err);
742
+ if (/-32601/.test(msg) || /method not found/i.test(msg)) {
743
+ return { supported: false, reason: "method not found (-32601)" };
744
+ }
745
+ return { supported: false, reason: msg };
746
+ }
747
+ }
748
+
749
+ export {
750
+ McpClient,
751
+ StdioTransport,
752
+ SseTransport,
753
+ StreamableHttpTransport,
754
+ parseMcpSpec,
755
+ inspectMcpServer
756
+ };
757
+ //# sourceMappingURL=chunk-I6YIAK6C.js.map