@ai-ecoverse/mcpcat 0.10.0 → 0.12.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.
package/README.md CHANGED
@@ -2,10 +2,16 @@
2
2
 
3
3
  A curl for the Model Context Protocol. One command that talks to any MCP server from a shell, and turns any shell command into an MCP server.
4
4
 
5
+ ## Install
6
+
7
+ ```sh
8
+ curl -fsSL https://github.com/ai-ecoverse/mcpcat/releases/latest/download/install.sh | sh
9
+ ```
10
+
11
+ Or via npm:
12
+
5
13
  ```sh
6
14
  npm install -g @ai-ecoverse/mcpcat
7
- mcpcat --version
8
- mcpcat --help
9
15
  ```
10
16
 
11
17
  mcpcat runs natively on macOS, Linux and Windows (Node ≥ 24), and inside Sliccy Seven as a kernel program, with the same flags, the same output and the same config file in both places.
@@ -7773,6 +7773,14 @@ var McpAuthError = class extends Error {
7773
7773
  this.target = target;
7774
7774
  }
7775
7775
  };
7776
+ var AuthRetryableError = class extends Error {
7777
+ response;
7778
+ constructor(response) {
7779
+ super(`HTTP ${response.status}: auth retry`);
7780
+ this.name = "AuthRetryableError";
7781
+ this.response = response;
7782
+ }
7783
+ };
7776
7784
  function selectSseFrame(body, targetId) {
7777
7785
  const text = new TextDecoder().decode(body);
7778
7786
  const blocks = text.replace(/\r\n/g, "\n").split(/\n\n/);
@@ -7797,6 +7805,42 @@ function mcpNameForMethod(method, params) {
7797
7805
  if (method === "resources/read") return params.uri;
7798
7806
  return void 0;
7799
7807
  }
7808
+ function makeAbort(timeoutMs, externalSignal) {
7809
+ const controller = new AbortController();
7810
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
7811
+ if (externalSignal) {
7812
+ if (externalSignal.aborted) {
7813
+ clearTimeout(timer);
7814
+ controller.abort();
7815
+ } else {
7816
+ externalSignal.addEventListener(
7817
+ "abort",
7818
+ () => {
7819
+ clearTimeout(timer);
7820
+ controller.abort();
7821
+ },
7822
+ { once: true }
7823
+ );
7824
+ }
7825
+ }
7826
+ return { controller, timer };
7827
+ }
7828
+ function logHeaders(verbose, dir, entries) {
7829
+ for (const [k, v] of entries) {
7830
+ if (dir === ">") verbose.headerOut(k, v);
7831
+ else verbose.headerIn(k, v);
7832
+ }
7833
+ }
7834
+ function buildServerInfo(discoverResult2) {
7835
+ return {
7836
+ name: discoverResult2?.serverInfo?.name,
7837
+ version: discoverResult2?.serverInfo?.version,
7838
+ title: discoverResult2?.serverInfo?.title,
7839
+ protocolVersion: STATELESS_PROTOCOL_VERSION,
7840
+ capabilities: discoverResult2?.capabilities ?? {},
7841
+ instructions: discoverResult2?.instructions
7842
+ };
7843
+ }
7800
7844
  async function createStatelessConnection(options) {
7801
7845
  const {
7802
7846
  url,
@@ -7805,11 +7849,12 @@ async function createStatelessConnection(options) {
7805
7849
  timeoutMs = 6e4,
7806
7850
  clientInfo = { name: "mcpcat", version: "0.0.0" },
7807
7851
  signal: externalSignal,
7808
- verbose
7852
+ verbose,
7853
+ authProvider
7809
7854
  } = options;
7810
7855
  let nextId2 = 1;
7811
7856
  let negotiatedVersion;
7812
- function buildHeaders(method, params) {
7857
+ function buildHeaders(method, params, token) {
7813
7858
  const h = {
7814
7859
  "Content-Type": "application/json",
7815
7860
  Accept: "application/json, text/event-stream",
@@ -7819,36 +7864,12 @@ async function createStatelessConnection(options) {
7819
7864
  const n = mcpNameForMethod(method, params);
7820
7865
  if (n) h["Mcp-Name"] = n;
7821
7866
  if (negotiatedVersion) h["Mcp-Protocol-Version"] = negotiatedVersion;
7867
+ if (token) h["Authorization"] = `Bearer ${token}`;
7822
7868
  return h;
7823
7869
  }
7824
- function makeAbort() {
7825
- const controller = new AbortController();
7826
- const timer = setTimeout(() => controller.abort(), timeoutMs);
7827
- if (externalSignal) {
7828
- if (externalSignal.aborted) {
7829
- clearTimeout(timer);
7830
- controller.abort();
7831
- } else {
7832
- externalSignal.addEventListener(
7833
- "abort",
7834
- () => {
7835
- clearTimeout(timer);
7836
- controller.abort();
7837
- },
7838
- { once: true }
7839
- );
7840
- }
7841
- }
7842
- return { controller, timer };
7843
- }
7844
- function logHeaders(dir, entries) {
7845
- if (!verbose?.enabled) return;
7846
- for (const [k, v] of entries) {
7847
- if (dir === ">") verbose.headerOut(k, v);
7848
- else verbose.headerIn(k, v);
7849
- }
7850
- }
7851
7870
  async function handleErrorStatus(res, method) {
7871
+ if ((res.status === 401 || res.status === 403) && authProvider)
7872
+ throw new AuthRetryableError(res);
7852
7873
  if (res.status === 401 || res.status === 403) throw new McpAuthError(res.status, url);
7853
7874
  const text = await res.text();
7854
7875
  let rpc;
@@ -7860,34 +7881,27 @@ async function createStatelessConnection(options) {
7860
7881
  if (rpc) throw new StatelessRpcError(rpc.code, rpc.message, rpc.data);
7861
7882
  throw new StatelessHttpError(res.status, res.statusText, method, text.slice(0, 512));
7862
7883
  }
7863
- async function request(method, params) {
7864
- const id = nextId2++;
7865
- const headers = buildHeaders(method, params);
7866
- const meta = {
7867
- protocolVersion: negotiatedVersion ?? STATELESS_PROTOCOL_VERSION,
7868
- clientInfo
7869
- };
7870
- const body = JSON.stringify({
7871
- jsonrpc: "2.0",
7872
- id,
7873
- method,
7874
- params: { ...params, _meta: meta }
7875
- });
7884
+ async function doFetch(method, params, body) {
7885
+ const token = await authProvider?.token();
7886
+ const headers = buildHeaders(method, params, token);
7876
7887
  if (verbose?.enabled) {
7877
7888
  verbose.request(method, body);
7878
- logHeaders(">", Object.entries(headers));
7889
+ logHeaders(verbose, ">", Object.entries(headers));
7879
7890
  }
7880
- const { controller, timer } = makeAbort();
7881
- let res;
7891
+ const { controller, timer } = makeAbort(timeoutMs, externalSignal);
7882
7892
  try {
7883
- res = await fetchImpl(url, {
7893
+ const res = await fetchImpl(url, {
7884
7894
  method: "POST",
7885
7895
  headers,
7886
7896
  body,
7887
7897
  signal: controller.signal
7888
7898
  });
7899
+ if (verbose?.enabled) {
7900
+ verbose.http("<", `HTTP/1.1 ${res.status} ${res.statusText}`);
7901
+ logHeaders(verbose, "<", res.headers.entries());
7902
+ }
7903
+ return { res, token };
7889
7904
  } catch (err) {
7890
- clearTimeout(timer);
7891
7905
  if (controller.signal.aborted && !externalSignal?.aborted) {
7892
7906
  throw new McpTimeoutError2(method, timeoutMs);
7893
7907
  }
@@ -7895,11 +7909,8 @@ async function createStatelessConnection(options) {
7895
7909
  } finally {
7896
7910
  clearTimeout(timer);
7897
7911
  }
7898
- if (verbose?.enabled) {
7899
- verbose.http("<", `HTTP/1.1 ${res.status} ${res.statusText}`);
7900
- logHeaders("<", res.headers.entries());
7901
- }
7902
- if (res.status >= 400) await handleErrorStatus(res, method);
7912
+ }
7913
+ async function parseResponse(res, id) {
7903
7914
  const ct = res.headers.get("content-type") ?? "";
7904
7915
  let frame;
7905
7916
  if (ct.toLowerCase().includes("text/event-stream")) {
@@ -7913,10 +7924,53 @@ async function createStatelessConnection(options) {
7913
7924
  }
7914
7925
  return frame.result;
7915
7926
  }
7927
+ async function handleAuthError(err, token) {
7928
+ if (!authProvider?.onUnauthorized) throw new McpAuthError(err.response.status, url);
7929
+ try {
7930
+ await authProvider.onUnauthorized({
7931
+ response: err.response,
7932
+ serverUrl: new URL(url),
7933
+ fetch: fetchImpl,
7934
+ token
7935
+ });
7936
+ } catch (authErr) {
7937
+ if (authErr instanceof AuthStepUpError) throw authErr;
7938
+ throw new McpAuthError(err.response.status, url);
7939
+ }
7940
+ }
7941
+ async function request(method, params) {
7942
+ const id = nextId2++;
7943
+ const meta = {
7944
+ protocolVersion: negotiatedVersion ?? STATELESS_PROTOCOL_VERSION,
7945
+ clientInfo
7946
+ };
7947
+ const body = JSON.stringify({
7948
+ jsonrpc: "2.0",
7949
+ id,
7950
+ method,
7951
+ params: { ...params, _meta: meta }
7952
+ });
7953
+ for (let attempt = 0; attempt < 2; attempt++) {
7954
+ const { res, token } = await doFetch(method, params, body);
7955
+ if (res.status >= 400) {
7956
+ try {
7957
+ await handleErrorStatus(res, method);
7958
+ } catch (err) {
7959
+ if (err instanceof AuthRetryableError && attempt === 0) {
7960
+ await handleAuthError(err, token);
7961
+ continue;
7962
+ }
7963
+ throw err;
7964
+ }
7965
+ }
7966
+ return parseResponse(res, id);
7967
+ }
7968
+ throw new McpAuthError(401, url);
7969
+ }
7916
7970
  const discoverResult2 = await request("server/discover", {
7917
7971
  protocolVersion: STATELESS_PROTOCOL_VERSION
7918
7972
  });
7919
- const supported = Array.isArray(discoverResult2?.supportedVersions) ? discoverResult2.supportedVersions.filter((v) => typeof v === "string") : [];
7973
+ const supported = discoverResult2?.supportedVersions?.filter((v) => typeof v === "string") ?? [];
7920
7974
  if (!supported.includes(STATELESS_PROTOCOL_VERSION)) {
7921
7975
  throw new Error(
7922
7976
  `Server does not support ${STATELESS_PROTOCOL_VERSION} (advertised: ${supported.join(", ") || "none"})`
@@ -7927,15 +7981,7 @@ async function createStatelessConnection(options) {
7927
7981
  return {
7928
7982
  async info() {
7929
7983
  if (serverInfoCache) return serverInfoCache;
7930
- const r = discoverResult2;
7931
- serverInfoCache = {
7932
- name: r?.serverInfo?.name,
7933
- version: r?.serverInfo?.version,
7934
- title: r?.serverInfo?.title,
7935
- protocolVersion: STATELESS_PROTOCOL_VERSION,
7936
- capabilities: r?.capabilities ?? {},
7937
- instructions: r?.instructions
7938
- };
7984
+ serverInfoCache = buildServerInfo(discoverResult2);
7939
7985
  return serverInfoCache;
7940
7986
  },
7941
7987
  async listTools() {
@@ -8085,23 +8131,12 @@ async function connect(platform2, options) {
8085
8131
  });
8086
8132
  }
8087
8133
  if (!options.url) throw new Error("connect requires either a url or a command");
8088
- if (options.authProvider) {
8089
- const token = await options.authProvider.token();
8090
- if (token) {
8091
- return createPiConnection({
8092
- url: options.url,
8093
- headers: options.headers,
8094
- fetchImpl: platform2.fetch,
8095
- authProvider: options.authProvider,
8096
- ...shared
8097
- });
8098
- }
8099
- }
8100
8134
  if (options.protocolVersion && !options.protocolVersion.startsWith("2026")) {
8101
8135
  return createPiConnection({
8102
8136
  url: options.url,
8103
8137
  headers: options.headers,
8104
8138
  fetchImpl: platform2.fetch,
8139
+ authProvider: options.authProvider,
8105
8140
  ...shared
8106
8141
  });
8107
8142
  }
@@ -8113,11 +8148,14 @@ async function connect(platform2, options) {
8113
8148
  clientInfo: { name: "mcpcat", version: options.version ?? "0.0.0" },
8114
8149
  timeoutMs: options.timeoutMs,
8115
8150
  signal: options.signal,
8116
- verbose: options.verbose
8151
+ verbose: options.verbose,
8152
+ authProvider: options.authProvider
8117
8153
  });
8118
8154
  } catch (err) {
8119
8155
  const isMethodNotFound = err instanceof StatelessRpcError && err.code === -32601 || err instanceof StatelessHttpError && err.status >= 400 && err.status < 500;
8120
8156
  if (options.protocolVersion?.startsWith("2026")) throw err;
8157
+ if (err instanceof McpAuthError || err instanceof AuthStepUpError) throw err;
8158
+ if (err.name === "McpOAuthAuthorizationRequiredError") throw err;
8121
8159
  if (isMethodNotFound) {
8122
8160
  return createPiConnection({
8123
8161
  url: options.url,
@@ -8605,13 +8643,16 @@ async function createSandbox(platform2, tools, globals, timeoutMs) {
8605
8643
  return new InlineCodemodeSandbox2({ tools, globals, timeoutMs, wasm });
8606
8644
  }
8607
8645
  const { CodemodeSandbox: CodemodeSandbox2, loadQuickJSWasm: loadQuickJSWasm2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
8608
- const wasmPath = await resolveWasmPath(platform2);
8646
+ const getSeaDir = globalThis.__mcpcat_get_sea_asset_dir;
8647
+ const seaDir = getSeaDir?.();
8648
+ const wasmPath = seaDir ? `${seaDir}/quickjs.wasm` : await resolveWasmPath(platform2);
8649
+ const workerUrl = seaDir ? new URL(`file://${seaDir}/codemode-worker.mjs`) : new URL("./codemode-worker.mjs", import.meta.url);
8609
8650
  return new CodemodeSandbox2({
8610
8651
  tools,
8611
8652
  globals,
8612
8653
  timeoutMs,
8613
8654
  wasm: loadQuickJSWasm2(wasmPath),
8614
- workerUrl: new URL("./codemode-worker.mjs", import.meta.url)
8655
+ workerUrl
8615
8656
  });
8616
8657
  }
8617
8658
  async function resolveWasmPath(platform2) {
@@ -14049,11 +14090,20 @@ async function runClientVerb(platform2, version, parsed) {
14049
14090
  return EXIT_AUTH;
14050
14091
  }
14051
14092
  const msg = transportHint(err) ?? `connection failed: ${err.message}`;
14093
+ if (interrupted) {
14094
+ await platform2.write(2, "mcpcat: interrupted\n");
14095
+ return EXIT_INTERRUPTED;
14096
+ }
14052
14097
  await writeErr(platform2, parsed.jsonOutput, "connection", msg);
14053
14098
  return EXIT_CONNECTION;
14054
14099
  }
14055
14100
  try {
14056
- return await runVerb(platform2, parsed, conn, onProgress);
14101
+ const code2 = await runVerb(platform2, parsed, conn, onProgress);
14102
+ if (interrupted) {
14103
+ await platform2.write(2, "mcpcat: interrupted\n");
14104
+ return EXIT_INTERRUPTED;
14105
+ }
14106
+ return code2;
14057
14107
  } catch (err) {
14058
14108
  if (interrupted) {
14059
14109
  await platform2.write(2, "mcpcat: interrupted\n");
@@ -14277,5 +14327,5 @@ function createHostPlatform() {
14277
14327
  // src/host.ts
14278
14328
  var platform = createHostPlatform();
14279
14329
  var args = process.argv.slice(2);
14280
- var code = await run(platform, "0.10.0", args);
14330
+ var code = await run(platform, "0.12.0", args);
14281
14331
  process.exit(code);
@@ -7386,6 +7386,14 @@ var McpAuthError = class extends Error {
7386
7386
  this.target = target;
7387
7387
  }
7388
7388
  };
7389
+ var AuthRetryableError = class extends Error {
7390
+ response;
7391
+ constructor(response) {
7392
+ super(`HTTP ${response.status}: auth retry`);
7393
+ this.name = "AuthRetryableError";
7394
+ this.response = response;
7395
+ }
7396
+ };
7389
7397
  function selectSseFrame(body, targetId) {
7390
7398
  const text = new TextDecoder().decode(body);
7391
7399
  const blocks = text.replace(/\r\n/g, "\n").split(/\n\n/);
@@ -7410,6 +7418,42 @@ function mcpNameForMethod(method, params) {
7410
7418
  if (method === "resources/read") return params.uri;
7411
7419
  return void 0;
7412
7420
  }
7421
+ function makeAbort(timeoutMs, externalSignal) {
7422
+ const controller = new AbortController();
7423
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
7424
+ if (externalSignal) {
7425
+ if (externalSignal.aborted) {
7426
+ clearTimeout(timer);
7427
+ controller.abort();
7428
+ } else {
7429
+ externalSignal.addEventListener(
7430
+ "abort",
7431
+ () => {
7432
+ clearTimeout(timer);
7433
+ controller.abort();
7434
+ },
7435
+ { once: true }
7436
+ );
7437
+ }
7438
+ }
7439
+ return { controller, timer };
7440
+ }
7441
+ function logHeaders(verbose, dir, entries) {
7442
+ for (const [k, v] of entries) {
7443
+ if (dir === ">") verbose.headerOut(k, v);
7444
+ else verbose.headerIn(k, v);
7445
+ }
7446
+ }
7447
+ function buildServerInfo(discoverResult2) {
7448
+ return {
7449
+ name: discoverResult2?.serverInfo?.name,
7450
+ version: discoverResult2?.serverInfo?.version,
7451
+ title: discoverResult2?.serverInfo?.title,
7452
+ protocolVersion: STATELESS_PROTOCOL_VERSION,
7453
+ capabilities: discoverResult2?.capabilities ?? {},
7454
+ instructions: discoverResult2?.instructions
7455
+ };
7456
+ }
7413
7457
  async function createStatelessConnection(options) {
7414
7458
  const {
7415
7459
  url,
@@ -7418,11 +7462,12 @@ async function createStatelessConnection(options) {
7418
7462
  timeoutMs = 6e4,
7419
7463
  clientInfo = { name: "mcpcat", version: "0.0.0" },
7420
7464
  signal: externalSignal,
7421
- verbose
7465
+ verbose,
7466
+ authProvider
7422
7467
  } = options;
7423
7468
  let nextId2 = 1;
7424
7469
  let negotiatedVersion;
7425
- function buildHeaders(method, params) {
7470
+ function buildHeaders(method, params, token) {
7426
7471
  const h = {
7427
7472
  "Content-Type": "application/json",
7428
7473
  Accept: "application/json, text/event-stream",
@@ -7432,36 +7477,12 @@ async function createStatelessConnection(options) {
7432
7477
  const n = mcpNameForMethod(method, params);
7433
7478
  if (n) h["Mcp-Name"] = n;
7434
7479
  if (negotiatedVersion) h["Mcp-Protocol-Version"] = negotiatedVersion;
7480
+ if (token) h["Authorization"] = `Bearer ${token}`;
7435
7481
  return h;
7436
7482
  }
7437
- function makeAbort() {
7438
- const controller = new AbortController();
7439
- const timer = setTimeout(() => controller.abort(), timeoutMs);
7440
- if (externalSignal) {
7441
- if (externalSignal.aborted) {
7442
- clearTimeout(timer);
7443
- controller.abort();
7444
- } else {
7445
- externalSignal.addEventListener(
7446
- "abort",
7447
- () => {
7448
- clearTimeout(timer);
7449
- controller.abort();
7450
- },
7451
- { once: true }
7452
- );
7453
- }
7454
- }
7455
- return { controller, timer };
7456
- }
7457
- function logHeaders(dir, entries) {
7458
- if (!verbose?.enabled) return;
7459
- for (const [k, v] of entries) {
7460
- if (dir === ">") verbose.headerOut(k, v);
7461
- else verbose.headerIn(k, v);
7462
- }
7463
- }
7464
7483
  async function handleErrorStatus(res, method) {
7484
+ if ((res.status === 401 || res.status === 403) && authProvider)
7485
+ throw new AuthRetryableError(res);
7465
7486
  if (res.status === 401 || res.status === 403) throw new McpAuthError(res.status, url);
7466
7487
  const text = await res.text();
7467
7488
  let rpc;
@@ -7473,34 +7494,27 @@ async function createStatelessConnection(options) {
7473
7494
  if (rpc) throw new StatelessRpcError(rpc.code, rpc.message, rpc.data);
7474
7495
  throw new StatelessHttpError(res.status, res.statusText, method, text.slice(0, 512));
7475
7496
  }
7476
- async function request(method, params) {
7477
- const id = nextId2++;
7478
- const headers = buildHeaders(method, params);
7479
- const meta = {
7480
- protocolVersion: negotiatedVersion ?? STATELESS_PROTOCOL_VERSION,
7481
- clientInfo
7482
- };
7483
- const body = JSON.stringify({
7484
- jsonrpc: "2.0",
7485
- id,
7486
- method,
7487
- params: { ...params, _meta: meta }
7488
- });
7497
+ async function doFetch(method, params, body) {
7498
+ const token = await authProvider?.token();
7499
+ const headers = buildHeaders(method, params, token);
7489
7500
  if (verbose?.enabled) {
7490
7501
  verbose.request(method, body);
7491
- logHeaders(">", Object.entries(headers));
7502
+ logHeaders(verbose, ">", Object.entries(headers));
7492
7503
  }
7493
- const { controller, timer } = makeAbort();
7494
- let res;
7504
+ const { controller, timer } = makeAbort(timeoutMs, externalSignal);
7495
7505
  try {
7496
- res = await fetchImpl(url, {
7506
+ const res = await fetchImpl(url, {
7497
7507
  method: "POST",
7498
7508
  headers,
7499
7509
  body,
7500
7510
  signal: controller.signal
7501
7511
  });
7512
+ if (verbose?.enabled) {
7513
+ verbose.http("<", `HTTP/1.1 ${res.status} ${res.statusText}`);
7514
+ logHeaders(verbose, "<", res.headers.entries());
7515
+ }
7516
+ return { res, token };
7502
7517
  } catch (err) {
7503
- clearTimeout(timer);
7504
7518
  if (controller.signal.aborted && !externalSignal?.aborted) {
7505
7519
  throw new McpTimeoutError2(method, timeoutMs);
7506
7520
  }
@@ -7508,11 +7522,8 @@ async function createStatelessConnection(options) {
7508
7522
  } finally {
7509
7523
  clearTimeout(timer);
7510
7524
  }
7511
- if (verbose?.enabled) {
7512
- verbose.http("<", `HTTP/1.1 ${res.status} ${res.statusText}`);
7513
- logHeaders("<", res.headers.entries());
7514
- }
7515
- if (res.status >= 400) await handleErrorStatus(res, method);
7525
+ }
7526
+ async function parseResponse(res, id) {
7516
7527
  const ct = res.headers.get("content-type") ?? "";
7517
7528
  let frame;
7518
7529
  if (ct.toLowerCase().includes("text/event-stream")) {
@@ -7526,10 +7537,53 @@ async function createStatelessConnection(options) {
7526
7537
  }
7527
7538
  return frame.result;
7528
7539
  }
7540
+ async function handleAuthError(err, token) {
7541
+ if (!authProvider?.onUnauthorized) throw new McpAuthError(err.response.status, url);
7542
+ try {
7543
+ await authProvider.onUnauthorized({
7544
+ response: err.response,
7545
+ serverUrl: new URL(url),
7546
+ fetch: fetchImpl,
7547
+ token
7548
+ });
7549
+ } catch (authErr) {
7550
+ if (authErr instanceof AuthStepUpError) throw authErr;
7551
+ throw new McpAuthError(err.response.status, url);
7552
+ }
7553
+ }
7554
+ async function request(method, params) {
7555
+ const id = nextId2++;
7556
+ const meta = {
7557
+ protocolVersion: negotiatedVersion ?? STATELESS_PROTOCOL_VERSION,
7558
+ clientInfo
7559
+ };
7560
+ const body = JSON.stringify({
7561
+ jsonrpc: "2.0",
7562
+ id,
7563
+ method,
7564
+ params: { ...params, _meta: meta }
7565
+ });
7566
+ for (let attempt = 0; attempt < 2; attempt++) {
7567
+ const { res, token } = await doFetch(method, params, body);
7568
+ if (res.status >= 400) {
7569
+ try {
7570
+ await handleErrorStatus(res, method);
7571
+ } catch (err) {
7572
+ if (err instanceof AuthRetryableError && attempt === 0) {
7573
+ await handleAuthError(err, token);
7574
+ continue;
7575
+ }
7576
+ throw err;
7577
+ }
7578
+ }
7579
+ return parseResponse(res, id);
7580
+ }
7581
+ throw new McpAuthError(401, url);
7582
+ }
7529
7583
  const discoverResult2 = await request("server/discover", {
7530
7584
  protocolVersion: STATELESS_PROTOCOL_VERSION
7531
7585
  });
7532
- const supported = Array.isArray(discoverResult2?.supportedVersions) ? discoverResult2.supportedVersions.filter((v) => typeof v === "string") : [];
7586
+ const supported = discoverResult2?.supportedVersions?.filter((v) => typeof v === "string") ?? [];
7533
7587
  if (!supported.includes(STATELESS_PROTOCOL_VERSION)) {
7534
7588
  throw new Error(
7535
7589
  `Server does not support ${STATELESS_PROTOCOL_VERSION} (advertised: ${supported.join(", ") || "none"})`
@@ -7540,15 +7594,7 @@ async function createStatelessConnection(options) {
7540
7594
  return {
7541
7595
  async info() {
7542
7596
  if (serverInfoCache) return serverInfoCache;
7543
- const r = discoverResult2;
7544
- serverInfoCache = {
7545
- name: r?.serverInfo?.name,
7546
- version: r?.serverInfo?.version,
7547
- title: r?.serverInfo?.title,
7548
- protocolVersion: STATELESS_PROTOCOL_VERSION,
7549
- capabilities: r?.capabilities ?? {},
7550
- instructions: r?.instructions
7551
- };
7597
+ serverInfoCache = buildServerInfo(discoverResult2);
7552
7598
  return serverInfoCache;
7553
7599
  },
7554
7600
  async listTools() {
@@ -7698,23 +7744,12 @@ async function connect(platform2, options) {
7698
7744
  });
7699
7745
  }
7700
7746
  if (!options.url) throw new Error("connect requires either a url or a command");
7701
- if (options.authProvider) {
7702
- const token = await options.authProvider.token();
7703
- if (token) {
7704
- return createPiConnection({
7705
- url: options.url,
7706
- headers: options.headers,
7707
- fetchImpl: platform2.fetch,
7708
- authProvider: options.authProvider,
7709
- ...shared
7710
- });
7711
- }
7712
- }
7713
7747
  if (options.protocolVersion && !options.protocolVersion.startsWith("2026")) {
7714
7748
  return createPiConnection({
7715
7749
  url: options.url,
7716
7750
  headers: options.headers,
7717
7751
  fetchImpl: platform2.fetch,
7752
+ authProvider: options.authProvider,
7718
7753
  ...shared
7719
7754
  });
7720
7755
  }
@@ -7726,11 +7761,14 @@ async function connect(platform2, options) {
7726
7761
  clientInfo: { name: "mcpcat", version: options.version ?? "0.0.0" },
7727
7762
  timeoutMs: options.timeoutMs,
7728
7763
  signal: options.signal,
7729
- verbose: options.verbose
7764
+ verbose: options.verbose,
7765
+ authProvider: options.authProvider
7730
7766
  });
7731
7767
  } catch (err) {
7732
7768
  const isMethodNotFound = err instanceof StatelessRpcError && err.code === -32601 || err instanceof StatelessHttpError && err.status >= 400 && err.status < 500;
7733
7769
  if (options.protocolVersion?.startsWith("2026")) throw err;
7770
+ if (err instanceof McpAuthError || err instanceof AuthStepUpError) throw err;
7771
+ if (err.name === "McpOAuthAuthorizationRequiredError") throw err;
7734
7772
  if (isMethodNotFound) {
7735
7773
  return createPiConnection({
7736
7774
  url: options.url,
@@ -8218,13 +8256,16 @@ async function createSandbox(platform2, tools, globals, timeoutMs) {
8218
8256
  return new InlineCodemodeSandbox2({ tools, globals, timeoutMs, wasm });
8219
8257
  }
8220
8258
  const { CodemodeSandbox: CodemodeSandbox2, loadQuickJSWasm: loadQuickJSWasm2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
8221
- const wasmPath = await resolveWasmPath(platform2);
8259
+ const getSeaDir = globalThis.__mcpcat_get_sea_asset_dir;
8260
+ const seaDir = getSeaDir?.();
8261
+ const wasmPath = seaDir ? `${seaDir}/quickjs.wasm` : await resolveWasmPath(platform2);
8262
+ const workerUrl = seaDir ? new URL(`file://${seaDir}/codemode-worker.mjs`) : new URL("./codemode-worker.mjs", import.meta.url);
8222
8263
  return new CodemodeSandbox2({
8223
8264
  tools,
8224
8265
  globals,
8225
8266
  timeoutMs,
8226
8267
  wasm: loadQuickJSWasm2(wasmPath),
8227
- workerUrl: new URL("./codemode-worker.mjs", import.meta.url)
8268
+ workerUrl
8228
8269
  });
8229
8270
  }
8230
8271
  async function resolveWasmPath(platform2) {
@@ -13662,11 +13703,20 @@ async function runClientVerb(platform2, version, parsed) {
13662
13703
  return EXIT_AUTH;
13663
13704
  }
13664
13705
  const msg = transportHint(err) ?? `connection failed: ${err.message}`;
13706
+ if (interrupted) {
13707
+ await platform2.write(2, "mcpcat: interrupted\n");
13708
+ return EXIT_INTERRUPTED;
13709
+ }
13665
13710
  await writeErr(platform2, parsed.jsonOutput, "connection", msg);
13666
13711
  return EXIT_CONNECTION;
13667
13712
  }
13668
13713
  try {
13669
- return await runVerb(platform2, parsed, conn, onProgress);
13714
+ const code = await runVerb(platform2, parsed, conn, onProgress);
13715
+ if (interrupted) {
13716
+ await platform2.write(2, "mcpcat: interrupted\n");
13717
+ return EXIT_INTERRUPTED;
13718
+ }
13719
+ return code;
13670
13720
  } catch (err) {
13671
13721
  if (interrupted) {
13672
13722
  await platform2.write(2, "mcpcat: interrupted\n");
@@ -13805,7 +13855,7 @@ async function main(ctx) {
13805
13855
  packageDir: "/node_modules/@ai-ecoverse/mcpcat"
13806
13856
  });
13807
13857
  const args = ctx.argv.slice(1);
13808
- return run(platform2, "0.10.0", args);
13858
+ return run(platform2, "0.12.0", args);
13809
13859
  }
13810
13860
  export {
13811
13861
  main as default
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/mcpcat",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "A curl for the Model Context Protocol",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",