@ai-ecoverse/mcpcat 0.15.9 → 0.16.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
@@ -24,6 +24,10 @@ npm install -g @ai-ecoverse/mcpcat
24
24
 
25
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
+ ## Releases: certified = latest
28
+
29
+ Every change that alters what mcpcat ships is certified before it reaches `main`. A pull request that touches `src/`, the build, the dependencies or the release workflows gets mcpcat's cert-review on a pack of its head, and again in the merge queue on the exact tree that lands on `main`; a FAIL blocks the merge. Docs-, test- and CI-only changes skip it as "not behaviour". Each merge to `main` is then released to npm as `latest` in one step by semantic-release, with provenance, so `npm install @ai-ecoverse/mcpcat` and the installer always get certified code. The verdict line is a comment on the pull request and the `cert-review` commit status.
30
+
27
31
  ## Quick start
28
32
 
29
33
  ```sh
@@ -9113,6 +9113,12 @@ async function connect(platform2, options) {
9113
9113
  }
9114
9114
  }
9115
9115
 
9116
+ // src/render/sanitize.ts
9117
+ var CONTROL_RE = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g;
9118
+ function sanitizeTerminal(text) {
9119
+ return text.replace(CONTROL_RE, (c) => `\\x${c.charCodeAt(0).toString(16).padStart(2, "0")}`);
9120
+ }
9121
+
9116
9122
  // src/client/elicit.ts
9117
9123
  function readLine(platform2) {
9118
9124
  return new Promise((resolve, reject) => {
@@ -9164,7 +9170,7 @@ async function promptForSchema(platform2, schema) {
9164
9170
  const desc = prop.description ? ` - ${prop.description}` : "";
9165
9171
  const def = prop.default !== void 0 ? ` [${String(prop.default)}]` : "";
9166
9172
  const choices = prop.enum ? ` (${prop.enum.join("|")})` : "";
9167
- await platform2.write(2, `${key}${req}${desc}${choices}${def}: `);
9173
+ await platform2.write(2, sanitizeTerminal(`${key}${req}${desc}${choices}${def}: `));
9168
9174
  const line = await readLine(platform2);
9169
9175
  const value = line.trim();
9170
9176
  if (value === "" && prop.default !== void 0) {
@@ -9186,8 +9192,8 @@ function parseInputRequest(result) {
9186
9192
  async function handleElicitation(platform2, inputRequest, isTty, respondData) {
9187
9193
  if (inputRequest.url) {
9188
9194
  if (isTty) {
9189
- await platform2.write(2, `Open this URL to continue: ${inputRequest.url}
9190
- `);
9195
+ await platform2.write(2, sanitizeTerminal(`Open this URL to continue: ${inputRequest.url}
9196
+ `));
9191
9197
  await platform2.write(2, "Press Enter when done...");
9192
9198
  await readLine(platform2);
9193
9199
  return { answers: {} };
@@ -9202,8 +9208,8 @@ async function handleElicitation(platform2, inputRequest, isTty, respondData) {
9202
9208
  }
9203
9209
  const schema = inputRequest.schema ?? { type: "object", properties: {} };
9204
9210
  if (inputRequest.message) {
9205
- await platform2.write(2, `${inputRequest.message}
9206
- `);
9211
+ await platform2.write(2, sanitizeTerminal(`${inputRequest.message}
9212
+ `));
9207
9213
  }
9208
9214
  const answers = await promptForSchema(platform2, schema);
9209
9215
  return { answers };
@@ -9724,8 +9730,11 @@ async function formatOutput(platform2, result, json) {
9724
9730
  `);
9725
9731
  }
9726
9732
  if (!result.ok) {
9727
- await platform2.write(2, `mcpcat: script failed: ${result.error.message}
9728
- `);
9733
+ await platform2.write(
9734
+ 2,
9735
+ sanitizeTerminal(`mcpcat: script failed: ${result.error.message}
9736
+ `)
9737
+ );
9729
9738
  }
9730
9739
  }
9731
9740
  const isTimeout = !result.ok && result.error && result.error.kind === "timeout";
@@ -11187,8 +11196,8 @@ async function renderCallResult(platform2, result, options) {
11187
11196
  }
11188
11197
  const text = renderCallResultText(result, options);
11189
11198
  if (result.isError) {
11190
- await platform2.write(2, r(`${text || "(tool reported error)"}
11191
- `));
11199
+ await platform2.write(2, sanitizeTerminal(r(`${text || "(tool reported error)"}
11200
+ `)));
11192
11201
  return 1;
11193
11202
  }
11194
11203
  if (text) {
@@ -13296,8 +13305,8 @@ async function runWebMcpServe(platform2, options, version, resolvedBearer, gener
13296
13305
  return 3;
13297
13306
  }
13298
13307
  if (!resolved.tab) {
13299
- await platform2.write(2, `mcpcat serve --webmcp: ${resolved.error}
13300
- `);
13308
+ await platform2.write(2, sanitizeTerminal(`mcpcat serve --webmcp: ${resolved.error}
13309
+ `));
13301
13310
  return 3;
13302
13311
  }
13303
13312
  tab = resolved.tab;
@@ -14300,8 +14309,8 @@ ${url.href}
14300
14309
  await platform2.write(2, "mcpcat: sign-in cancelled\n");
14301
14310
  return 1;
14302
14311
  }
14303
- await platform2.write(2, `mcpcat: login failed: ${err.message}
14304
- `);
14312
+ await platform2.write(2, sanitizeTerminal(`mcpcat: login failed: ${err.message}
14313
+ `));
14305
14314
  return 4;
14306
14315
  }
14307
14316
  }
@@ -14934,8 +14943,8 @@ async function runAdd(platform2, args2, probe) {
14934
14943
  if (!flags.noProbe && probe) {
14935
14944
  const reason = await probe(name, config, platform2);
14936
14945
  if (reason) {
14937
- await platform2.write(2, `mcpcat add: probe failed: ${reason}
14938
- `);
14946
+ await platform2.write(2, sanitizeTerminal(`mcpcat add: probe failed: ${reason}
14947
+ `));
14939
14948
  return 3;
14940
14949
  }
14941
14950
  }
@@ -15316,7 +15325,7 @@ function redact(text, secrets) {
15316
15325
  function createVerboseLogger(platform2, enabled) {
15317
15326
  const secrets = /* @__PURE__ */ new Set();
15318
15327
  function write(line) {
15319
- const safe = redact(line, secrets);
15328
+ const safe = sanitizeTerminal(redact(line, secrets));
15320
15329
  platform2.write(2, `${safe}
15321
15330
  `).catch(() => {
15322
15331
  });
@@ -16135,8 +16144,11 @@ async function connectTargets(platform2, names, config, parsed, version) {
16135
16144
  const info = await conn.info();
16136
16145
  targets.push({ name, conn, instructions: info.instructions });
16137
16146
  } catch (err) {
16138
- await platform2.write(2, `mcpcat: connection to ${name} failed: ${err.message}
16139
- `);
16147
+ await platform2.write(
16148
+ 2,
16149
+ sanitizeTerminal(`mcpcat: connection to ${name} failed: ${err.message}
16150
+ `)
16151
+ );
16140
16152
  }
16141
16153
  }
16142
16154
  return {
@@ -16555,8 +16567,8 @@ async function runClientVerb(platform2, version, parsed) {
16555
16567
  if (!showProgress) return;
16556
16568
  const pct = total ? ` ${Math.round(progress / total * 100)}%` : "";
16557
16569
  const msg = message ? ` ${message}` : "";
16558
- platform2.write(2, `* progress:${pct}${msg}
16559
- `).catch(() => {
16570
+ platform2.write(2, sanitizeTerminal(`* progress:${pct}${msg}
16571
+ `)).catch(() => {
16560
16572
  });
16561
16573
  };
16562
16574
  const config = await MergedConfigSource.load(platform2);
@@ -16684,13 +16696,13 @@ async function closeAndKillStdio(conn) {
16684
16696
  }
16685
16697
  async function writeErr(p, json, kind, msg) {
16686
16698
  if (json) await renderErrorJson(p, kind, msg);
16687
- else await p.write(2, `mcpcat: ${msg}
16688
- `);
16699
+ else await p.write(2, sanitizeTerminal(`mcpcat: ${msg}
16700
+ `));
16689
16701
  }
16690
16702
  async function writeErrRedacted(p, json, kind, msg, redact2) {
16691
16703
  if (json) await renderErrorJson(p, kind, msg, void 0, void 0, redact2);
16692
- else await p.write(2, `mcpcat: ${redact2(msg)}
16693
- `);
16704
+ else await p.write(2, sanitizeTerminal(`mcpcat: ${redact2(msg)}
16705
+ `));
16694
16706
  }
16695
16707
  function collectConfigSecrets(headers, env) {
16696
16708
  if (!headers) return [];
@@ -16963,5 +16975,5 @@ try {
16963
16975
  });
16964
16976
  } catch {
16965
16977
  }
16966
- var code = await run(platform, "0.15.9", args);
16978
+ var code = await run(platform, "0.16.3", args);
16967
16979
  process.exit(code);
@@ -8734,6 +8734,12 @@ async function connect(platform2, options) {
8734
8734
  }
8735
8735
  }
8736
8736
 
8737
+ // src/render/sanitize.ts
8738
+ var CONTROL_RE = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g;
8739
+ function sanitizeTerminal(text) {
8740
+ return text.replace(CONTROL_RE, (c) => `\\x${c.charCodeAt(0).toString(16).padStart(2, "0")}`);
8741
+ }
8742
+
8737
8743
  // src/client/elicit.ts
8738
8744
  function readLine(platform2) {
8739
8745
  return new Promise((resolve2, reject) => {
@@ -8785,7 +8791,7 @@ async function promptForSchema(platform2, schema) {
8785
8791
  const desc = prop.description ? ` - ${prop.description}` : "";
8786
8792
  const def = prop.default !== void 0 ? ` [${String(prop.default)}]` : "";
8787
8793
  const choices = prop.enum ? ` (${prop.enum.join("|")})` : "";
8788
- await platform2.write(2, `${key}${req}${desc}${choices}${def}: `);
8794
+ await platform2.write(2, sanitizeTerminal(`${key}${req}${desc}${choices}${def}: `));
8789
8795
  const line = await readLine(platform2);
8790
8796
  const value = line.trim();
8791
8797
  if (value === "" && prop.default !== void 0) {
@@ -8807,8 +8813,8 @@ function parseInputRequest(result) {
8807
8813
  async function handleElicitation(platform2, inputRequest, isTty, respondData) {
8808
8814
  if (inputRequest.url) {
8809
8815
  if (isTty) {
8810
- await platform2.write(2, `Open this URL to continue: ${inputRequest.url}
8811
- `);
8816
+ await platform2.write(2, sanitizeTerminal(`Open this URL to continue: ${inputRequest.url}
8817
+ `));
8812
8818
  await platform2.write(2, "Press Enter when done...");
8813
8819
  await readLine(platform2);
8814
8820
  return { answers: {} };
@@ -8823,8 +8829,8 @@ async function handleElicitation(platform2, inputRequest, isTty, respondData) {
8823
8829
  }
8824
8830
  const schema = inputRequest.schema ?? { type: "object", properties: {} };
8825
8831
  if (inputRequest.message) {
8826
- await platform2.write(2, `${inputRequest.message}
8827
- `);
8832
+ await platform2.write(2, sanitizeTerminal(`${inputRequest.message}
8833
+ `));
8828
8834
  }
8829
8835
  const answers = await promptForSchema(platform2, schema);
8830
8836
  return { answers };
@@ -9345,8 +9351,11 @@ async function formatOutput(platform2, result, json) {
9345
9351
  `);
9346
9352
  }
9347
9353
  if (!result.ok) {
9348
- await platform2.write(2, `mcpcat: script failed: ${result.error.message}
9349
- `);
9354
+ await platform2.write(
9355
+ 2,
9356
+ sanitizeTerminal(`mcpcat: script failed: ${result.error.message}
9357
+ `)
9358
+ );
9350
9359
  }
9351
9360
  }
9352
9361
  const isTimeout = !result.ok && result.error && result.error.kind === "timeout";
@@ -10808,8 +10817,8 @@ async function renderCallResult(platform2, result, options) {
10808
10817
  }
10809
10818
  const text = renderCallResultText(result, options);
10810
10819
  if (result.isError) {
10811
- await platform2.write(2, r(`${text || "(tool reported error)"}
10812
- `));
10820
+ await platform2.write(2, sanitizeTerminal(r(`${text || "(tool reported error)"}
10821
+ `)));
10813
10822
  return 1;
10814
10823
  }
10815
10824
  if (text) {
@@ -12917,8 +12926,8 @@ async function runWebMcpServe(platform2, options, version, resolvedBearer, gener
12917
12926
  return 3;
12918
12927
  }
12919
12928
  if (!resolved.tab) {
12920
- await platform2.write(2, `mcpcat serve --webmcp: ${resolved.error}
12921
- `);
12929
+ await platform2.write(2, sanitizeTerminal(`mcpcat serve --webmcp: ${resolved.error}
12930
+ `));
12922
12931
  return 3;
12923
12932
  }
12924
12933
  tab = resolved.tab;
@@ -13921,8 +13930,8 @@ ${url.href}
13921
13930
  await platform2.write(2, "mcpcat: sign-in cancelled\n");
13922
13931
  return 1;
13923
13932
  }
13924
- await platform2.write(2, `mcpcat: login failed: ${err.message}
13925
- `);
13933
+ await platform2.write(2, sanitizeTerminal(`mcpcat: login failed: ${err.message}
13934
+ `));
13926
13935
  return 4;
13927
13936
  }
13928
13937
  }
@@ -14555,8 +14564,8 @@ async function runAdd(platform2, args, probe) {
14555
14564
  if (!flags.noProbe && probe) {
14556
14565
  const reason = await probe(name, config, platform2);
14557
14566
  if (reason) {
14558
- await platform2.write(2, `mcpcat add: probe failed: ${reason}
14559
- `);
14567
+ await platform2.write(2, sanitizeTerminal(`mcpcat add: probe failed: ${reason}
14568
+ `));
14560
14569
  return 3;
14561
14570
  }
14562
14571
  }
@@ -14937,7 +14946,7 @@ function redact(text, secrets) {
14937
14946
  function createVerboseLogger(platform2, enabled) {
14938
14947
  const secrets = /* @__PURE__ */ new Set();
14939
14948
  function write(line) {
14940
- const safe = redact(line, secrets);
14949
+ const safe = sanitizeTerminal(redact(line, secrets));
14941
14950
  platform2.write(2, `${safe}
14942
14951
  `).catch(() => {
14943
14952
  });
@@ -15756,8 +15765,11 @@ async function connectTargets(platform2, names, config, parsed, version) {
15756
15765
  const info = await conn.info();
15757
15766
  targets.push({ name, conn, instructions: info.instructions });
15758
15767
  } catch (err) {
15759
- await platform2.write(2, `mcpcat: connection to ${name} failed: ${err.message}
15760
- `);
15768
+ await platform2.write(
15769
+ 2,
15770
+ sanitizeTerminal(`mcpcat: connection to ${name} failed: ${err.message}
15771
+ `)
15772
+ );
15761
15773
  }
15762
15774
  }
15763
15775
  return {
@@ -16176,8 +16188,8 @@ async function runClientVerb(platform2, version, parsed) {
16176
16188
  if (!showProgress) return;
16177
16189
  const pct = total ? ` ${Math.round(progress / total * 100)}%` : "";
16178
16190
  const msg = message ? ` ${message}` : "";
16179
- platform2.write(2, `* progress:${pct}${msg}
16180
- `).catch(() => {
16191
+ platform2.write(2, sanitizeTerminal(`* progress:${pct}${msg}
16192
+ `)).catch(() => {
16181
16193
  });
16182
16194
  };
16183
16195
  const config = await MergedConfigSource.load(platform2);
@@ -16305,13 +16317,13 @@ async function closeAndKillStdio(conn) {
16305
16317
  }
16306
16318
  async function writeErr(p, json, kind, msg) {
16307
16319
  if (json) await renderErrorJson(p, kind, msg);
16308
- else await p.write(2, `mcpcat: ${msg}
16309
- `);
16320
+ else await p.write(2, sanitizeTerminal(`mcpcat: ${msg}
16321
+ `));
16310
16322
  }
16311
16323
  async function writeErrRedacted(p, json, kind, msg, redact2) {
16312
16324
  if (json) await renderErrorJson(p, kind, msg, void 0, void 0, redact2);
16313
- else await p.write(2, `mcpcat: ${redact2(msg)}
16314
- `);
16325
+ else await p.write(2, sanitizeTerminal(`mcpcat: ${redact2(msg)}
16326
+ `));
16315
16327
  }
16316
16328
  function collectConfigSecrets(headers, env2) {
16317
16329
  if (!headers) return [];
@@ -16402,11 +16414,12 @@ async function kernelSpawn(ctx, options) {
16402
16414
  }
16403
16415
  }
16404
16416
  const useGroupKill = groupSpawnSupported;
16417
+ let waited;
16405
16418
  return {
16406
16419
  stdin: result.stdin ?? new WritableStream(),
16407
16420
  stdout: result.stdout,
16408
16421
  stderr: result.stderr,
16409
- wait: () => result.wait(),
16422
+ wait: () => waited ??= result.wait(),
16410
16423
  kill: async (signal) => {
16411
16424
  if (useGroupKill) {
16412
16425
  try {
@@ -16684,7 +16697,7 @@ async function main(ctx) {
16684
16697
  packageDir: "/node_modules/@ai-ecoverse/mcpcat"
16685
16698
  });
16686
16699
  const args = ctx.argv.slice(1);
16687
- return run(platform2, "0.15.9", args);
16700
+ return run(platform2, "0.16.3", args);
16688
16701
  }
16689
16702
  export {
16690
16703
  main as default
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/mcpcat",
3
- "version": "0.15.9",
3
+ "version": "0.16.3",
4
4
  "description": "A curl for the Model Context Protocol",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -56,15 +56,15 @@
56
56
  "trust:dry": "npx fledgling sync --dry-run -y --skip-publish"
57
57
  },
58
58
  "devDependencies": {
59
- "@ai-ecoverse/slicc-bios": "1.2.0",
60
- "@ai-ecoverse/slicc-kernel": "1.97.0",
61
- "@ai-ecoverse/slicc-shared-web": "1.20.0",
59
+ "@ai-ecoverse/slicc-bios": "1.5.2",
60
+ "@ai-ecoverse/slicc-kernel": "1.107.1",
61
+ "@ai-ecoverse/slicc-shared-web": "1.21.1",
62
62
  "@biomejs/biome": "2.5.15",
63
63
  "@earendil-works/pi-codemode": "1.2.0",
64
64
  "@earendil-works/pi-mcp": "1.2.0",
65
65
  "@types/node": "26.6.5",
66
66
  "esbuild": "0.28.2",
67
- "kernel-cert-new": "npm:@ai-ecoverse/slicc-kernel@1.97.0",
67
+ "kernel-cert-new": "npm:@ai-ecoverse/slicc-kernel@1.107.1",
68
68
  "monocart-coverage-reports": "2.13.2",
69
69
  "proper-lockfile": "4.1.2",
70
70
  "quickjs-wasi": "3.6.2",