@ai-ecoverse/mcpcat 0.15.1 → 0.15.3

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
@@ -22,7 +22,7 @@ Or via npm:
22
22
  npm install -g @ai-ecoverse/mcpcat
23
23
  ```
24
24
 
25
- 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. Orphan-free shutdown of stdio server trees in the kernel needs slicc-kernel ≥ 1.74.0; on older kernels only the direct child is signalled. When the kernel sends SIGKILL to mcpcat's process group, no handler can run and the server tree survives; a kernel "parent death signal" spawn option (like Linux `PR_SET_PDEATHSIG`) would cover that case.
25
+ 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. Orphan-free shutdown of stdio server trees in the kernel needs slicc-kernel ≥ 1.74.0; on older kernels only the direct child is signalled. A SIGKILLed mcpcat runs no handler. On slicc-kernel ≥ 1.88.0, mcpcat spawns stdio servers and served commands with a parent death signal (like Linux `PR_SET_PDEATHSIG`), so the kernel SIGKILLs each server's process group when mcpcat dies. On older kernels, and on the host, where there is no portable equivalent, servers that exit on stdin EOF (most MCP servers) still stop, but servers that ignore it keep running.
26
26
 
27
27
  ## Quick start
28
28
 
@@ -6477,6 +6477,7 @@ var McpOAuthProvider = class {
6477
6477
  // src/auth/jerk-token-source.ts
6478
6478
  var MIN_VERSION = [0, 14, 1];
6479
6479
  var VERSION_TIMEOUT_MS = 2e3;
6480
+ var JERK_TIMEOUT_MS = 15e3;
6480
6481
  var cachedAvailable;
6481
6482
  function parseVersion(raw) {
6482
6483
  const match = raw.trim().match(/(\d+)\.(\d+)\.(\d+)/);
@@ -6523,23 +6524,39 @@ async function jerkAvailable(platform2) {
6523
6524
  return false;
6524
6525
  }
6525
6526
  try {
6526
- const proc = await platform2.spawn({ argv: ["jerk", "--version"] });
6527
- const timeout = new Promise((_, reject) => {
6528
- setTimeout(() => reject(new Error("timeout")), VERSION_TIMEOUT_MS);
6529
- });
6530
- const work = Promise.all([readAll(proc.stdout), proc.wait()]).then(([out, result]) => {
6531
- proc.stderr.cancel().catch(() => {
6532
- });
6533
- if (result.status !== 0) return false;
6534
- const ver = parseVersion(out);
6535
- return ver !== void 0 && versionSatisfied(ver);
6536
- });
6537
- cachedAvailable = await Promise.race([work, timeout]);
6527
+ const result = await runJerk(platform2, ["jerk", "--version"], VERSION_TIMEOUT_MS);
6528
+ const ver = result.status === 0 ? parseVersion(result.stdout) : void 0;
6529
+ cachedAvailable = ver !== void 0 && versionSatisfied(ver);
6538
6530
  } catch {
6539
6531
  cachedAvailable = false;
6540
6532
  }
6541
6533
  return cachedAvailable;
6542
6534
  }
6535
+ var JerkTimeoutError = class extends Error {
6536
+ command;
6537
+ constructor(argv) {
6538
+ const command = argv.slice(0, 3).join(" ");
6539
+ super(`jerk did not answer; run \`${command}\` yourself`);
6540
+ this.name = "JerkTimeoutError";
6541
+ this.command = command;
6542
+ }
6543
+ };
6544
+ async function runJerk(platform2, argv, timeoutMs) {
6545
+ const proc = await platform2.spawn({ argv, stdin: "ignore" });
6546
+ let timer;
6547
+ const expired = new Promise((resolve) => {
6548
+ timer = setTimeout(() => resolve("timeout"), timeoutMs);
6549
+ });
6550
+ const work = Promise.all([readAll(proc.stdout), readAll(proc.stderr), proc.wait()]);
6551
+ const outcome = await Promise.race([work, expired]);
6552
+ clearTimeout(timer);
6553
+ if (outcome === "timeout") {
6554
+ await proc.kill("SIGKILL");
6555
+ throw new JerkTimeoutError(argv);
6556
+ }
6557
+ const [stdout, stderr, result] = outcome;
6558
+ return { status: result.status, stdout, stderr };
6559
+ }
6543
6560
  function jerkId(name) {
6544
6561
  if (name.startsWith("mcp:")) return name;
6545
6562
  if (name.startsWith("url:")) return `mcp:${name}`;
@@ -6560,8 +6577,10 @@ var JerkSignInRequiredError = class extends Error {
6560
6577
  };
6561
6578
  var JerkTokenSource = class {
6562
6579
  platform;
6563
- constructor(platform2) {
6580
+ timeoutMs;
6581
+ constructor(platform2, timeoutMs = JERK_TIMEOUT_MS) {
6564
6582
  this.platform = platform2;
6583
+ this.timeoutMs = timeoutMs;
6565
6584
  }
6566
6585
  async getToken(name, serverUrl, scope) {
6567
6586
  const id = jerkId(name);
@@ -6569,12 +6588,7 @@ var JerkTokenSource = class {
6569
6588
  if (scope) {
6570
6589
  argv.push("--scope", scope);
6571
6590
  }
6572
- const proc = await this.platform.spawn({ argv });
6573
- const [outRaw, , result] = await Promise.all([
6574
- readAll(proc.stdout),
6575
- readAll(proc.stderr),
6576
- proc.wait()
6577
- ]);
6591
+ const result = await runJerk(this.platform, argv, this.timeoutMs);
6578
6592
  if (result.status === JERK_EXIT_SIGN_IN) {
6579
6593
  throw new JerkSignInRequiredError(id, scope);
6580
6594
  }
@@ -6584,7 +6598,7 @@ var JerkTokenSource = class {
6584
6598
  if (result.status !== 0) {
6585
6599
  return void 0;
6586
6600
  }
6587
- const parsed = JSON.parse(outRaw.trim());
6601
+ const parsed = JSON.parse(result.stdout.trim());
6588
6602
  if (!parsed.token) return void 0;
6589
6603
  return {
6590
6604
  accessToken: parsed.token,
@@ -8177,7 +8191,8 @@ var PlatformStdioTransport = class {
8177
8191
  this.process = await this.options.platform.spawn({
8178
8192
  argv: [this.options.command, ...this.options.args ?? []],
8179
8193
  env,
8180
- cwd: this.options.cwd
8194
+ cwd: this.options.cwd,
8195
+ dieWithParent: true
8181
8196
  });
8182
8197
  trackStdio(this.process);
8183
8198
  }
@@ -8570,7 +8585,7 @@ async function createStatelessConnection(options) {
8570
8585
  token
8571
8586
  });
8572
8587
  } catch (authErr) {
8573
- if (authErr instanceof AuthStepUpError) throw authErr;
8588
+ if (authErr instanceof AuthStepUpError || authErr instanceof JerkTimeoutError) throw authErr;
8574
8589
  throw new McpAuthError(err.response.status, url);
8575
8590
  }
8576
8591
  }
@@ -8911,7 +8926,8 @@ async function probeStdioStateless(options) {
8911
8926
  const proc = await options.platform.spawn({
8912
8927
  argv: [options.command, ...options.args ?? []],
8913
8928
  env: inheritedEnv,
8914
- cwd: options.cwd
8929
+ cwd: options.cwd,
8930
+ dieWithParent: true
8915
8931
  });
8916
8932
  trackStdio(proc);
8917
8933
  const rpc = createStdioJsonRpc(proc, options.platform, {
@@ -11496,15 +11512,15 @@ function readMcpCommand(value) {
11496
11512
  }
11497
11513
  function readMcpPositionals(value) {
11498
11514
  if (!Array.isArray(value)) return [];
11499
- const positionals = [];
11515
+ const positionals2 = [];
11500
11516
  for (const entry of value) {
11501
11517
  if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
11502
11518
  const name = ownString(entry, "name");
11503
11519
  if (!name) continue;
11504
11520
  const required = Object.getOwnPropertyDescriptor(entry, "required")?.value;
11505
- positionals.push({ name, required: required !== false, description: "" });
11521
+ positionals2.push({ name, required: required !== false, description: "" });
11506
11522
  }
11507
- return positionals;
11523
+ return positionals2;
11508
11524
  }
11509
11525
  function readMcpFlags(value) {
11510
11526
  if (!Array.isArray(value)) return [];
@@ -12529,7 +12545,8 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
12529
12545
  proc = await platform2.spawn({
12530
12546
  argv: [cmdPath, ...argv],
12531
12547
  env: childEnv,
12532
- cwd
12548
+ cwd,
12549
+ dieWithParent: true
12533
12550
  });
12534
12551
  } catch (err) {
12535
12552
  return { stdout: "", stderr: `spawn failed: ${err.message}`, exitCode: 127 };
@@ -13844,16 +13861,25 @@ async function signIn(options) {
13844
13861
 
13845
13862
  // src/cli/auth-verbs.ts
13846
13863
  var resolvedSource;
13847
- async function createTokenSource(platform2) {
13864
+ async function createTokenSource(platform2, timeoutMs = JERK_TIMEOUT_MS) {
13848
13865
  if (isKernel(platform2)) return new PiFileTokenSource(platform2);
13849
13866
  if (resolvedSource) return resolvedSource;
13850
13867
  if (await jerkAvailable(platform2)) {
13851
- resolvedSource = new JerkTokenSource(platform2);
13868
+ resolvedSource = new JerkTokenSource(platform2, timeoutMs);
13852
13869
  } else {
13853
13870
  resolvedSource = new PiFileTokenSource(platform2);
13854
13871
  }
13855
13872
  return resolvedSource;
13856
13873
  }
13874
+ var VALUE_FLAGS = /* @__PURE__ */ new Set(["--scope", "-m", "--timeout"]);
13875
+ function positionals(args2) {
13876
+ const out = [];
13877
+ for (let i = 0; i < args2.length; i++) {
13878
+ if (VALUE_FLAGS.has(args2[i])) i++;
13879
+ else if (!args2[i].startsWith("-")) out.push(args2[i]);
13880
+ }
13881
+ return out;
13882
+ }
13857
13883
  function resolveServerName(target) {
13858
13884
  try {
13859
13885
  const u = new URL(target.startsWith("http") ? target : `https://${target}`);
@@ -13934,18 +13960,10 @@ async function jerkLogin(platform2, target, args2) {
13934
13960
  if (result.status === JERK_EXIT_SIGN_IN) return 4;
13935
13961
  return result.status;
13936
13962
  }
13937
- async function jerkLogout(platform2, target) {
13963
+ async function jerkLogout(platform2, target, source) {
13938
13964
  const resolved = await resolveLoginTarget(platform2, target);
13939
13965
  const id = jerkMcpId(resolved.name);
13940
- const argv = ["jerk", id, "drop", "--yes"];
13941
- const proc = await platform2.spawn({ argv });
13942
- proc.stdin.close().catch(() => {
13943
- });
13944
- const [, , result] = await Promise.all([
13945
- readStream2(proc.stdout),
13946
- readStream2(proc.stderr),
13947
- proc.wait()
13948
- ]);
13966
+ const result = await runJerk(platform2, ["jerk", id, "drop", "--yes"], source.timeoutMs);
13949
13967
  if (result.status === 0) {
13950
13968
  await platform2.write(1, `Signed out of ${target}
13951
13969
  `);
@@ -13963,7 +13981,7 @@ async function runLogin(platform2, args2, source) {
13963
13981
  );
13964
13982
  return 4;
13965
13983
  }
13966
- const positional = args2.filter((a) => !a.startsWith("-"));
13984
+ const positional = positionals(args2);
13967
13985
  if (positional.length === 0) {
13968
13986
  await platform2.write(2, "mcpcat: login requires a target\n");
13969
13987
  return 2;
@@ -14054,7 +14072,7 @@ async function runLogout(platform2, args2, source) {
14054
14072
  await platform2.write(2, "mcpcat: logout is not available in seven.\n");
14055
14073
  return 4;
14056
14074
  }
14057
- const positional = args2.filter((a) => !a.startsWith("-"));
14075
+ const positional = positionals(args2);
14058
14076
  if (positional.length === 0) {
14059
14077
  await platform2.write(2, "mcpcat: logout requires a target\n");
14060
14078
  return 2;
@@ -14062,8 +14080,13 @@ async function runLogout(platform2, args2, source) {
14062
14080
  const target = positional[0];
14063
14081
  if (source instanceof JerkTokenSource) {
14064
14082
  try {
14065
- return await jerkLogout(platform2, target);
14083
+ return await jerkLogout(platform2, target, source);
14066
14084
  } catch (err) {
14085
+ if (err instanceof JerkTimeoutError) {
14086
+ await platform2.write(2, `mcpcat: ${err.message}
14087
+ `);
14088
+ return 4;
14089
+ }
14067
14090
  await platform2.write(2, `mcpcat: logout failed: ${err.message}
14068
14091
  `);
14069
14092
  return 1;
@@ -15222,6 +15245,10 @@ var VERBS_WITH_TARGET = /* @__PURE__ */ new Set([
15222
15245
  "prompts",
15223
15246
  "prompt"
15224
15247
  ]);
15248
+ function withoutVerb(args2, verb) {
15249
+ const at = args2.indexOf(verb);
15250
+ return [...args2.slice(0, at), ...args2.slice(at + 1)];
15251
+ }
15225
15252
  function resolveMaxPositional(positional) {
15226
15253
  if (positional.length === 0) return 3;
15227
15254
  const first = positional[0];
@@ -16049,12 +16076,12 @@ async function runInner(platform2, version, args2) {
16049
16076
  const discResult = await dispatchDiscoveryVerb(platform2, parsed, args2);
16050
16077
  if (discResult !== void 0) return discResult;
16051
16078
  if (parsed.verb === "login") {
16052
- const source = await createTokenSource(platform2);
16053
- return runLogin(platform2, args2.slice(1), source);
16079
+ const source = await createTokenSource(platform2, parsed.timeoutMs);
16080
+ return runLogin(platform2, withoutVerb(args2, "login"), source);
16054
16081
  }
16055
16082
  if (parsed.verb === "logout") {
16056
- const source = await createTokenSource(platform2);
16057
- return runLogout(platform2, args2.slice(1), source);
16083
+ const source = await createTokenSource(platform2, parsed.timeoutMs);
16084
+ return runLogout(platform2, withoutVerb(args2, "logout"), source);
16058
16085
  }
16059
16086
  if (UNIMPLEMENTED_VERBS.has(parsed.verb)) {
16060
16087
  await platform2.write(2, `mcpcat: '${parsed.verb}' is not implemented yet
@@ -16121,7 +16148,7 @@ function classifyError(err) {
16121
16148
  if (err instanceof McpAuthError) {
16122
16149
  return { code: EXIT_AUTH, kind: "auth", msg: err.message };
16123
16150
  }
16124
- if (err instanceof JerkSignInRequiredError) {
16151
+ if (err instanceof JerkSignInRequiredError || err instanceof JerkTimeoutError) {
16125
16152
  return { code: EXIT_AUTH, kind: "auth", msg: err.message };
16126
16153
  }
16127
16154
  if (err instanceof AuthStepUpError) {
@@ -16145,9 +16172,9 @@ function classifyError(err) {
16145
16172
  }
16146
16173
  return { code: EXIT_RPC_ERROR, kind: "rpc_error", msg };
16147
16174
  }
16148
- async function resolveAuthProvider(platform2, hasAuthHeader, url, target, config, onCredentialObtained) {
16175
+ async function resolveAuthProvider(platform2, hasAuthHeader, url, target, config, timeoutMs, onCredentialObtained) {
16149
16176
  if (hasAuthHeader || !url) return void 0;
16150
- const source = await createTokenSource(platform2);
16177
+ const source = await createTokenSource(platform2, timeoutMs);
16151
16178
  const entry = config.entry(target);
16152
16179
  const oauth = entry && "url" in entry.config ? entry.config.oauth : void 0;
16153
16180
  const name = entry ? target : `url:${new URL(url).origin}${new URL(url).pathname}`;
@@ -16179,7 +16206,7 @@ function classifyConnectionError(err, target, interrupted) {
16179
16206
  msg: `${err.reason} (${hint})`
16180
16207
  };
16181
16208
  }
16182
- if (err instanceof McpAuthError || err instanceof AuthStepUpError || err instanceof JerkSignInRequiredError) {
16209
+ if (err instanceof McpAuthError || err instanceof AuthStepUpError || err instanceof JerkSignInRequiredError || err instanceof JerkTimeoutError) {
16183
16210
  return classifyError(err);
16184
16211
  }
16185
16212
  if (err.name === "McpOAuthAuthorizationRequiredError" || err.name === "McpAuthRequiredError") {
@@ -16327,6 +16354,7 @@ async function runClientVerb(platform2, version, parsed) {
16327
16354
  resolved.url,
16328
16355
  target,
16329
16356
  config,
16357
+ timeoutMs,
16330
16358
  (secret) => {
16331
16359
  allSecrets.add(secret);
16332
16360
  verbose.secrets.add(secret);
@@ -16463,10 +16491,11 @@ var USE_PROCESS_GROUPS2 = process.platform !== "win32";
16463
16491
  function nodeSpawn(options) {
16464
16492
  return new Promise((resolve, reject) => {
16465
16493
  let rejected = false;
16494
+ const ignoreStdin = options.stdin === "ignore";
16466
16495
  const child = cpSpawn(options.argv[0], options.argv.slice(1), {
16467
16496
  env: options.env ?? process.env,
16468
16497
  cwd: options.cwd,
16469
- stdio: ["pipe", "pipe", "pipe"],
16498
+ stdio: [ignoreStdin ? "ignore" : "pipe", "pipe", "pipe"],
16470
16499
  detached: USE_PROCESS_GROUPS2
16471
16500
  });
16472
16501
  child.on("error", (err) => {
@@ -16477,7 +16506,7 @@ function nodeSpawn(options) {
16477
16506
  });
16478
16507
  const stdout = Readable.toWeb(child.stdout);
16479
16508
  const stderr = Readable.toWeb(child.stderr);
16480
- const stdin = Writable.toWeb(child.stdin);
16509
+ const stdin = ignoreStdin ? new WritableStream() : Writable.toWeb(child.stdin);
16481
16510
  const closePromise = new Promise((res) => {
16482
16511
  child.on("close", (code2, signal) => {
16483
16512
  res({
@@ -16684,5 +16713,5 @@ try {
16684
16713
  });
16685
16714
  } catch {
16686
16715
  }
16687
- var code = await run(platform, "0.15.1", args);
16716
+ var code = await run(platform, "0.15.3", args);
16688
16717
  process.exit(code);
@@ -6098,6 +6098,7 @@ var McpOAuthProvider = class {
6098
6098
  // src/auth/jerk-token-source.ts
6099
6099
  var MIN_VERSION = [0, 14, 1];
6100
6100
  var VERSION_TIMEOUT_MS = 2e3;
6101
+ var JERK_TIMEOUT_MS = 15e3;
6101
6102
  var cachedAvailable;
6102
6103
  function parseVersion(raw) {
6103
6104
  const match = raw.trim().match(/(\d+)\.(\d+)\.(\d+)/);
@@ -6144,23 +6145,39 @@ async function jerkAvailable(platform2) {
6144
6145
  return false;
6145
6146
  }
6146
6147
  try {
6147
- const proc = await platform2.spawn({ argv: ["jerk", "--version"] });
6148
- const timeout = new Promise((_, reject) => {
6149
- setTimeout(() => reject(new Error("timeout")), VERSION_TIMEOUT_MS);
6150
- });
6151
- const work = Promise.all([readAll(proc.stdout), proc.wait()]).then(([out, result]) => {
6152
- proc.stderr.cancel().catch(() => {
6153
- });
6154
- if (result.status !== 0) return false;
6155
- const ver = parseVersion(out);
6156
- return ver !== void 0 && versionSatisfied(ver);
6157
- });
6158
- cachedAvailable = await Promise.race([work, timeout]);
6148
+ const result = await runJerk(platform2, ["jerk", "--version"], VERSION_TIMEOUT_MS);
6149
+ const ver = result.status === 0 ? parseVersion(result.stdout) : void 0;
6150
+ cachedAvailable = ver !== void 0 && versionSatisfied(ver);
6159
6151
  } catch {
6160
6152
  cachedAvailable = false;
6161
6153
  }
6162
6154
  return cachedAvailable;
6163
6155
  }
6156
+ var JerkTimeoutError = class extends Error {
6157
+ command;
6158
+ constructor(argv) {
6159
+ const command = argv.slice(0, 3).join(" ");
6160
+ super(`jerk did not answer; run \`${command}\` yourself`);
6161
+ this.name = "JerkTimeoutError";
6162
+ this.command = command;
6163
+ }
6164
+ };
6165
+ async function runJerk(platform2, argv, timeoutMs) {
6166
+ const proc = await platform2.spawn({ argv, stdin: "ignore" });
6167
+ let timer;
6168
+ const expired = new Promise((resolve2) => {
6169
+ timer = setTimeout(() => resolve2("timeout"), timeoutMs);
6170
+ });
6171
+ const work = Promise.all([readAll(proc.stdout), readAll(proc.stderr), proc.wait()]);
6172
+ const outcome = await Promise.race([work, expired]);
6173
+ clearTimeout(timer);
6174
+ if (outcome === "timeout") {
6175
+ await proc.kill("SIGKILL");
6176
+ throw new JerkTimeoutError(argv);
6177
+ }
6178
+ const [stdout, stderr, result] = outcome;
6179
+ return { status: result.status, stdout, stderr };
6180
+ }
6164
6181
  function jerkId(name) {
6165
6182
  if (name.startsWith("mcp:")) return name;
6166
6183
  if (name.startsWith("url:")) return `mcp:${name}`;
@@ -6181,8 +6198,10 @@ var JerkSignInRequiredError = class extends Error {
6181
6198
  };
6182
6199
  var JerkTokenSource = class {
6183
6200
  platform;
6184
- constructor(platform2) {
6201
+ timeoutMs;
6202
+ constructor(platform2, timeoutMs = JERK_TIMEOUT_MS) {
6185
6203
  this.platform = platform2;
6204
+ this.timeoutMs = timeoutMs;
6186
6205
  }
6187
6206
  async getToken(name, serverUrl, scope) {
6188
6207
  const id = jerkId(name);
@@ -6190,12 +6209,7 @@ var JerkTokenSource = class {
6190
6209
  if (scope) {
6191
6210
  argv.push("--scope", scope);
6192
6211
  }
6193
- const proc = await this.platform.spawn({ argv });
6194
- const [outRaw, , result] = await Promise.all([
6195
- readAll(proc.stdout),
6196
- readAll(proc.stderr),
6197
- proc.wait()
6198
- ]);
6212
+ const result = await runJerk(this.platform, argv, this.timeoutMs);
6199
6213
  if (result.status === JERK_EXIT_SIGN_IN) {
6200
6214
  throw new JerkSignInRequiredError(id, scope);
6201
6215
  }
@@ -6205,7 +6219,7 @@ var JerkTokenSource = class {
6205
6219
  if (result.status !== 0) {
6206
6220
  return void 0;
6207
6221
  }
6208
- const parsed = JSON.parse(outRaw.trim());
6222
+ const parsed = JSON.parse(result.stdout.trim());
6209
6223
  if (!parsed.token) return void 0;
6210
6224
  return {
6211
6225
  accessToken: parsed.token,
@@ -7798,7 +7812,8 @@ var PlatformStdioTransport = class {
7798
7812
  this.process = await this.options.platform.spawn({
7799
7813
  argv: [this.options.command, ...this.options.args ?? []],
7800
7814
  env: env2,
7801
- cwd: this.options.cwd
7815
+ cwd: this.options.cwd,
7816
+ dieWithParent: true
7802
7817
  });
7803
7818
  trackStdio(this.process);
7804
7819
  }
@@ -8191,7 +8206,7 @@ async function createStatelessConnection(options) {
8191
8206
  token
8192
8207
  });
8193
8208
  } catch (authErr) {
8194
- if (authErr instanceof AuthStepUpError) throw authErr;
8209
+ if (authErr instanceof AuthStepUpError || authErr instanceof JerkTimeoutError) throw authErr;
8195
8210
  throw new McpAuthError(err.response.status, url);
8196
8211
  }
8197
8212
  }
@@ -8532,7 +8547,8 @@ async function probeStdioStateless(options) {
8532
8547
  const proc = await options.platform.spawn({
8533
8548
  argv: [options.command, ...options.args ?? []],
8534
8549
  env: inheritedEnv,
8535
- cwd: options.cwd
8550
+ cwd: options.cwd,
8551
+ dieWithParent: true
8536
8552
  });
8537
8553
  trackStdio(proc);
8538
8554
  const rpc = createStdioJsonRpc(proc, options.platform, {
@@ -11117,15 +11133,15 @@ function readMcpCommand(value) {
11117
11133
  }
11118
11134
  function readMcpPositionals(value) {
11119
11135
  if (!Array.isArray(value)) return [];
11120
- const positionals = [];
11136
+ const positionals2 = [];
11121
11137
  for (const entry of value) {
11122
11138
  if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
11123
11139
  const name = ownString(entry, "name");
11124
11140
  if (!name) continue;
11125
11141
  const required = Object.getOwnPropertyDescriptor(entry, "required")?.value;
11126
- positionals.push({ name, required: required !== false, description: "" });
11142
+ positionals2.push({ name, required: required !== false, description: "" });
11127
11143
  }
11128
- return positionals;
11144
+ return positionals2;
11129
11145
  }
11130
11146
  function readMcpFlags(value) {
11131
11147
  if (!Array.isArray(value)) return [];
@@ -12150,7 +12166,8 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
12150
12166
  proc = await platform2.spawn({
12151
12167
  argv: [cmdPath, ...argv],
12152
12168
  env: childEnv,
12153
- cwd
12169
+ cwd,
12170
+ dieWithParent: true
12154
12171
  });
12155
12172
  } catch (err) {
12156
12173
  return { stdout: "", stderr: `spawn failed: ${err.message}`, exitCode: 127 };
@@ -13465,16 +13482,25 @@ async function signIn(options) {
13465
13482
 
13466
13483
  // src/cli/auth-verbs.ts
13467
13484
  var resolvedSource;
13468
- async function createTokenSource(platform2) {
13485
+ async function createTokenSource(platform2, timeoutMs = JERK_TIMEOUT_MS) {
13469
13486
  if (isKernel(platform2)) return new PiFileTokenSource(platform2);
13470
13487
  if (resolvedSource) return resolvedSource;
13471
13488
  if (await jerkAvailable(platform2)) {
13472
- resolvedSource = new JerkTokenSource(platform2);
13489
+ resolvedSource = new JerkTokenSource(platform2, timeoutMs);
13473
13490
  } else {
13474
13491
  resolvedSource = new PiFileTokenSource(platform2);
13475
13492
  }
13476
13493
  return resolvedSource;
13477
13494
  }
13495
+ var VALUE_FLAGS = /* @__PURE__ */ new Set(["--scope", "-m", "--timeout"]);
13496
+ function positionals(args) {
13497
+ const out = [];
13498
+ for (let i = 0; i < args.length; i++) {
13499
+ if (VALUE_FLAGS.has(args[i])) i++;
13500
+ else if (!args[i].startsWith("-")) out.push(args[i]);
13501
+ }
13502
+ return out;
13503
+ }
13478
13504
  function resolveServerName(target) {
13479
13505
  try {
13480
13506
  const u = new URL(target.startsWith("http") ? target : `https://${target}`);
@@ -13555,18 +13581,10 @@ async function jerkLogin(platform2, target, args) {
13555
13581
  if (result.status === JERK_EXIT_SIGN_IN) return 4;
13556
13582
  return result.status;
13557
13583
  }
13558
- async function jerkLogout(platform2, target) {
13584
+ async function jerkLogout(platform2, target, source) {
13559
13585
  const resolved = await resolveLoginTarget(platform2, target);
13560
13586
  const id = jerkMcpId(resolved.name);
13561
- const argv = ["jerk", id, "drop", "--yes"];
13562
- const proc = await platform2.spawn({ argv });
13563
- proc.stdin.close().catch(() => {
13564
- });
13565
- const [, , result] = await Promise.all([
13566
- readStream2(proc.stdout),
13567
- readStream2(proc.stderr),
13568
- proc.wait()
13569
- ]);
13587
+ const result = await runJerk(platform2, ["jerk", id, "drop", "--yes"], source.timeoutMs);
13570
13588
  if (result.status === 0) {
13571
13589
  await platform2.write(1, `Signed out of ${target}
13572
13590
  `);
@@ -13584,7 +13602,7 @@ async function runLogin(platform2, args, source) {
13584
13602
  );
13585
13603
  return 4;
13586
13604
  }
13587
- const positional = args.filter((a) => !a.startsWith("-"));
13605
+ const positional = positionals(args);
13588
13606
  if (positional.length === 0) {
13589
13607
  await platform2.write(2, "mcpcat: login requires a target\n");
13590
13608
  return 2;
@@ -13675,7 +13693,7 @@ async function runLogout(platform2, args, source) {
13675
13693
  await platform2.write(2, "mcpcat: logout is not available in seven.\n");
13676
13694
  return 4;
13677
13695
  }
13678
- const positional = args.filter((a) => !a.startsWith("-"));
13696
+ const positional = positionals(args);
13679
13697
  if (positional.length === 0) {
13680
13698
  await platform2.write(2, "mcpcat: logout requires a target\n");
13681
13699
  return 2;
@@ -13683,8 +13701,13 @@ async function runLogout(platform2, args, source) {
13683
13701
  const target = positional[0];
13684
13702
  if (source instanceof JerkTokenSource) {
13685
13703
  try {
13686
- return await jerkLogout(platform2, target);
13704
+ return await jerkLogout(platform2, target, source);
13687
13705
  } catch (err) {
13706
+ if (err instanceof JerkTimeoutError) {
13707
+ await platform2.write(2, `mcpcat: ${err.message}
13708
+ `);
13709
+ return 4;
13710
+ }
13688
13711
  await platform2.write(2, `mcpcat: logout failed: ${err.message}
13689
13712
  `);
13690
13713
  return 1;
@@ -14843,6 +14866,10 @@ var VERBS_WITH_TARGET = /* @__PURE__ */ new Set([
14843
14866
  "prompts",
14844
14867
  "prompt"
14845
14868
  ]);
14869
+ function withoutVerb(args, verb) {
14870
+ const at = args.indexOf(verb);
14871
+ return [...args.slice(0, at), ...args.slice(at + 1)];
14872
+ }
14846
14873
  function resolveMaxPositional(positional) {
14847
14874
  if (positional.length === 0) return 3;
14848
14875
  const first = positional[0];
@@ -15670,12 +15697,12 @@ async function runInner(platform2, version, args) {
15670
15697
  const discResult = await dispatchDiscoveryVerb(platform2, parsed, args);
15671
15698
  if (discResult !== void 0) return discResult;
15672
15699
  if (parsed.verb === "login") {
15673
- const source = await createTokenSource(platform2);
15674
- return runLogin(platform2, args.slice(1), source);
15700
+ const source = await createTokenSource(platform2, parsed.timeoutMs);
15701
+ return runLogin(platform2, withoutVerb(args, "login"), source);
15675
15702
  }
15676
15703
  if (parsed.verb === "logout") {
15677
- const source = await createTokenSource(platform2);
15678
- return runLogout(platform2, args.slice(1), source);
15704
+ const source = await createTokenSource(platform2, parsed.timeoutMs);
15705
+ return runLogout(platform2, withoutVerb(args, "logout"), source);
15679
15706
  }
15680
15707
  if (UNIMPLEMENTED_VERBS.has(parsed.verb)) {
15681
15708
  await platform2.write(2, `mcpcat: '${parsed.verb}' is not implemented yet
@@ -15742,7 +15769,7 @@ function classifyError(err) {
15742
15769
  if (err instanceof McpAuthError) {
15743
15770
  return { code: EXIT_AUTH, kind: "auth", msg: err.message };
15744
15771
  }
15745
- if (err instanceof JerkSignInRequiredError) {
15772
+ if (err instanceof JerkSignInRequiredError || err instanceof JerkTimeoutError) {
15746
15773
  return { code: EXIT_AUTH, kind: "auth", msg: err.message };
15747
15774
  }
15748
15775
  if (err instanceof AuthStepUpError) {
@@ -15766,9 +15793,9 @@ function classifyError(err) {
15766
15793
  }
15767
15794
  return { code: EXIT_RPC_ERROR, kind: "rpc_error", msg };
15768
15795
  }
15769
- async function resolveAuthProvider(platform2, hasAuthHeader, url, target, config, onCredentialObtained) {
15796
+ async function resolveAuthProvider(platform2, hasAuthHeader, url, target, config, timeoutMs, onCredentialObtained) {
15770
15797
  if (hasAuthHeader || !url) return void 0;
15771
- const source = await createTokenSource(platform2);
15798
+ const source = await createTokenSource(platform2, timeoutMs);
15772
15799
  const entry = config.entry(target);
15773
15800
  const oauth = entry && "url" in entry.config ? entry.config.oauth : void 0;
15774
15801
  const name = entry ? target : `url:${new URL(url).origin}${new URL(url).pathname}`;
@@ -15800,7 +15827,7 @@ function classifyConnectionError(err, target, interrupted) {
15800
15827
  msg: `${err.reason} (${hint})`
15801
15828
  };
15802
15829
  }
15803
- if (err instanceof McpAuthError || err instanceof AuthStepUpError || err instanceof JerkSignInRequiredError) {
15830
+ if (err instanceof McpAuthError || err instanceof AuthStepUpError || err instanceof JerkSignInRequiredError || err instanceof JerkTimeoutError) {
15804
15831
  return classifyError(err);
15805
15832
  }
15806
15833
  if (err.name === "McpOAuthAuthorizationRequiredError" || err.name === "McpAuthRequiredError") {
@@ -15948,6 +15975,7 @@ async function runClientVerb(platform2, version, parsed) {
15948
15975
  resolved.url,
15949
15976
  target,
15950
15977
  config,
15978
+ timeoutMs,
15951
15979
  (secret) => {
15952
15980
  allSecrets.add(secret);
15953
15981
  verbose.secrets.add(secret);
@@ -16094,17 +16122,35 @@ function kernelFs(ctx) {
16094
16122
  };
16095
16123
  }
16096
16124
  var groupSpawnSupported;
16125
+ var deathSignalSupported;
16126
+ var PARENT_DEATH_SIGNAL = "SIGKILL";
16127
+ function isInvalid(err) {
16128
+ return err?.code === "EINVAL";
16129
+ }
16097
16130
  async function kernelSpawn(ctx, options) {
16098
16131
  const tryGroup = groupSpawnSupported !== false;
16099
- const result = await ctx.spawn({
16132
+ const base = {
16100
16133
  argv: options.argv,
16101
16134
  env: options.env,
16102
16135
  cwd: options.cwd,
16103
- stdin: "pipe",
16136
+ stdin: options.stdin === "ignore" ? "null" : "pipe",
16104
16137
  stdout: "pipe",
16105
16138
  stderr: "pipe",
16106
16139
  ...tryGroup ? { pgid: 0 } : {}
16107
- });
16140
+ };
16141
+ const tryDeath = options.dieWithParent === true && deathSignalSupported !== false;
16142
+ let result;
16143
+ if (tryDeath) {
16144
+ try {
16145
+ result = await ctx.spawn({ ...base, parentDeathSignal: PARENT_DEATH_SIGNAL });
16146
+ } catch (err) {
16147
+ if (!isInvalid(err)) throw err;
16148
+ deathSignalSupported = false;
16149
+ result = await ctx.spawn(base);
16150
+ }
16151
+ } else {
16152
+ result = await ctx.spawn(base);
16153
+ }
16108
16154
  const childPid = result.pid;
16109
16155
  if (groupSpawnSupported === void 0) {
16110
16156
  try {
@@ -16116,7 +16162,7 @@ async function kernelSpawn(ctx, options) {
16116
16162
  }
16117
16163
  const useGroupKill = groupSpawnSupported;
16118
16164
  return {
16119
- stdin: result.stdin,
16165
+ stdin: result.stdin ?? new WritableStream(),
16120
16166
  stdout: result.stdout,
16121
16167
  stderr: result.stderr,
16122
16168
  wait: () => result.wait(),
@@ -16348,7 +16394,7 @@ async function main(ctx) {
16348
16394
  packageDir: "/node_modules/@ai-ecoverse/mcpcat"
16349
16395
  });
16350
16396
  const args = ctx.argv.slice(1);
16351
- return run(platform2, "0.15.1", args);
16397
+ return run(platform2, "0.15.3", args);
16352
16398
  }
16353
16399
  export {
16354
16400
  main as default
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/mcpcat",
3
- "version": "0.15.1",
3
+ "version": "0.15.3",
4
4
  "description": "A curl for the Model Context Protocol",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -56,7 +56,7 @@
56
56
  "trust:dry": "npx fledgling sync --dry-run -y --skip-publish"
57
57
  },
58
58
  "devDependencies": {
59
- "@ai-ecoverse/slicc-kernel": "1.85.0",
59
+ "@ai-ecoverse/slicc-kernel": "1.88.1",
60
60
  "@ai-ecoverse/slicc-shared-web": "1.13.1",
61
61
  "@biomejs/biome": "2.5.15",
62
62
  "@earendil-works/pi-codemode": "1.2.0",