@ai-ecoverse/mcpcat 0.17.9 → 0.17.10

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
@@ -28,9 +28,7 @@ mcpcat runs natively on macOS, Linux and Windows (Node ≥ 24), and inside Slicc
28
28
 
29
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". A draft pull request is not reviewed: its `cert-review` is red with `draft: not reviewed, mark it ready for review`, and marking it ready starts the review (converted_to_draft cancels a running one). Mark ready = reviewed, and merges on green; a pull request held for anything stays a draft. For a PR that needs the coordinator's security read, the order is: the coordinator reads the draft, it is marked ready, the cert runs, then the queue. 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. It reads `certified …`, `cert-ci: FAIL …` when the reviewer or a check in code found a defect (status `failure`), or `cert-ci: INCOMPLETE …` when the review couldn't decide: the review job didn't succeed or was skipped, no verdict could be read, or the artifact or the runner's own `gh` was missing (status `error`). A fork's change to shipped behaviour also reads INCOMPLETE, since it gets no review. INCOMPLETE never certifies; re-run the failed jobs. A run that ends without `record` posting its status (a crashed plan, record or verdict job) still posts one on the head: `error` with its INCOMPLETE line, or `failure` for a FAIL. So the required `cert-review` check never waits forever. The reviewer's verdict can add defects but never clear one found in code: `record` re-reads the suites' evidence (`stage.json`, `suites.json`), uploaded before the reviewer runs and bound by the artifact id and digest the review job output. A suite or security row that failed on the host or a pinned kernel, a changed test that fails on the head, an import in the kernel bundle or an unexplained rebuild difference is a FAIL. Missing suites, kernels or rows are INCOMPLETE. The NEGATIVE runs each changed test on the base's code: the subject's tests, helpers and fixtures with the base's `dist`, `scripts/` and `.github/workflows/`, all from the base sha, so a test of the cert tooling runs against main's tooling. The record names both shas. A changed test that also passes on the base is named in the line as `gaps: no-negative <file>`. Every artifact a later job reads (the tarball, the verdict, the suites' evidence) is downloaded by the artifact id its producer output, after `bind.mjs` checks the artifact's name, run and digest against that producer's outputs. Any other artifact from this attempt of the run under `mcpcat-tarball-`, `mcpcat-npm-tarball-` or `cert-review-` makes the line INCOMPLETE. An earlier attempt's leftovers, created before this attempt started, are never read.
30
30
 
31
- After each release, `cert-verify.yml` (on the Release run's success, or dispatched from `main` with a `version`) checks again that npm serves the SLSA subject built by `release.yml` on `main` from the tag, and that every pull request merged since the previous tag carries a green `cert-review` status on its head. Its hosted, write-only `notes` job then puts one line into the GitHub release notes, between `<!-- cert-verify -->` markers: `certified @ai-ecoverse/mcpcat@<version> sha256=<sha> (…; pr-certified since v<prev>: …)`, or `verified …` when some pull request lacks a green cert-review. A re-run with the same bytes leaves the notes as they are. Notes that already name another sha for the version, or npm bytes that don't verify, are a supply-chain alert: nothing is written and an issue is filed. Checks that couldn't run read `cert-ci: INCOMPLETE` and write nothing.
32
-
33
- Silence never passes: every gate in the workflows prints a verdict line, and its caller requires that line, not just an exit code. These are `all-green: PASS|FAIL …`, the cert job's `cert: kernel <v> — N kernel tests passed`, shipped.mjs's `behaviour:`/`not behaviour:`, release verify's `verified …`, and the installer tests' `All installer tests passed.`. A gate that exits 0 without its line fails.
31
+ Silence never passes: every gate in the workflows prints a verdict line, and its caller requires that line, not just an exit code. These are `all-green: PASS|FAIL …`, the cert job's `cert: kernel <v> — N kernel tests passed`, shipped.mjs's `behaviour:`/`not behaviour:`, and the installer tests' `All installer tests passed.`. A gate that exits 0 without its line fails.
34
32
 
35
33
  ## Quick start
36
34
 
@@ -8421,10 +8421,11 @@ var KERNEL_REFUSAL = "nodeTransport refuses ";
8421
8421
  function firstLine(text) {
8422
8422
  return text.trim().split("\n")[0].trim().slice(0, 200);
8423
8423
  }
8424
- async function authReason(res) {
8424
+ async function authReason(res, redact2) {
8425
8425
  const type = res.headers.get("content-type") ?? "";
8426
- if (!type.toLowerCase().startsWith("text/plain")) return void 0;
8427
- return firstLine(await res.text()) || void 0;
8426
+ if (!type.toLowerCase().startsWith("text/plain")) return {};
8427
+ const text = await res.text();
8428
+ return { raw: firstLine(text), reason: firstLine(redact2(text)) || void 0 };
8428
8429
  }
8429
8430
  function selectSseFrame(body, targetId) {
8430
8431
  const text = new TextDecoder().decode(body);
@@ -8541,7 +8542,8 @@ async function createStatelessConnection(options) {
8541
8542
  signal: externalSignal,
8542
8543
  verbose,
8543
8544
  authProvider,
8544
- transportRefusals
8545
+ transportRefusals,
8546
+ redact: redact2 = (text) => text
8545
8547
  } = options;
8546
8548
  let nextId2 = 1;
8547
8549
  let negotiatedVersion;
@@ -8560,9 +8562,9 @@ async function createStatelessConnection(options) {
8560
8562
  }
8561
8563
  async function handleErrorStatus(res, method) {
8562
8564
  if (res.status === 401 || res.status === 403) {
8563
- const reason = await authReason(res);
8564
- if (transportRefusals && res.status === 403 && !res.headers.has("www-authenticate") && reason?.startsWith(KERNEL_REFUSAL)) {
8565
- throw new TransportRefusalError(403, reason);
8565
+ const { raw, reason } = await authReason(res, redact2);
8566
+ if (transportRefusals && res.status === 403 && !res.headers.has("www-authenticate") && raw?.startsWith(KERNEL_REFUSAL)) {
8567
+ throw new TransportRefusalError(403, reason ?? "");
8566
8568
  }
8567
8569
  if (authProvider) throw new AuthRetryableError(res, reason);
8568
8570
  throw new McpAuthError(res.status, url, reason);
@@ -8575,7 +8577,7 @@ async function createStatelessConnection(options) {
8575
8577
  } catch {
8576
8578
  }
8577
8579
  if (rpc) throw new StatelessRpcError(rpc.code, rpc.message, rpc.data);
8578
- throw new StatelessHttpError(res.status, res.statusText, method, text.slice(0, 512));
8580
+ throw new StatelessHttpError(res.status, res.statusText, method, redact2(text).slice(0, 512));
8579
8581
  }
8580
8582
  async function doFetch(method, params, body) {
8581
8583
  const token = await authProvider?.token();
@@ -9103,7 +9105,8 @@ async function connect(platform2, options) {
9103
9105
  signal: options.signal,
9104
9106
  verbose: options.verbose,
9105
9107
  authProvider: options.authProvider,
9106
- transportRefusals: platform2.kind === "kernel"
9108
+ transportRefusals: platform2.kind === "kernel",
9109
+ redact: options.redact
9107
9110
  });
9108
9111
  } catch (err) {
9109
9112
  const isMethodNotFound = err instanceof StatelessRpcError && err.code === -32601 || err instanceof StatelessHttpError && err.status >= 400 && err.status < 500;
@@ -12209,7 +12212,7 @@ function createProxyHandler(options) {
12209
12212
  let connecting = null;
12210
12213
  const scrub = secretMasker(options.hiddenHeaders, "***");
12211
12214
  async function doConnect() {
12212
- const c = await connect(options.platform, options.connectOpts);
12215
+ const c = await connect(options.platform, { ...options.connectOpts, redact: scrub });
12213
12216
  cachedInfo = await c.info();
12214
12217
  return c;
12215
12218
  }
@@ -16137,6 +16140,7 @@ async function probeServer(_name, config, platform2) {
16137
16140
  } else {
16138
16141
  return void 0;
16139
16142
  }
16143
+ opts.redact = (text) => redactSecrets(text, secrets);
16140
16144
  const conn = await connect(platform2, opts);
16141
16145
  try {
16142
16146
  await conn.info();
@@ -16730,7 +16734,8 @@ async function runClientVerb(platform2, version, parsed) {
16730
16734
  signal: sigintController.signal,
16731
16735
  verbose,
16732
16736
  authProvider,
16733
- roots
16737
+ roots,
16738
+ redact: redact2
16734
16739
  });
16735
16740
  activeConn = conn;
16736
16741
  } catch (err) {
@@ -17067,5 +17072,5 @@ try {
17067
17072
  });
17068
17073
  } catch {
17069
17074
  }
17070
- var code = await run(platform, "0.17.9", args);
17075
+ var code = await run(platform, "0.17.10", args);
17071
17076
  process.exit(code);
@@ -8042,10 +8042,11 @@ var KERNEL_REFUSAL = "nodeTransport refuses ";
8042
8042
  function firstLine(text) {
8043
8043
  return text.trim().split("\n")[0].trim().slice(0, 200);
8044
8044
  }
8045
- async function authReason(res) {
8045
+ async function authReason(res, redact2) {
8046
8046
  const type = res.headers.get("content-type") ?? "";
8047
- if (!type.toLowerCase().startsWith("text/plain")) return void 0;
8048
- return firstLine(await res.text()) || void 0;
8047
+ if (!type.toLowerCase().startsWith("text/plain")) return {};
8048
+ const text = await res.text();
8049
+ return { raw: firstLine(text), reason: firstLine(redact2(text)) || void 0 };
8049
8050
  }
8050
8051
  function selectSseFrame(body, targetId) {
8051
8052
  const text = new TextDecoder().decode(body);
@@ -8162,7 +8163,8 @@ async function createStatelessConnection(options) {
8162
8163
  signal: externalSignal,
8163
8164
  verbose,
8164
8165
  authProvider,
8165
- transportRefusals
8166
+ transportRefusals,
8167
+ redact: redact2 = (text) => text
8166
8168
  } = options;
8167
8169
  let nextId2 = 1;
8168
8170
  let negotiatedVersion;
@@ -8181,9 +8183,9 @@ async function createStatelessConnection(options) {
8181
8183
  }
8182
8184
  async function handleErrorStatus(res, method) {
8183
8185
  if (res.status === 401 || res.status === 403) {
8184
- const reason = await authReason(res);
8185
- if (transportRefusals && res.status === 403 && !res.headers.has("www-authenticate") && reason?.startsWith(KERNEL_REFUSAL)) {
8186
- throw new TransportRefusalError(403, reason);
8186
+ const { raw, reason } = await authReason(res, redact2);
8187
+ if (transportRefusals && res.status === 403 && !res.headers.has("www-authenticate") && raw?.startsWith(KERNEL_REFUSAL)) {
8188
+ throw new TransportRefusalError(403, reason ?? "");
8187
8189
  }
8188
8190
  if (authProvider) throw new AuthRetryableError(res, reason);
8189
8191
  throw new McpAuthError(res.status, url, reason);
@@ -8196,7 +8198,7 @@ async function createStatelessConnection(options) {
8196
8198
  } catch {
8197
8199
  }
8198
8200
  if (rpc) throw new StatelessRpcError(rpc.code, rpc.message, rpc.data);
8199
- throw new StatelessHttpError(res.status, res.statusText, method, text.slice(0, 512));
8201
+ throw new StatelessHttpError(res.status, res.statusText, method, redact2(text).slice(0, 512));
8200
8202
  }
8201
8203
  async function doFetch(method, params, body) {
8202
8204
  const token = await authProvider?.token();
@@ -8724,7 +8726,8 @@ async function connect(platform2, options) {
8724
8726
  signal: options.signal,
8725
8727
  verbose: options.verbose,
8726
8728
  authProvider: options.authProvider,
8727
- transportRefusals: platform2.kind === "kernel"
8729
+ transportRefusals: platform2.kind === "kernel",
8730
+ redact: options.redact
8728
8731
  });
8729
8732
  } catch (err) {
8730
8733
  const isMethodNotFound = err instanceof StatelessRpcError && err.code === -32601 || err instanceof StatelessHttpError && err.status >= 400 && err.status < 500;
@@ -11830,7 +11833,7 @@ function createProxyHandler(options) {
11830
11833
  let connecting = null;
11831
11834
  const scrub = secretMasker(options.hiddenHeaders, "***");
11832
11835
  async function doConnect() {
11833
- const c = await connect(options.platform, options.connectOpts);
11836
+ const c = await connect(options.platform, { ...options.connectOpts, redact: scrub });
11834
11837
  cachedInfo = await c.info();
11835
11838
  return c;
11836
11839
  }
@@ -15758,6 +15761,7 @@ async function probeServer(_name, config, platform2) {
15758
15761
  } else {
15759
15762
  return void 0;
15760
15763
  }
15764
+ opts.redact = (text) => redactSecrets(text, secrets);
15761
15765
  const conn = await connect(platform2, opts);
15762
15766
  try {
15763
15767
  await conn.info();
@@ -16351,7 +16355,8 @@ async function runClientVerb(platform2, version, parsed) {
16351
16355
  signal: sigintController.signal,
16352
16356
  verbose,
16353
16357
  authProvider,
16354
- roots
16358
+ roots,
16359
+ redact: redact2
16355
16360
  });
16356
16361
  activeConn = conn;
16357
16362
  } catch (err) {
@@ -16789,7 +16794,7 @@ async function main(ctx) {
16789
16794
  packageDir: "/node_modules/@ai-ecoverse/mcpcat"
16790
16795
  });
16791
16796
  const args = ctx.argv.slice(1);
16792
- return run(platform2, "0.17.9", args);
16797
+ return run(platform2, "0.17.10", args);
16793
16798
  }
16794
16799
  export {
16795
16800
  main as default
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/mcpcat",
3
- "version": "0.17.9",
3
+ "version": "0.17.10",
4
4
  "description": "A curl for the Model Context Protocol",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -61,9 +61,9 @@
61
61
  "trust:dry": "npx fledgling sync --dry-run -y --skip-publish"
62
62
  },
63
63
  "devDependencies": {
64
- "@ai-ecoverse/slicc-bios": "1.37.3",
64
+ "@ai-ecoverse/slicc-bios": "1.38.0",
65
65
  "@ai-ecoverse/slicc-kernel": "1.142.1",
66
- "@ai-ecoverse/slicc-shared-web": "1.29.11",
66
+ "@ai-ecoverse/slicc-shared-web": "1.29.13",
67
67
  "@biomejs/biome": "2.5.15",
68
68
  "@earendil-works/pi-codemode": "1.2.0",
69
69
  "@earendil-works/pi-mcp": "1.2.0",