@ai-ecoverse/mcpcat 0.31.16 → 0.31.18

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.
@@ -6558,6 +6558,12 @@ function agentIgnoresConfig(platform2, path) {
6558
6558
  function projectConfigPath(platform2) {
6559
6559
  return `${platform2.cwd}/.pi/mcp.json`;
6560
6560
  }
6561
+ function cacheDir(platform2) {
6562
+ if (platform2.env.XDG_CACHE_HOME) return `${platform2.env.XDG_CACHE_HOME}/mcpcat`;
6563
+ const home = platform2.env.HOME ?? platform2.env.USERPROFILE ?? "/";
6564
+ if (platform2.env.LOCALAPPDATA) return `${platform2.env.LOCALAPPDATA}\\mcpcat\\cache`;
6565
+ return `${home}/Library/Caches/mcpcat`;
6566
+ }
6561
6567
  function stateDir(platform2) {
6562
6568
  if (platform2.env.XDG_STATE_HOME) return `${platform2.env.XDG_STATE_HOME}/mcpcat`;
6563
6569
  const home = platform2.env.HOME ?? platform2.env.USERPROFILE ?? "/";
@@ -14287,10 +14293,30 @@ var HOLDER_RESTART_MAX_MS = 3e4;
14287
14293
  var HOLDER_STABLE_MS = 3e4;
14288
14294
  var HOLDER_STOP_GRACE_MS = 2e3;
14289
14295
  var HOLDER_READY_MS = 2e4;
14296
+ var FALLBACK_UNVERIFIED = "webmcp: other-extension fallback could not verify the reloaded document; tools are unavailable";
14290
14297
  var LABEL = "mcpcat serve --webmcp";
14291
- var GUARD_REFUSAL_RE = /^(?:(?:playwright-cli )?webmcp-polyfill: )?[A-Z][A-Za-z]*\.[a-z][A-Za-z]*: slicc: refused: \S/;
14298
+ var TERMINAL_REFUSALS = /* @__PURE__ */ new Set([
14299
+ "slicc: refused: target is on seven's origin",
14300
+ "slicc: refused: seven\u2019s own pages and the vault are out of reach of browser automation",
14301
+ "slicc: refused: breakpoints could run in seven\u2019s pages or the vault, so the guard doesn\u2019t offer the Debugger domains",
14302
+ "slicc: refused: update the browser guard and client",
14303
+ "slicc: refused: WebMCP installation could not verify the document; update the browser guard and client",
14304
+ "slicc: refused: WebMCP installation could not verify a usable guard and document; update the browser guard and client",
14305
+ "slicc: refused: WebMCP instrumentation is unavailable; update the browser guard and client",
14306
+ "webmcp: no tools are available through the other-extension fallback; page startup or parse-time checks may have happened before installation"
14307
+ ]);
14308
+ var FALLBACK_REFUSALS = /* @__PURE__ */ new Set([
14309
+ "other-extension fallback could not verify the reloaded document; tools are unavailable",
14310
+ "other-extension fallback could not verify the document"
14311
+ ]);
14312
+ function holderReason(line) {
14313
+ return line.trim().replace(/^playwright-cli webmcp-polyfill: /, "").replace(/^webmcp-polyfill: /, "").replace(/^[A-Z][A-Za-z]*\.[a-z][A-Za-z]*: /, "");
14314
+ }
14315
+ function isFallbackUnavailable(line) {
14316
+ return FALLBACK_REFUSALS.has(holderReason(line));
14317
+ }
14292
14318
  function isGuardRefusal(line) {
14293
- return GUARD_REFUSAL_RE.test(line.trim());
14319
+ return TERMINAL_REFUSALS.has(holderReason(line)) || isFallbackUnavailable(line);
14294
14320
  }
14295
14321
  function compareVersions(a, b) {
14296
14322
  const pa = a.split(".").map(Number);
@@ -14418,6 +14444,9 @@ function startHolder(platform2, options) {
14418
14444
  return;
14419
14445
  }
14420
14446
  proc = child;
14447
+ state.fallbackUnavailable = false;
14448
+ if (Number.isSafeInteger(child.pid) && child.pid > 0)
14449
+ report(`holder-pid ${child.pid} tab ${options.tab}`);
14421
14450
  writer = child.stdin.getWriter();
14422
14451
  let lastError;
14423
14452
  const out = drain(child.stdout, onEvent);
@@ -14425,7 +14454,8 @@ function startHolder(platform2, options) {
14425
14454
  const text = line.trim();
14426
14455
  if (!text) return;
14427
14456
  lastError = text;
14428
- if (readyAt === void 0 && isGuardRefusal(text)) refusal ??= text;
14457
+ if (isFallbackUnavailable(text)) state.fallbackUnavailable = true;
14458
+ if (isGuardRefusal(text)) refusal ??= text;
14429
14459
  else relay(text);
14430
14460
  });
14431
14461
  exited = Promise.all([out, err, child.wait()]).then(([, , result]) => {
@@ -14749,6 +14779,12 @@ function startDialogHolder(platform2, options) {
14749
14779
 
14750
14780
  // src/serve/webmcp.ts
14751
14781
  var UNAVAILABLE_RE = /WebMCP is unavailable on this page/;
14782
+ function emptyFallbackOrError(error, holderHint) {
14783
+ const hint = error.trim();
14784
+ if (hint === "webmcp: unverified isolated context" && holderHint === FALLBACK_UNVERIFIED)
14785
+ return { tools: [], hint: holderHint };
14786
+ return hint === "webmcp: no tools are available through the other-extension fallback; page startup or parse-time checks may have happened before installation" ? { tools: [], hint } : { tools: [], error };
14787
+ }
14752
14788
  function buildBaseArgs(options) {
14753
14789
  const args2 = [];
14754
14790
  if (options.cdp) {
@@ -15317,7 +15353,7 @@ function createWebMcpHandler(platform2, options) {
15317
15353
  async function getRpcTools() {
15318
15354
  const { tools, error, notFound, unavailable } = await listRaw();
15319
15355
  if (notFound) return null;
15320
- if (error) return { tools: [], error };
15356
+ if (error) return emptyFallbackOrError(error, options.emptyHint?.(false));
15321
15357
  const late = options.late?.() ?? false;
15322
15358
  const rpcTools = [];
15323
15359
  const hidden = {};
@@ -16479,6 +16515,7 @@ function emptyToolsHint(options, polyfill, unavailable) {
16479
16515
  return `the page shows no WebMCP tools, and the WebMCP polyfill needs playwright-cli ${POLYFILL_MIN_VERSION} or newer (found ${polyfill.oldVersion})`;
16480
16516
  }
16481
16517
  const status = polyfill.holder?.status();
16518
+ if (status?.refused && status.fallbackUnavailable) return FALLBACK_UNVERIFIED;
16482
16519
  if (isLate(polyfill.holder)) {
16483
16520
  return "the page may check for WebMCP only when it loads, before the polyfill was injected; reload the tab, or restart with --webmcp-reload (it reloads the tab)";
16484
16521
  }
@@ -17410,6 +17447,142 @@ function parseLauncher(text) {
17410
17447
  return m ? { node: m[1], entry: m[2] } : void 0;
17411
17448
  }
17412
17449
 
17450
+ // src/auth/callback-port.ts
17451
+ function hasFixedCallback(oauth) {
17452
+ if (oauth?.callbackPort !== void 0 && oauth.callbackPort > 0) return true;
17453
+ if (!oauth?.callbackUrl) return false;
17454
+ try {
17455
+ const port = Number(new URL(oauth.callbackUrl).port);
17456
+ return port > 0;
17457
+ } catch {
17458
+ return false;
17459
+ }
17460
+ }
17461
+ function needsInjectedCallbackPort(options) {
17462
+ return options.namedEntry && !hasFixedCallback(options.oauth);
17463
+ }
17464
+ async function withInjectedCallbackPortIfNeeded(platform2, name, options, fn) {
17465
+ if (!needsInjectedCallbackPort(options)) return fn(void 0);
17466
+ const port = await allocateCallbackPort();
17467
+ return withTempCallbackPortConfig(platform2, name, port, fn);
17468
+ }
17469
+ async function allocateCallbackPort() {
17470
+ const callback = await OAuthCallbackServer.listen({
17471
+ host: "127.0.0.1",
17472
+ port: 0,
17473
+ path: "/callback"
17474
+ });
17475
+ try {
17476
+ return Number(new URL(callback.redirectUrl).port);
17477
+ } finally {
17478
+ await callback.close();
17479
+ }
17480
+ }
17481
+ function isRecord7(value) {
17482
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17483
+ }
17484
+ function hostEnv(platform2) {
17485
+ const out = {};
17486
+ for (const [key, value] of Object.entries(platform2.env)) {
17487
+ if (value !== void 0) out[key] = value;
17488
+ }
17489
+ return out;
17490
+ }
17491
+ async function makeTempDir(platform2) {
17492
+ const base = `${cacheDir(platform2)}/mcp-cb`;
17493
+ await platform2.fs.mkdir(base, { mode: 448 });
17494
+ for (let i = 0; i < 32; i++) {
17495
+ const dir = `${base}/${Date.now().toString(36)}-${i.toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
17496
+ try {
17497
+ await platform2.fs.mkdirExclusive(dir);
17498
+ return dir;
17499
+ } catch {
17500
+ }
17501
+ }
17502
+ throw new Error("unable to create temporary mcp config directory");
17503
+ }
17504
+ async function removeTempDir(platform2, dir) {
17505
+ try {
17506
+ if (platform2.fs.unlink) await platform2.fs.unlink(`${dir}/mcp.json`);
17507
+ } catch {
17508
+ }
17509
+ try {
17510
+ await platform2.fs.rmdir(dir);
17511
+ } catch {
17512
+ }
17513
+ }
17514
+ async function withTempCallbackPortConfig(platform2, name, port, fn) {
17515
+ const path = userConfigPath(platform2);
17516
+ let raw;
17517
+ try {
17518
+ raw = new TextDecoder().decode(await platform2.fs.readFile(path));
17519
+ } catch {
17520
+ return fn(void 0);
17521
+ }
17522
+ let parsed;
17523
+ try {
17524
+ parsed = JSON.parse(raw);
17525
+ } catch {
17526
+ return fn(void 0);
17527
+ }
17528
+ if (!isRecord7(parsed) || !isRecord7(parsed.mcpServers) || !isRecord7(parsed.mcpServers[name])) {
17529
+ return fn(void 0);
17530
+ }
17531
+ const entry = { ...parsed.mcpServers[name] };
17532
+ const prevOauth = isRecord7(entry.oauth) ? { ...entry.oauth } : {};
17533
+ const oauth = { ...prevOauth, callbackPort: port };
17534
+ if (typeof oauth.callbackUrl === "string") {
17535
+ try {
17536
+ const url = new URL(oauth.callbackUrl);
17537
+ if (!(Number(url.port) > 0)) {
17538
+ url.port = String(port);
17539
+ oauth.callbackUrl = url.href;
17540
+ }
17541
+ } catch {
17542
+ }
17543
+ }
17544
+ entry.oauth = oauth;
17545
+ const tempServers = { ...parsed.mcpServers, [name]: entry };
17546
+ const merged = { ...parsed, mcpServers: tempServers };
17547
+ const tempDir = await makeTempDir(platform2);
17548
+ const tempPath = `${tempDir}/mcp.json`;
17549
+ await platform2.fs.writeFile(tempPath, `${JSON.stringify(merged, null, 2)}
17550
+ `, { mode: 384 });
17551
+ const env = { ...hostEnv(platform2), PI_MCP_CONFIG: tempPath };
17552
+ let cleaned = false;
17553
+ const cleanup = async () => {
17554
+ if (cleaned) return;
17555
+ cleaned = true;
17556
+ await removeTempDir(platform2, tempDir);
17557
+ };
17558
+ const exitFor = (signal) => signal === "SIGINT" ? 130 : signal === "SIGTERM" ? 143 : 129;
17559
+ let signalCode;
17560
+ let wake;
17561
+ const stopped = new Promise((resolve) => {
17562
+ wake = resolve;
17563
+ });
17564
+ if (platform2.onSignal) {
17565
+ for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
17566
+ await platform2.onSignal(signal, () => {
17567
+ void cleanup().then(() => {
17568
+ signalCode = exitFor(signal);
17569
+ wake?.();
17570
+ });
17571
+ });
17572
+ }
17573
+ }
17574
+ try {
17575
+ const result = await Promise.race([
17576
+ fn(env).then((value) => ({ ok: true, value })),
17577
+ stopped.then(() => ({ ok: false }))
17578
+ ]);
17579
+ if (!result.ok) platform2.exit(signalCode ?? 130);
17580
+ return result.value;
17581
+ } finally {
17582
+ await cleanup();
17583
+ }
17584
+ }
17585
+
17413
17586
  // src/auth/oauth.ts
17414
17587
  var CALLBACK_HOST2 = "127.0.0.1";
17415
17588
  var CALLBACK_PATH2 = "/callback";
@@ -17425,14 +17598,23 @@ function timedFetch2(fetchImpl) {
17425
17598
  });
17426
17599
  };
17427
17600
  }
17601
+ function nonzeroPort(value) {
17602
+ return value !== void 0 && value > 0 ? value : void 0;
17603
+ }
17604
+ function preferredPortFromRegistration(configured, registered) {
17605
+ if (configured !== void 0) return configured;
17606
+ if (!registered[0]) return void 0;
17607
+ return nonzeroPort(Number(new URL(registered[0]).port));
17608
+ }
17428
17609
  function callbackSettings(oauth) {
17429
17610
  const raw = oauth?.callbackUrl ?? `http://${CALLBACK_HOST2}${CALLBACK_PATH2}`;
17430
17611
  const url = new URL(raw);
17431
17612
  const address = url.hostname.replace(/^\[|\]$/g, "");
17432
- const port = url.port ? Number(url.port) : oauth?.callbackPort;
17613
+ const port = nonzeroPort(url.port ? Number(url.port) : oauth?.callbackPort);
17433
17614
  let fixedRedirectUrl;
17434
- if (url.port) fixedRedirectUrl = oauth?.callbackUrl;
17435
- else if (port !== void 0) {
17615
+ if (nonzeroPort(url.port ? Number(url.port) : void 0) !== void 0 && oauth?.callbackUrl) {
17616
+ fixedRedirectUrl = oauth.callbackUrl;
17617
+ } else if (port !== void 0) {
17436
17618
  url.port = String(port);
17437
17619
  fixedRedirectUrl = url.href;
17438
17620
  }
@@ -17444,6 +17626,24 @@ function callbackSettings(oauth) {
17444
17626
  fixedRedirectUrl
17445
17627
  };
17446
17628
  }
17629
+ async function listenCallback(cbSettings, preferredPort) {
17630
+ try {
17631
+ return await OAuthCallbackServer.listen({
17632
+ host: cbSettings.host,
17633
+ redirectHost: cbSettings.redirectHost,
17634
+ port: preferredPort ?? 0,
17635
+ path: cbSettings.path
17636
+ });
17637
+ } catch (err) {
17638
+ if (preferredPort === void 0 || cbSettings.fixedRedirectUrl) throw err;
17639
+ return OAuthCallbackServer.listen({
17640
+ host: cbSettings.host,
17641
+ redirectHost: cbSettings.redirectHost,
17642
+ port: 0,
17643
+ path: cbSettings.path
17644
+ });
17645
+ }
17646
+ }
17447
17647
  function cimdForServer(metadata, redirectUrl) {
17448
17648
  if (!metadata?.client_id_metadata_document_supported) return void 0;
17449
17649
  if (!metadata.token_endpoint_auth_methods_supported?.includes("none")) return void 0;
@@ -17548,24 +17748,17 @@ async function signIn(options) {
17548
17748
  const stored = await store.load();
17549
17749
  const cbSettings = callbackSettings(oauth);
17550
17750
  const registered = stored?.clientInformation && "redirect_uris" in stored.clientInformation ? stored.clientInformation.redirect_uris : [];
17551
- const preferredPort = cbSettings.port ?? (registered[0] ? Number(new URL(registered[0]).port) || void 0 : void 0);
17552
- const callback = await OAuthCallbackServer.listen({
17553
- host: cbSettings.host,
17554
- redirectHost: cbSettings.redirectHost,
17555
- port: preferredPort ?? 0,
17556
- path: cbSettings.path
17557
- });
17751
+ const preferredPort = preferredPortFromRegistration(cbSettings.port, registered);
17752
+ const callback = await listenCallback(cbSettings, preferredPort);
17558
17753
  const redirectUrl = cbSettings.fixedRedirectUrl ?? callback.redirectUrl;
17559
17754
  try {
17560
17755
  if (stored) {
17561
17756
  const next = { ...stored };
17562
17757
  delete next.oauthState;
17563
- const hasCimd = buildClientMetadataDocumentFn(oauth, redirectUrl) !== void 0;
17564
- const keepClient = oauth?.clientId || (hasCimd ? !stored.clientInformation : registered.includes(redirectUrl));
17565
- if (!keepClient) {
17758
+ delete next.tokens;
17759
+ delete next.tokensExpireAt;
17760
+ if (!oauth?.clientId) {
17566
17761
  delete next.clientInformation;
17567
- delete next.tokens;
17568
- delete next.tokensExpireAt;
17569
17762
  }
17570
17763
  await store.save(next);
17571
17764
  }
@@ -17648,13 +17841,14 @@ async function resolveLoginTarget(platform2, target) {
17648
17841
  return {
17649
17842
  name: target,
17650
17843
  serverUrl: httpConfig.url,
17651
- oauth: httpConfig.oauth
17844
+ oauth: httpConfig.oauth,
17845
+ namedEntry: true
17652
17846
  };
17653
17847
  }
17654
17848
  const resolved = resolveServerName(target);
17655
17849
  if (resolved.isUrl) {
17656
17850
  const url = withScheme(target);
17657
- return { name: resolved.name, serverUrl: url, oauth: void 0 };
17851
+ return { name: resolved.name, serverUrl: url, oauth: void 0, namedEntry: false };
17658
17852
  }
17659
17853
  throw new Error(`unknown target '${target}': not a URL and not found in mcp.json`);
17660
17854
  }
@@ -17685,24 +17879,31 @@ async function jerkLogin(platform2, target, resolved, args2) {
17685
17879
  if (args2.includes("--no-browser")) {
17686
17880
  argv.push("--no-browser");
17687
17881
  }
17688
- const proc = await platform2.spawn({ argv });
17689
- proc.stdin.close().catch(() => {
17690
- });
17691
- const [, errText, result] = await Promise.all([
17692
- readStream2(proc.stdout),
17693
- readStream2(proc.stderr),
17694
- proc.wait()
17695
- ]);
17696
- if (result.status === 0) {
17697
- await platform2.write(1, `Signed in to ${target}
17882
+ return withInjectedCallbackPortIfNeeded(
17883
+ platform2,
17884
+ resolved.name,
17885
+ { namedEntry: resolved.namedEntry, oauth: resolved.oauth },
17886
+ async (env) => {
17887
+ const proc = await platform2.spawn({ argv, env });
17888
+ proc.stdin.close().catch(() => {
17889
+ });
17890
+ const [, errText, result] = await Promise.all([
17891
+ readStream2(proc.stdout),
17892
+ readStream2(proc.stderr),
17893
+ proc.wait()
17894
+ ]);
17895
+ if (result.status === 0) {
17896
+ await platform2.write(1, `Signed in to ${target}
17698
17897
  `);
17699
- return 0;
17700
- }
17701
- if (errText.trim()) {
17702
- await platform2.write(2, sanitizeTerminal(errText));
17703
- }
17704
- if (result.status === JERK_EXIT_SIGN_IN) return 4;
17705
- return result.status;
17898
+ return 0;
17899
+ }
17900
+ if (errText.trim()) {
17901
+ await platform2.write(2, sanitizeTerminal(errText));
17902
+ }
17903
+ if (result.status === JERK_EXIT_SIGN_IN) return 4;
17904
+ return result.status;
17905
+ }
17906
+ );
17706
17907
  }
17707
17908
  async function jerkLogout(platform2, target, resolved, source) {
17708
17909
  const id = jerkId(resolved.name, resolved.serverUrl);
@@ -17879,11 +18080,11 @@ function concatUint8Arrays(arrays) {
17879
18080
  }
17880
18081
 
17881
18082
  // src/config/import.ts
17882
- function isRecord7(value) {
18083
+ function isRecord8(value) {
17883
18084
  return typeof value === "object" && value !== null && !Array.isArray(value);
17884
18085
  }
17885
18086
  function isStringRecord2(value) {
17886
- return isRecord7(value) && Object.values(value).every((v) => typeof v === "string");
18087
+ return isRecord8(value) && Object.values(value).every((v) => typeof v === "string");
17887
18088
  }
17888
18089
  function tryParse(text) {
17889
18090
  try {
@@ -17913,17 +18114,17 @@ function importClaude(text, names) {
17913
18114
  result.errors.push(error);
17914
18115
  return result;
17915
18116
  }
17916
- if (!isRecord7(parsed)) {
18117
+ if (!isRecord8(parsed)) {
17917
18118
  result.errors.push("expected an object");
17918
18119
  return result;
17919
18120
  }
17920
18121
  const mcpServers = parsed.mcpServers;
17921
- if (!isRecord7(mcpServers)) {
18122
+ if (!isRecord8(mcpServers)) {
17922
18123
  result.errors.push('no "mcpServers" object');
17923
18124
  return result;
17924
18125
  }
17925
18126
  for (const [name, entry] of filterNames(Object.entries(mcpServers), names)) {
17926
- if (!isRecord7(entry)) {
18127
+ if (!isRecord8(entry)) {
17927
18128
  result.errors.push(`${name}: not an object`);
17928
18129
  continue;
17929
18130
  }
@@ -17938,17 +18139,17 @@ function importVSCode(text, names) {
17938
18139
  result.errors.push(error);
17939
18140
  return result;
17940
18141
  }
17941
- if (!isRecord7(parsed)) {
18142
+ if (!isRecord8(parsed)) {
17942
18143
  result.errors.push("expected an object");
17943
18144
  return result;
17944
18145
  }
17945
18146
  const servers = parsed.servers ?? parsed.mcpServers;
17946
- if (!isRecord7(servers)) {
18147
+ if (!isRecord8(servers)) {
17947
18148
  result.errors.push('no "servers" or "mcpServers" object');
17948
18149
  return result;
17949
18150
  }
17950
18151
  for (const [name, entry] of filterNames(Object.entries(servers), names)) {
17951
- if (!isRecord7(entry)) {
18152
+ if (!isRecord8(entry)) {
17952
18153
  result.errors.push(`${name}: not an object`);
17953
18154
  continue;
17954
18155
  }
@@ -17982,17 +18183,17 @@ function importSixServers(text, names) {
17982
18183
  result.errors.push(error);
17983
18184
  return result;
17984
18185
  }
17985
- if (!isRecord7(parsed)) {
18186
+ if (!isRecord8(parsed)) {
17986
18187
  result.errors.push("expected an object");
17987
18188
  return result;
17988
18189
  }
17989
18190
  const servers = parsed.servers;
17990
- if (!isRecord7(servers)) {
18191
+ if (!isRecord8(servers)) {
17991
18192
  result.errors.push('no "servers" object');
17992
18193
  return result;
17993
18194
  }
17994
18195
  for (const [name, entry] of filterNames(Object.entries(servers), names)) {
17995
- if (!isRecord7(entry)) {
18196
+ if (!isRecord8(entry)) {
17996
18197
  result.errors.push(`${name}: not an object`);
17997
18198
  continue;
17998
18199
  }
@@ -18000,7 +18201,7 @@ function importSixServers(text, names) {
18000
18201
  if (typeof entry.url === "string") config.url = entry.url;
18001
18202
  if (isStringRecord2(entry.headers)) config.headers = entry.headers;
18002
18203
  if (typeof entry.exposure === "string") config.exposure = entry.exposure;
18003
- if (isRecord7(entry.toolExposure)) config.toolExposure = entry.toolExposure;
18204
+ if (isRecord8(entry.toolExposure)) config.toolExposure = entry.toolExposure;
18004
18205
  result.servers.push({ name, config });
18005
18206
  }
18006
18207
  return result;
@@ -18017,7 +18218,7 @@ function importCodexToml(text, names) {
18017
18218
  if (fields.command) config.command = fields.command;
18018
18219
  if (fields.args) config.args = fields.args;
18019
18220
  if (fields.url) config.url = fields.url;
18020
- if (fields.env && isRecord7(fields.env)) config.env = fields.env;
18221
+ if (fields.env && isRecord8(fields.env)) config.env = fields.env;
18021
18222
  result.servers.push({ name, config });
18022
18223
  }
18023
18224
  return result;
@@ -18069,7 +18270,7 @@ function convertOpenCodeLocal(entry) {
18069
18270
  } else if (typeof entry.command === "string") {
18070
18271
  config.command = entry.command;
18071
18272
  }
18072
- if (isRecord7(entry.environment)) {
18273
+ if (isRecord8(entry.environment)) {
18073
18274
  const env = {};
18074
18275
  for (const [k, v] of Object.entries(entry.environment)) {
18075
18276
  if (typeof v === "string")
@@ -18092,17 +18293,17 @@ function importOpenCode(text, names) {
18092
18293
  result.errors.push(error);
18093
18294
  return result;
18094
18295
  }
18095
- if (!isRecord7(parsed)) {
18296
+ if (!isRecord8(parsed)) {
18096
18297
  result.errors.push("expected an object");
18097
18298
  return result;
18098
18299
  }
18099
18300
  const mcpServers = parsed.mcpServers ?? parsed.mcp;
18100
- if (!isRecord7(mcpServers)) {
18301
+ if (!isRecord8(mcpServers)) {
18101
18302
  result.errors.push("no mcp servers found");
18102
18303
  return result;
18103
18304
  }
18104
18305
  for (const [name, entry] of filterNames(Object.entries(mcpServers), names)) {
18105
- if (!isRecord7(entry)) {
18306
+ if (!isRecord8(entry)) {
18106
18307
  result.errors.push(`${name}: not an object`);
18107
18308
  continue;
18108
18309
  }
@@ -18118,9 +18319,9 @@ function detectFormat(text, filename) {
18118
18319
  if (filename.includes(".vscode")) return "vscode";
18119
18320
  try {
18120
18321
  const parsed = JSON.parse(text);
18121
- if (isRecord7(parsed)) {
18122
- if (parsed.servers && isRecord7(parsed.servers) && parsed.version !== void 0) return "six";
18123
- if (parsed.servers && isRecord7(parsed.servers)) return "vscode";
18322
+ if (isRecord8(parsed)) {
18323
+ if (parsed.servers && isRecord8(parsed.servers) && parsed.version !== void 0) return "six";
18324
+ if (parsed.servers && isRecord8(parsed.servers)) return "vscode";
18124
18325
  if (parsed.mcpServers) return "claude";
18125
18326
  if (parsed.mcp) return "opencode";
18126
18327
  }
@@ -22284,5 +22485,5 @@ try {
22284
22485
  });
22285
22486
  } catch {
22286
22487
  }
22287
- var code = await run(platform, "0.31.16", args);
22488
+ var code = await run(platform, "0.31.18", args);
22288
22489
  process.exit(code);
@@ -6179,6 +6179,12 @@ function agentIgnoresConfig(platform2, path) {
6179
6179
  function projectConfigPath(platform2) {
6180
6180
  return `${platform2.cwd}/.pi/mcp.json`;
6181
6181
  }
6182
+ function cacheDir(platform2) {
6183
+ if (platform2.env.XDG_CACHE_HOME) return `${platform2.env.XDG_CACHE_HOME}/mcpcat`;
6184
+ const home = platform2.env.HOME ?? platform2.env.USERPROFILE ?? "/";
6185
+ if (platform2.env.LOCALAPPDATA) return `${platform2.env.LOCALAPPDATA}\\mcpcat\\cache`;
6186
+ return `${home}/Library/Caches/mcpcat`;
6187
+ }
6182
6188
  function stateDir(platform2) {
6183
6189
  if (platform2.env.XDG_STATE_HOME) return `${platform2.env.XDG_STATE_HOME}/mcpcat`;
6184
6190
  const home = platform2.env.HOME ?? platform2.env.USERPROFILE ?? "/";
@@ -6572,6 +6578,7 @@ var HEADER_REJECTED = "The server rejected the explicit Authorization header.";
6572
6578
  var PLACEHOLDER_NOT_ACCEPTED = "The request carried the signed-in credential's placeholder and the server still answered 401. Seven's vault fills the placeholder in when the request is forwarded, so either that forwarding is not active in this seven yet or the server rejected the credential. Try again from the Settings page's Connect, or ask whoever runs seven.";
6573
6579
  var VAULT_NO_ANSWER = "Seven's vault did not answer, so the request carried no credential. Try again, or ask whoever runs seven.";
6574
6580
  var PLACEHOLDER_REFUSED = "Seven's vault fills a placeholder in when the request is forwarded, so either that forwarding is not active in this seven yet or it failed. Try again, or ask whoever runs seven.";
6581
+ var CAUSED = /\brefused \([a-z][a-z-]*\): \S/;
6575
6582
  var NO_PLACEHOLDER = `This user gets no placeholder for this server. With no sign-in yet: ${SETTINGS_SIGN_IN} After a sign-in, the agent already has this server's tools, because a Settings sign-in gives it the server as native tools, and a human can use the server from a panel terminal as root.`;
6576
6583
  function placeholderRefusal(err) {
6577
6584
  if (!(err instanceof TypeError)) return void 0;
@@ -6579,7 +6586,7 @@ function placeholderRefusal(err) {
6579
6586
  if (code !== "EACCES" || typeof detail !== "string") return void 0;
6580
6587
  if (!detail.includes("sealed placeholder")) return void 0;
6581
6588
  const reason = detail.replace(/fnox_proxy_[0-9a-f]{24}/g, "[placeholder]");
6582
- return authError(`${reason}. ${PLACEHOLDER_REFUSED}`);
6589
+ return authError(CAUSED.test(reason) ? `${reason}.` : `${reason}. ${PLACEHOLDER_REFUSED}`);
6583
6590
  }
6584
6591
  var SettingsSignInError = class extends McpcatError {
6585
6592
  advice;
@@ -13917,10 +13924,30 @@ var HOLDER_RESTART_MAX_MS = 3e4;
13917
13924
  var HOLDER_STABLE_MS = 3e4;
13918
13925
  var HOLDER_STOP_GRACE_MS = 2e3;
13919
13926
  var HOLDER_READY_MS = 2e4;
13927
+ var FALLBACK_UNVERIFIED = "webmcp: other-extension fallback could not verify the reloaded document; tools are unavailable";
13920
13928
  var LABEL = "mcpcat serve --webmcp";
13921
- var GUARD_REFUSAL_RE = /^(?:(?:playwright-cli )?webmcp-polyfill: )?[A-Z][A-Za-z]*\.[a-z][A-Za-z]*: slicc: refused: \S/;
13929
+ var TERMINAL_REFUSALS = /* @__PURE__ */ new Set([
13930
+ "slicc: refused: target is on seven's origin",
13931
+ "slicc: refused: seven\u2019s own pages and the vault are out of reach of browser automation",
13932
+ "slicc: refused: breakpoints could run in seven\u2019s pages or the vault, so the guard doesn\u2019t offer the Debugger domains",
13933
+ "slicc: refused: update the browser guard and client",
13934
+ "slicc: refused: WebMCP installation could not verify the document; update the browser guard and client",
13935
+ "slicc: refused: WebMCP installation could not verify a usable guard and document; update the browser guard and client",
13936
+ "slicc: refused: WebMCP instrumentation is unavailable; update the browser guard and client",
13937
+ "webmcp: no tools are available through the other-extension fallback; page startup or parse-time checks may have happened before installation"
13938
+ ]);
13939
+ var FALLBACK_REFUSALS = /* @__PURE__ */ new Set([
13940
+ "other-extension fallback could not verify the reloaded document; tools are unavailable",
13941
+ "other-extension fallback could not verify the document"
13942
+ ]);
13943
+ function holderReason(line) {
13944
+ return line.trim().replace(/^playwright-cli webmcp-polyfill: /, "").replace(/^webmcp-polyfill: /, "").replace(/^[A-Z][A-Za-z]*\.[a-z][A-Za-z]*: /, "");
13945
+ }
13946
+ function isFallbackUnavailable(line) {
13947
+ return FALLBACK_REFUSALS.has(holderReason(line));
13948
+ }
13922
13949
  function isGuardRefusal(line) {
13923
- return GUARD_REFUSAL_RE.test(line.trim());
13950
+ return TERMINAL_REFUSALS.has(holderReason(line)) || isFallbackUnavailable(line);
13924
13951
  }
13925
13952
  function compareVersions(a, b) {
13926
13953
  const pa = a.split(".").map(Number);
@@ -14048,6 +14075,9 @@ function startHolder(platform2, options) {
14048
14075
  return;
14049
14076
  }
14050
14077
  proc = child;
14078
+ state.fallbackUnavailable = false;
14079
+ if (Number.isSafeInteger(child.pid) && child.pid > 0)
14080
+ report(`holder-pid ${child.pid} tab ${options.tab}`);
14051
14081
  writer = child.stdin.getWriter();
14052
14082
  let lastError;
14053
14083
  const out = drain(child.stdout, onEvent);
@@ -14055,7 +14085,8 @@ function startHolder(platform2, options) {
14055
14085
  const text = line.trim();
14056
14086
  if (!text) return;
14057
14087
  lastError = text;
14058
- if (readyAt === void 0 && isGuardRefusal(text)) refusal ??= text;
14088
+ if (isFallbackUnavailable(text)) state.fallbackUnavailable = true;
14089
+ if (isGuardRefusal(text)) refusal ??= text;
14059
14090
  else relay(text);
14060
14091
  });
14061
14092
  exited = Promise.all([out, err, child.wait()]).then(([, , result]) => {
@@ -14379,6 +14410,12 @@ function startDialogHolder(platform2, options) {
14379
14410
 
14380
14411
  // src/serve/webmcp.ts
14381
14412
  var UNAVAILABLE_RE = /WebMCP is unavailable on this page/;
14413
+ function emptyFallbackOrError(error, holderHint) {
14414
+ const hint = error.trim();
14415
+ if (hint === "webmcp: unverified isolated context" && holderHint === FALLBACK_UNVERIFIED)
14416
+ return { tools: [], hint: holderHint };
14417
+ return hint === "webmcp: no tools are available through the other-extension fallback; page startup or parse-time checks may have happened before installation" ? { tools: [], hint } : { tools: [], error };
14418
+ }
14382
14419
  function buildBaseArgs(options) {
14383
14420
  const args = [];
14384
14421
  if (options.cdp) {
@@ -14947,7 +14984,7 @@ function createWebMcpHandler(platform2, options) {
14947
14984
  async function getRpcTools() {
14948
14985
  const { tools, error, notFound, unavailable } = await listRaw();
14949
14986
  if (notFound) return null;
14950
- if (error) return { tools: [], error };
14987
+ if (error) return emptyFallbackOrError(error, options.emptyHint?.(false));
14951
14988
  const late = options.late?.() ?? false;
14952
14989
  const rpcTools = [];
14953
14990
  const hidden = {};
@@ -16109,6 +16146,7 @@ function emptyToolsHint(options, polyfill, unavailable) {
16109
16146
  return `the page shows no WebMCP tools, and the WebMCP polyfill needs playwright-cli ${POLYFILL_MIN_VERSION} or newer (found ${polyfill.oldVersion})`;
16110
16147
  }
16111
16148
  const status = polyfill.holder?.status();
16149
+ if (status?.refused && status.fallbackUnavailable) return FALLBACK_UNVERIFIED;
16112
16150
  if (isLate(polyfill.holder)) {
16113
16151
  return "the page may check for WebMCP only when it loads, before the polyfill was injected; reload the tab, or restart with --webmcp-reload (it reloads the tab)";
16114
16152
  }
@@ -16936,6 +16974,142 @@ function parseUpgradeArgs(args) {
16936
16974
  return { kind: "run", check, force };
16937
16975
  }
16938
16976
 
16977
+ // src/auth/callback-port.ts
16978
+ function hasFixedCallback(oauth) {
16979
+ if (oauth?.callbackPort !== void 0 && oauth.callbackPort > 0) return true;
16980
+ if (!oauth?.callbackUrl) return false;
16981
+ try {
16982
+ const port = Number(new URL(oauth.callbackUrl).port);
16983
+ return port > 0;
16984
+ } catch {
16985
+ return false;
16986
+ }
16987
+ }
16988
+ function needsInjectedCallbackPort(options) {
16989
+ return options.namedEntry && !hasFixedCallback(options.oauth);
16990
+ }
16991
+ async function withInjectedCallbackPortIfNeeded(platform2, name, options, fn) {
16992
+ if (!needsInjectedCallbackPort(options)) return fn(void 0);
16993
+ const port = await allocateCallbackPort();
16994
+ return withTempCallbackPortConfig(platform2, name, port, fn);
16995
+ }
16996
+ async function allocateCallbackPort() {
16997
+ const callback = await OAuthCallbackServer.listen({
16998
+ host: "127.0.0.1",
16999
+ port: 0,
17000
+ path: "/callback"
17001
+ });
17002
+ try {
17003
+ return Number(new URL(callback.redirectUrl).port);
17004
+ } finally {
17005
+ await callback.close();
17006
+ }
17007
+ }
17008
+ function isRecord7(value) {
17009
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17010
+ }
17011
+ function hostEnv(platform2) {
17012
+ const out = {};
17013
+ for (const [key, value] of Object.entries(platform2.env)) {
17014
+ if (value !== void 0) out[key] = value;
17015
+ }
17016
+ return out;
17017
+ }
17018
+ async function makeTempDir(platform2) {
17019
+ const base = `${cacheDir(platform2)}/mcp-cb`;
17020
+ await platform2.fs.mkdir(base, { mode: 448 });
17021
+ for (let i = 0; i < 32; i++) {
17022
+ const dir = `${base}/${Date.now().toString(36)}-${i.toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
17023
+ try {
17024
+ await platform2.fs.mkdirExclusive(dir);
17025
+ return dir;
17026
+ } catch {
17027
+ }
17028
+ }
17029
+ throw new Error("unable to create temporary mcp config directory");
17030
+ }
17031
+ async function removeTempDir(platform2, dir) {
17032
+ try {
17033
+ if (platform2.fs.unlink) await platform2.fs.unlink(`${dir}/mcp.json`);
17034
+ } catch {
17035
+ }
17036
+ try {
17037
+ await platform2.fs.rmdir(dir);
17038
+ } catch {
17039
+ }
17040
+ }
17041
+ async function withTempCallbackPortConfig(platform2, name, port, fn) {
17042
+ const path = userConfigPath(platform2);
17043
+ let raw;
17044
+ try {
17045
+ raw = new TextDecoder().decode(await platform2.fs.readFile(path));
17046
+ } catch {
17047
+ return fn(void 0);
17048
+ }
17049
+ let parsed;
17050
+ try {
17051
+ parsed = JSON.parse(raw);
17052
+ } catch {
17053
+ return fn(void 0);
17054
+ }
17055
+ if (!isRecord7(parsed) || !isRecord7(parsed.mcpServers) || !isRecord7(parsed.mcpServers[name])) {
17056
+ return fn(void 0);
17057
+ }
17058
+ const entry = { ...parsed.mcpServers[name] };
17059
+ const prevOauth = isRecord7(entry.oauth) ? { ...entry.oauth } : {};
17060
+ const oauth = { ...prevOauth, callbackPort: port };
17061
+ if (typeof oauth.callbackUrl === "string") {
17062
+ try {
17063
+ const url = new URL(oauth.callbackUrl);
17064
+ if (!(Number(url.port) > 0)) {
17065
+ url.port = String(port);
17066
+ oauth.callbackUrl = url.href;
17067
+ }
17068
+ } catch {
17069
+ }
17070
+ }
17071
+ entry.oauth = oauth;
17072
+ const tempServers = { ...parsed.mcpServers, [name]: entry };
17073
+ const merged = { ...parsed, mcpServers: tempServers };
17074
+ const tempDir = await makeTempDir(platform2);
17075
+ const tempPath = `${tempDir}/mcp.json`;
17076
+ await platform2.fs.writeFile(tempPath, `${JSON.stringify(merged, null, 2)}
17077
+ `, { mode: 384 });
17078
+ const env2 = { ...hostEnv(platform2), PI_MCP_CONFIG: tempPath };
17079
+ let cleaned = false;
17080
+ const cleanup = async () => {
17081
+ if (cleaned) return;
17082
+ cleaned = true;
17083
+ await removeTempDir(platform2, tempDir);
17084
+ };
17085
+ const exitFor = (signal) => signal === "SIGINT" ? 130 : signal === "SIGTERM" ? 143 : 129;
17086
+ let signalCode;
17087
+ let wake;
17088
+ const stopped = new Promise((resolve2) => {
17089
+ wake = resolve2;
17090
+ });
17091
+ if (platform2.onSignal) {
17092
+ for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
17093
+ await platform2.onSignal(signal, () => {
17094
+ void cleanup().then(() => {
17095
+ signalCode = exitFor(signal);
17096
+ wake?.();
17097
+ });
17098
+ });
17099
+ }
17100
+ }
17101
+ try {
17102
+ const result = await Promise.race([
17103
+ fn(env2).then((value) => ({ ok: true, value })),
17104
+ stopped.then(() => ({ ok: false }))
17105
+ ]);
17106
+ if (!result.ok) platform2.exit(signalCode ?? 130);
17107
+ return result.value;
17108
+ } finally {
17109
+ await cleanup();
17110
+ }
17111
+ }
17112
+
16939
17113
  // src/auth/oauth.ts
16940
17114
  var CALLBACK_HOST2 = "127.0.0.1";
16941
17115
  var CALLBACK_PATH2 = "/callback";
@@ -16951,14 +17125,23 @@ function timedFetch2(fetchImpl) {
16951
17125
  });
16952
17126
  };
16953
17127
  }
17128
+ function nonzeroPort(value) {
17129
+ return value !== void 0 && value > 0 ? value : void 0;
17130
+ }
17131
+ function preferredPortFromRegistration(configured, registered) {
17132
+ if (configured !== void 0) return configured;
17133
+ if (!registered[0]) return void 0;
17134
+ return nonzeroPort(Number(new URL(registered[0]).port));
17135
+ }
16954
17136
  function callbackSettings(oauth) {
16955
17137
  const raw = oauth?.callbackUrl ?? `http://${CALLBACK_HOST2}${CALLBACK_PATH2}`;
16956
17138
  const url = new URL(raw);
16957
17139
  const address = url.hostname.replace(/^\[|\]$/g, "");
16958
- const port = url.port ? Number(url.port) : oauth?.callbackPort;
17140
+ const port = nonzeroPort(url.port ? Number(url.port) : oauth?.callbackPort);
16959
17141
  let fixedRedirectUrl;
16960
- if (url.port) fixedRedirectUrl = oauth?.callbackUrl;
16961
- else if (port !== void 0) {
17142
+ if (nonzeroPort(url.port ? Number(url.port) : void 0) !== void 0 && oauth?.callbackUrl) {
17143
+ fixedRedirectUrl = oauth.callbackUrl;
17144
+ } else if (port !== void 0) {
16962
17145
  url.port = String(port);
16963
17146
  fixedRedirectUrl = url.href;
16964
17147
  }
@@ -16970,6 +17153,24 @@ function callbackSettings(oauth) {
16970
17153
  fixedRedirectUrl
16971
17154
  };
16972
17155
  }
17156
+ async function listenCallback(cbSettings, preferredPort) {
17157
+ try {
17158
+ return await OAuthCallbackServer.listen({
17159
+ host: cbSettings.host,
17160
+ redirectHost: cbSettings.redirectHost,
17161
+ port: preferredPort ?? 0,
17162
+ path: cbSettings.path
17163
+ });
17164
+ } catch (err) {
17165
+ if (preferredPort === void 0 || cbSettings.fixedRedirectUrl) throw err;
17166
+ return OAuthCallbackServer.listen({
17167
+ host: cbSettings.host,
17168
+ redirectHost: cbSettings.redirectHost,
17169
+ port: 0,
17170
+ path: cbSettings.path
17171
+ });
17172
+ }
17173
+ }
16973
17174
  function cimdForServer(metadata, redirectUrl) {
16974
17175
  if (!metadata?.client_id_metadata_document_supported) return void 0;
16975
17176
  if (!metadata.token_endpoint_auth_methods_supported?.includes("none")) return void 0;
@@ -17074,24 +17275,17 @@ async function signIn(options) {
17074
17275
  const stored = await store.load();
17075
17276
  const cbSettings = callbackSettings(oauth);
17076
17277
  const registered = stored?.clientInformation && "redirect_uris" in stored.clientInformation ? stored.clientInformation.redirect_uris : [];
17077
- const preferredPort = cbSettings.port ?? (registered[0] ? Number(new URL(registered[0]).port) || void 0 : void 0);
17078
- const callback = await OAuthCallbackServer.listen({
17079
- host: cbSettings.host,
17080
- redirectHost: cbSettings.redirectHost,
17081
- port: preferredPort ?? 0,
17082
- path: cbSettings.path
17083
- });
17278
+ const preferredPort = preferredPortFromRegistration(cbSettings.port, registered);
17279
+ const callback = await listenCallback(cbSettings, preferredPort);
17084
17280
  const redirectUrl = cbSettings.fixedRedirectUrl ?? callback.redirectUrl;
17085
17281
  try {
17086
17282
  if (stored) {
17087
17283
  const next = { ...stored };
17088
17284
  delete next.oauthState;
17089
- const hasCimd = buildClientMetadataDocumentFn(oauth, redirectUrl) !== void 0;
17090
- const keepClient = oauth?.clientId || (hasCimd ? !stored.clientInformation : registered.includes(redirectUrl));
17091
- if (!keepClient) {
17285
+ delete next.tokens;
17286
+ delete next.tokensExpireAt;
17287
+ if (!oauth?.clientId) {
17092
17288
  delete next.clientInformation;
17093
- delete next.tokens;
17094
- delete next.tokensExpireAt;
17095
17289
  }
17096
17290
  await store.save(next);
17097
17291
  }
@@ -17174,13 +17368,14 @@ async function resolveLoginTarget(platform2, target) {
17174
17368
  return {
17175
17369
  name: target,
17176
17370
  serverUrl: httpConfig.url,
17177
- oauth: httpConfig.oauth
17371
+ oauth: httpConfig.oauth,
17372
+ namedEntry: true
17178
17373
  };
17179
17374
  }
17180
17375
  const resolved = resolveServerName(target);
17181
17376
  if (resolved.isUrl) {
17182
17377
  const url = withScheme(target);
17183
- return { name: resolved.name, serverUrl: url, oauth: void 0 };
17378
+ return { name: resolved.name, serverUrl: url, oauth: void 0, namedEntry: false };
17184
17379
  }
17185
17380
  throw new Error(`unknown target '${target}': not a URL and not found in mcp.json`);
17186
17381
  }
@@ -17211,24 +17406,31 @@ async function jerkLogin(platform2, target, resolved, args) {
17211
17406
  if (args.includes("--no-browser")) {
17212
17407
  argv.push("--no-browser");
17213
17408
  }
17214
- const proc = await platform2.spawn({ argv });
17215
- proc.stdin.close().catch(() => {
17216
- });
17217
- const [, errText, result] = await Promise.all([
17218
- readStream2(proc.stdout),
17219
- readStream2(proc.stderr),
17220
- proc.wait()
17221
- ]);
17222
- if (result.status === 0) {
17223
- await platform2.write(1, `Signed in to ${target}
17409
+ return withInjectedCallbackPortIfNeeded(
17410
+ platform2,
17411
+ resolved.name,
17412
+ { namedEntry: resolved.namedEntry, oauth: resolved.oauth },
17413
+ async (env2) => {
17414
+ const proc = await platform2.spawn({ argv, env: env2 });
17415
+ proc.stdin.close().catch(() => {
17416
+ });
17417
+ const [, errText, result] = await Promise.all([
17418
+ readStream2(proc.stdout),
17419
+ readStream2(proc.stderr),
17420
+ proc.wait()
17421
+ ]);
17422
+ if (result.status === 0) {
17423
+ await platform2.write(1, `Signed in to ${target}
17224
17424
  `);
17225
- return 0;
17226
- }
17227
- if (errText.trim()) {
17228
- await platform2.write(2, sanitizeTerminal(errText));
17229
- }
17230
- if (result.status === JERK_EXIT_SIGN_IN) return 4;
17231
- return result.status;
17425
+ return 0;
17426
+ }
17427
+ if (errText.trim()) {
17428
+ await platform2.write(2, sanitizeTerminal(errText));
17429
+ }
17430
+ if (result.status === JERK_EXIT_SIGN_IN) return 4;
17431
+ return result.status;
17432
+ }
17433
+ );
17232
17434
  }
17233
17435
  async function jerkLogout(platform2, target, resolved, source) {
17234
17436
  const id = jerkId(resolved.name, resolved.serverUrl);
@@ -17405,11 +17607,11 @@ function concatUint8Arrays(arrays) {
17405
17607
  }
17406
17608
 
17407
17609
  // src/config/import.ts
17408
- function isRecord7(value) {
17610
+ function isRecord8(value) {
17409
17611
  return typeof value === "object" && value !== null && !Array.isArray(value);
17410
17612
  }
17411
17613
  function isStringRecord2(value) {
17412
- return isRecord7(value) && Object.values(value).every((v) => typeof v === "string");
17614
+ return isRecord8(value) && Object.values(value).every((v) => typeof v === "string");
17413
17615
  }
17414
17616
  function tryParse(text) {
17415
17617
  try {
@@ -17439,17 +17641,17 @@ function importClaude(text, names) {
17439
17641
  result.errors.push(error);
17440
17642
  return result;
17441
17643
  }
17442
- if (!isRecord7(parsed)) {
17644
+ if (!isRecord8(parsed)) {
17443
17645
  result.errors.push("expected an object");
17444
17646
  return result;
17445
17647
  }
17446
17648
  const mcpServers = parsed.mcpServers;
17447
- if (!isRecord7(mcpServers)) {
17649
+ if (!isRecord8(mcpServers)) {
17448
17650
  result.errors.push('no "mcpServers" object');
17449
17651
  return result;
17450
17652
  }
17451
17653
  for (const [name, entry] of filterNames(Object.entries(mcpServers), names)) {
17452
- if (!isRecord7(entry)) {
17654
+ if (!isRecord8(entry)) {
17453
17655
  result.errors.push(`${name}: not an object`);
17454
17656
  continue;
17455
17657
  }
@@ -17464,17 +17666,17 @@ function importVSCode(text, names) {
17464
17666
  result.errors.push(error);
17465
17667
  return result;
17466
17668
  }
17467
- if (!isRecord7(parsed)) {
17669
+ if (!isRecord8(parsed)) {
17468
17670
  result.errors.push("expected an object");
17469
17671
  return result;
17470
17672
  }
17471
17673
  const servers = parsed.servers ?? parsed.mcpServers;
17472
- if (!isRecord7(servers)) {
17674
+ if (!isRecord8(servers)) {
17473
17675
  result.errors.push('no "servers" or "mcpServers" object');
17474
17676
  return result;
17475
17677
  }
17476
17678
  for (const [name, entry] of filterNames(Object.entries(servers), names)) {
17477
- if (!isRecord7(entry)) {
17679
+ if (!isRecord8(entry)) {
17478
17680
  result.errors.push(`${name}: not an object`);
17479
17681
  continue;
17480
17682
  }
@@ -17508,17 +17710,17 @@ function importSixServers(text, names) {
17508
17710
  result.errors.push(error);
17509
17711
  return result;
17510
17712
  }
17511
- if (!isRecord7(parsed)) {
17713
+ if (!isRecord8(parsed)) {
17512
17714
  result.errors.push("expected an object");
17513
17715
  return result;
17514
17716
  }
17515
17717
  const servers = parsed.servers;
17516
- if (!isRecord7(servers)) {
17718
+ if (!isRecord8(servers)) {
17517
17719
  result.errors.push('no "servers" object');
17518
17720
  return result;
17519
17721
  }
17520
17722
  for (const [name, entry] of filterNames(Object.entries(servers), names)) {
17521
- if (!isRecord7(entry)) {
17723
+ if (!isRecord8(entry)) {
17522
17724
  result.errors.push(`${name}: not an object`);
17523
17725
  continue;
17524
17726
  }
@@ -17526,7 +17728,7 @@ function importSixServers(text, names) {
17526
17728
  if (typeof entry.url === "string") config.url = entry.url;
17527
17729
  if (isStringRecord2(entry.headers)) config.headers = entry.headers;
17528
17730
  if (typeof entry.exposure === "string") config.exposure = entry.exposure;
17529
- if (isRecord7(entry.toolExposure)) config.toolExposure = entry.toolExposure;
17731
+ if (isRecord8(entry.toolExposure)) config.toolExposure = entry.toolExposure;
17530
17732
  result.servers.push({ name, config });
17531
17733
  }
17532
17734
  return result;
@@ -17543,7 +17745,7 @@ function importCodexToml(text, names) {
17543
17745
  if (fields.command) config.command = fields.command;
17544
17746
  if (fields.args) config.args = fields.args;
17545
17747
  if (fields.url) config.url = fields.url;
17546
- if (fields.env && isRecord7(fields.env)) config.env = fields.env;
17748
+ if (fields.env && isRecord8(fields.env)) config.env = fields.env;
17547
17749
  result.servers.push({ name, config });
17548
17750
  }
17549
17751
  return result;
@@ -17595,7 +17797,7 @@ function convertOpenCodeLocal(entry) {
17595
17797
  } else if (typeof entry.command === "string") {
17596
17798
  config.command = entry.command;
17597
17799
  }
17598
- if (isRecord7(entry.environment)) {
17800
+ if (isRecord8(entry.environment)) {
17599
17801
  const env2 = {};
17600
17802
  for (const [k, v] of Object.entries(entry.environment)) {
17601
17803
  if (typeof v === "string")
@@ -17618,17 +17820,17 @@ function importOpenCode(text, names) {
17618
17820
  result.errors.push(error);
17619
17821
  return result;
17620
17822
  }
17621
- if (!isRecord7(parsed)) {
17823
+ if (!isRecord8(parsed)) {
17622
17824
  result.errors.push("expected an object");
17623
17825
  return result;
17624
17826
  }
17625
17827
  const mcpServers = parsed.mcpServers ?? parsed.mcp;
17626
- if (!isRecord7(mcpServers)) {
17828
+ if (!isRecord8(mcpServers)) {
17627
17829
  result.errors.push("no mcp servers found");
17628
17830
  return result;
17629
17831
  }
17630
17832
  for (const [name, entry] of filterNames(Object.entries(mcpServers), names)) {
17631
- if (!isRecord7(entry)) {
17833
+ if (!isRecord8(entry)) {
17632
17834
  result.errors.push(`${name}: not an object`);
17633
17835
  continue;
17634
17836
  }
@@ -17644,9 +17846,9 @@ function detectFormat(text, filename) {
17644
17846
  if (filename.includes(".vscode")) return "vscode";
17645
17847
  try {
17646
17848
  const parsed = JSON.parse(text);
17647
- if (isRecord7(parsed)) {
17648
- if (parsed.servers && isRecord7(parsed.servers) && parsed.version !== void 0) return "six";
17649
- if (parsed.servers && isRecord7(parsed.servers)) return "vscode";
17849
+ if (isRecord8(parsed)) {
17850
+ if (parsed.servers && isRecord8(parsed.servers) && parsed.version !== void 0) return "six";
17851
+ if (parsed.servers && isRecord8(parsed.servers)) return "vscode";
17650
17852
  if (parsed.mcpServers) return "claude";
17651
17853
  if (parsed.mcp) return "opencode";
17652
17854
  }
@@ -21238,6 +21440,7 @@ async function kernelSpawn(ctx, options) {
21238
21440
  const useGroupKill = groupSpawnSupported;
21239
21441
  let waited;
21240
21442
  return {
21443
+ pid: childPid,
21241
21444
  stdin: result.stdin ?? new WritableStream(),
21242
21445
  stdout: result.stdout,
21243
21446
  stderr: result.stderr,
@@ -21651,7 +21854,7 @@ async function main(ctx) {
21651
21854
  packageDir: "/node_modules/@ai-ecoverse/mcpcat"
21652
21855
  });
21653
21856
  const args = ctx.argv.slice(1);
21654
- return run(platform2, "0.31.16", args);
21857
+ return run(platform2, "0.31.18", args);
21655
21858
  }
21656
21859
  export {
21657
21860
  main as default
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/mcpcat",
3
- "version": "0.31.16",
3
+ "version": "0.31.18",
4
4
  "description": "A curl for the Model Context Protocol",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -58,16 +58,16 @@
58
58
  "trust:dry": "npx fledgling sync --dry-run -y --skip-publish"
59
59
  },
60
60
  "devDependencies": {
61
- "@ai-ecoverse/slicc-kernel": "1.172.0",
62
- "@ai-ecoverse/slicc-shared-web": "2.13.7",
63
- "@ai-ecoverse/slicc-vault": "1.19.0",
61
+ "@ai-ecoverse/slicc-kernel": "1.174.0",
62
+ "@ai-ecoverse/slicc-shared-web": "2.13.10",
63
+ "@ai-ecoverse/slicc-vault": "1.22.0",
64
64
  "@biomejs/biome": "2.6.3",
65
65
  "@earendil-works/pi-codemode": "1.4.0",
66
66
  "@earendil-works/pi-mcp": "1.4.0",
67
67
  "@types/node": "26.6.5",
68
68
  "esbuild": "0.28.2",
69
69
  "lefthook": "2.2.2",
70
- "monocart-coverage-reports": "2.13.2",
70
+ "monocart-coverage-reports": "3.2.0",
71
71
  "proper-lockfile": "4.1.2",
72
72
  "quickjs-wasi": "3.6.3",
73
73
  "semantic-release": "25.0.9",