@ai-ecoverse/mcpcat 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4899,6 +4899,197 @@ var init_portable = __esm({
4899
4899
  }
4900
4900
  });
4901
4901
 
4902
+ // src/args/coerce.ts
4903
+ function asSchemaObject(schema) {
4904
+ return schema ?? {};
4905
+ }
4906
+ function emptyArgObject() {
4907
+ return /* @__PURE__ */ Object.create(null);
4908
+ }
4909
+ function isUnsafeObjectKey(key) {
4910
+ return key === "__proto__" || key === "prototype" || key === "constructor";
4911
+ }
4912
+ function isPlainObject(value) {
4913
+ return typeof value === "object" && value !== null && !Array.isArray(value) && value !== Object.prototype;
4914
+ }
4915
+ function splitFlag(a) {
4916
+ const eq = a.indexOf("=");
4917
+ if (eq > 2) return { key: a.slice(2, eq), inlineValue: a.slice(eq + 1) };
4918
+ return { key: a.slice(2), inlineValue: void 0 };
4919
+ }
4920
+ function coerceScalar(raw, type) {
4921
+ switch (type) {
4922
+ case "integer": {
4923
+ if (!/^-?\d+$/.test(raw)) return { ok: false, error: `expected integer, got "${raw}"` };
4924
+ return { ok: true, value: Number(raw) };
4925
+ }
4926
+ case "number": {
4927
+ const n = Number(raw);
4928
+ if (!Number.isFinite(n)) return { ok: false, error: `expected number, got "${raw}"` };
4929
+ return { ok: true, value: n };
4930
+ }
4931
+ case "boolean": {
4932
+ if (raw === "true" || raw === "1" || raw === "yes") return { ok: true, value: true };
4933
+ if (raw === "false" || raw === "0" || raw === "no") return { ok: true, value: false };
4934
+ return { ok: false, error: `expected boolean, got "${raw}"` };
4935
+ }
4936
+ case "object":
4937
+ return coerceJsonObject(raw);
4938
+ default:
4939
+ return { ok: true, value: raw };
4940
+ }
4941
+ }
4942
+ function coerceJsonObject(raw) {
4943
+ let parsed;
4944
+ try {
4945
+ parsed = JSON.parse(raw);
4946
+ } catch {
4947
+ return { ok: false, error: `expected object JSON, got "${raw}"` };
4948
+ }
4949
+ if (!isPlainObject(parsed)) {
4950
+ return { ok: false, error: `expected object JSON, got "${raw}"` };
4951
+ }
4952
+ return { ok: true, value: parsed };
4953
+ }
4954
+ function resolveToolFlag(key, properties) {
4955
+ if (Object.hasOwn(properties, key)) {
4956
+ return { ok: true, rootKey: key, dottedPath: null, meta: properties[key] ?? {} };
4957
+ }
4958
+ const dot = key.indexOf(".");
4959
+ if (dot <= 0) return { ok: false, error: `unknown flag: --${key}` };
4960
+ const rootKey = key.slice(0, dot);
4961
+ const rest = key.slice(dot + 1);
4962
+ if (!Object.hasOwn(properties, rootKey) || rest.length === 0) {
4963
+ return { ok: false, error: `unknown flag: --${key}` };
4964
+ }
4965
+ const parts = rest.split(".");
4966
+ if (isUnsafeObjectKey(rootKey) || parts.some((p) => p.length === 0 || isUnsafeObjectKey(p))) {
4967
+ return { ok: false, error: `unknown flag: --${key}` };
4968
+ }
4969
+ return { ok: true, rootKey, dottedPath: parts, meta: properties[rootKey] ?? {} };
4970
+ }
4971
+ function readFlagRaw(args, i, key, inlineValue, type, dottedPath) {
4972
+ if (inlineValue !== void 0) {
4973
+ return { ok: true, raw: inlineValue, nextIndex: i + 1, bareBoolean: false };
4974
+ }
4975
+ const next = args[i + 1];
4976
+ if (!dottedPath && type === "boolean" && (next === void 0 || next.startsWith("--"))) {
4977
+ return { ok: true, raw: "", nextIndex: i + 1, bareBoolean: true };
4978
+ }
4979
+ if (next === void 0) {
4980
+ return { ok: false, error: `flag --${key} requires a value` };
4981
+ }
4982
+ return { ok: true, raw: next, nextIndex: i + 2, bareBoolean: false };
4983
+ }
4984
+ function setDotted(out, rootKey, path, value) {
4985
+ if (isUnsafeObjectKey(rootKey) || path.some((p) => p.length === 0 || isUnsafeObjectKey(p))) {
4986
+ return { ok: false, error: `unknown flag: --${rootKey}.${path.join(".")}` };
4987
+ }
4988
+ const existing = Object.hasOwn(out, rootKey) ? out[rootKey] : void 0;
4989
+ if (existing === void 0) {
4990
+ out[rootKey] = emptyArgObject();
4991
+ } else if (!isPlainObject(existing)) {
4992
+ return { ok: false, error: `--${rootKey}: cannot nest into a ${typeof existing} value` };
4993
+ }
4994
+ let cursor = out[rootKey];
4995
+ for (let i = 0; i < path.length - 1; i++) {
4996
+ const seg = path[i];
4997
+ const next = Object.hasOwn(cursor, seg) ? cursor[seg] : void 0;
4998
+ if (next === void 0) {
4999
+ cursor[seg] = emptyArgObject();
5000
+ } else if (!isPlainObject(next)) {
5001
+ return {
5002
+ ok: false,
5003
+ error: `--${rootKey}.${path.slice(0, i + 1).join(".")}: cannot nest into a ${typeof next} value`
5004
+ };
5005
+ }
5006
+ cursor = cursor[seg];
5007
+ }
5008
+ cursor[path[path.length - 1]] = value;
5009
+ return { ok: true };
5010
+ }
5011
+ function assignFlagValue(out, rootKey, dottedPath, value, isArray) {
5012
+ if (dottedPath) return setDotted(out, rootKey, dottedPath, value);
5013
+ if (isArray) {
5014
+ const prev = out[rootKey];
5015
+ if (Array.isArray(prev)) prev.push(value);
5016
+ else out[rootKey] = [value];
5017
+ return { ok: true };
5018
+ }
5019
+ const existing = out[rootKey];
5020
+ if (isPlainObject(existing) && isPlainObject(value)) {
5021
+ Object.assign(existing, value);
5022
+ return { ok: true };
5023
+ }
5024
+ out[rootKey] = value;
5025
+ return { ok: true };
5026
+ }
5027
+ function parseOneFlag(args, i, properties, out) {
5028
+ const a = args[i];
5029
+ if (a === "--") return { ok: true, nextIndex: args.length };
5030
+ if (a.startsWith("--no-") && a.length > 5) {
5031
+ const key2 = a.slice(5);
5032
+ if (Object.hasOwn(properties, key2) && properties[key2]?.type === "boolean") {
5033
+ out[key2] = false;
5034
+ return { ok: true, nextIndex: i + 1 };
5035
+ }
5036
+ return { ok: false, error: `unknown flag: ${a}` };
5037
+ }
5038
+ if (!a.startsWith("--")) {
5039
+ return { ok: false, error: `unexpected positional argument "${a}"` };
5040
+ }
5041
+ const { key, inlineValue } = splitFlag(a);
5042
+ const resolved = resolveToolFlag(key, properties);
5043
+ if (!resolved.ok) return resolved;
5044
+ const { rootKey, dottedPath, meta } = resolved;
5045
+ const declaredType = meta.type;
5046
+ const type = declaredType ?? "string";
5047
+ const isArray = type === "array";
5048
+ const itemType = (isArray ? meta.items?.type : void 0) ?? "string";
5049
+ if (dottedPath && declaredType !== void 0 && declaredType !== "object") {
5050
+ return { ok: false, error: `unknown flag: --${key}` };
5051
+ }
5052
+ const taken = readFlagRaw(args, i, key, inlineValue, type, dottedPath);
5053
+ if (!taken.ok) return taken;
5054
+ if (taken.bareBoolean) {
5055
+ out[rootKey] = true;
5056
+ return { ok: true, nextIndex: taken.nextIndex };
5057
+ }
5058
+ const valueType = dottedPath ? "string" : isArray ? itemType : type;
5059
+ const coerced = coerceScalar(taken.raw, valueType);
5060
+ if (!coerced.ok) return { ok: false, error: `--${key}: ${coerced.error}` };
5061
+ const assigned = assignFlagValue(out, rootKey, dottedPath, coerced.value, isArray);
5062
+ if (!assigned.ok) return assigned;
5063
+ return { ok: true, nextIndex: taken.nextIndex };
5064
+ }
5065
+ function coerceArgsBySchema(args, schema, options) {
5066
+ const s = asSchemaObject(schema);
5067
+ const properties = s.properties ?? {};
5068
+ const required = new Set(Array.isArray(s.required) ? s.required : []);
5069
+ const out = emptyArgObject();
5070
+ let i = 0;
5071
+ while (i < args.length) {
5072
+ const result = parseOneFlag(args, i, properties, out);
5073
+ if (!result.ok) return result;
5074
+ i = result.nextIndex;
5075
+ }
5076
+ if (!options?.noValidate) {
5077
+ for (const r of required) {
5078
+ if (!Object.hasOwn(out, r)) {
5079
+ return { ok: false, error: `missing required flag --${r}` };
5080
+ }
5081
+ }
5082
+ }
5083
+ return { ok: true, value: out };
5084
+ }
5085
+ function mergeArgs(base, flags) {
5086
+ const result = { ...base };
5087
+ for (const [k, v] of Object.entries(flags)) {
5088
+ result[k] = v;
5089
+ }
5090
+ return result;
5091
+ }
5092
+
4902
5093
  // node_modules/@earendil-works/pi-mcp/dist/protocol/jsonrpc.js
4903
5094
  var JSON_RPC_ERROR_CODES = {
4904
5095
  parseError: -32700,
@@ -6252,67 +6443,6 @@ var PlatformStdioTransport = class {
6252
6443
  }
6253
6444
  };
6254
6445
 
6255
- // src/client/pi-connection.ts
6256
- async function createPiConnection(options) {
6257
- const clientOptions = {
6258
- name: "mcpcat",
6259
- version: options.version ?? "0.0.0"
6260
- };
6261
- const client = new McpClient(clientOptions);
6262
- let transport;
6263
- if (options.url) {
6264
- transport = new StreamableHttpTransport({
6265
- url: options.url,
6266
- headers: options.headers,
6267
- fetch: options.fetchImpl
6268
- });
6269
- } else if (options.command) {
6270
- if (options.platform) {
6271
- transport = new PlatformStdioTransport({
6272
- command: options.command,
6273
- args: options.args,
6274
- env: options.env,
6275
- cwd: options.cwd,
6276
- inheritEnv: true,
6277
- platform: options.platform
6278
- });
6279
- } else {
6280
- transport = new StdioTransport({
6281
- command: options.command,
6282
- args: options.args,
6283
- env: options.env,
6284
- cwd: options.cwd,
6285
- inheritEnv: true,
6286
- stderr: "pipe"
6287
- });
6288
- }
6289
- } else {
6290
- throw new Error("PiConnection requires either a url or a command");
6291
- }
6292
- const initResult = await client.connect(transport);
6293
- return {
6294
- async info() {
6295
- return {
6296
- name: initResult.serverInfo?.name,
6297
- version: initResult.serverInfo?.version,
6298
- title: initResult.serverInfo?.title,
6299
- protocolVersion: initResult.protocolVersion,
6300
- capabilities: initResult.capabilities,
6301
- instructions: initResult.instructions
6302
- };
6303
- },
6304
- async listTools() {
6305
- return client.listTools();
6306
- },
6307
- async callTool(name, args) {
6308
- return client.callTool(name, args);
6309
- },
6310
- async close() {
6311
- await client.close();
6312
- }
6313
- };
6314
- }
6315
-
6316
6446
  // src/client/stateless-connection.ts
6317
6447
  var STATELESS_PROTOCOL_VERSION = "2026-07-28";
6318
6448
  var StatelessRpcError = class extends Error {
@@ -6341,6 +6471,22 @@ var InputRequiredError = class extends Error {
6341
6471
  this.requestJson = requestJson;
6342
6472
  }
6343
6473
  };
6474
+ var McpTimeoutError2 = class extends Error {
6475
+ constructor(method, ms) {
6476
+ super(`timeout after ${ms}ms waiting for ${method}`);
6477
+ this.name = "McpTimeoutError";
6478
+ }
6479
+ };
6480
+ var McpAuthError = class extends Error {
6481
+ status;
6482
+ target;
6483
+ constructor(status, target) {
6484
+ super(`HTTP ${status}: authentication required. Run: mcpcat login ${target}`);
6485
+ this.name = "McpAuthError";
6486
+ this.status = status;
6487
+ this.target = target;
6488
+ }
6489
+ };
6344
6490
  function selectSseFrame(body, targetId) {
6345
6491
  const text = new TextDecoder().decode(body);
6346
6492
  const blocks = text.replace(/\r\n/g, "\n").split(/\n\n/);
@@ -6371,25 +6517,66 @@ async function createStatelessConnection(options) {
6371
6517
  headers: extraHeaders,
6372
6518
  fetchImpl,
6373
6519
  timeoutMs = 6e4,
6374
- clientInfo = { name: "mcpcat", version: "0.0.0" }
6520
+ clientInfo = { name: "mcpcat", version: "0.0.0" },
6521
+ signal: externalSignal,
6522
+ verbose
6375
6523
  } = options;
6376
6524
  let nextId2 = 1;
6377
6525
  let negotiatedVersion;
6378
- async function request(method, params) {
6379
- const id = nextId2++;
6380
- const headers = {
6526
+ function buildHeaders(method, params) {
6527
+ const h = {
6381
6528
  "Content-Type": "application/json",
6382
6529
  Accept: "application/json, text/event-stream",
6383
6530
  "Mcp-Method": method,
6384
6531
  ...extraHeaders
6385
6532
  };
6386
- const mcpName = mcpNameForMethod(method, params);
6387
- if (mcpName) {
6388
- headers["Mcp-Name"] = mcpName;
6533
+ const n = mcpNameForMethod(method, params);
6534
+ if (n) h["Mcp-Name"] = n;
6535
+ if (negotiatedVersion) h["Mcp-Protocol-Version"] = negotiatedVersion;
6536
+ return h;
6537
+ }
6538
+ function makeAbort() {
6539
+ const controller = new AbortController();
6540
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
6541
+ if (externalSignal) {
6542
+ if (externalSignal.aborted) {
6543
+ clearTimeout(timer);
6544
+ controller.abort();
6545
+ } else {
6546
+ externalSignal.addEventListener(
6547
+ "abort",
6548
+ () => {
6549
+ clearTimeout(timer);
6550
+ controller.abort();
6551
+ },
6552
+ { once: true }
6553
+ );
6554
+ }
6389
6555
  }
6390
- if (negotiatedVersion) {
6391
- headers["Mcp-Protocol-Version"] = negotiatedVersion;
6556
+ return { controller, timer };
6557
+ }
6558
+ function logHeaders(dir, entries) {
6559
+ if (!verbose?.enabled) return;
6560
+ for (const [k, v] of entries) {
6561
+ if (dir === ">") verbose.headerOut(k, v);
6562
+ else verbose.headerIn(k, v);
6563
+ }
6564
+ }
6565
+ async function handleErrorStatus(res, method) {
6566
+ if (res.status === 401 || res.status === 403) throw new McpAuthError(res.status, url);
6567
+ const text = await res.text();
6568
+ let rpc;
6569
+ try {
6570
+ const p = JSON.parse(text);
6571
+ if (p.error && typeof p.error.code === "number") rpc = p.error;
6572
+ } catch {
6392
6573
  }
6574
+ if (rpc) throw new StatelessRpcError(rpc.code, rpc.message, rpc.data);
6575
+ throw new StatelessHttpError(res.status, res.statusText, method, text.slice(0, 512));
6576
+ }
6577
+ async function request(method, params) {
6578
+ const id = nextId2++;
6579
+ const headers = buildHeaders(method, params);
6393
6580
  const meta = {
6394
6581
  protocolVersion: negotiatedVersion ?? STATELESS_PROTOCOL_VERSION,
6395
6582
  clientInfo
@@ -6400,8 +6587,11 @@ async function createStatelessConnection(options) {
6400
6587
  method,
6401
6588
  params: { ...params, _meta: meta }
6402
6589
  });
6403
- const controller = new AbortController();
6404
- const timer = setTimeout(() => controller.abort(), timeoutMs);
6590
+ if (verbose?.enabled) {
6591
+ verbose.request(method, body);
6592
+ logHeaders(">", Object.entries(headers));
6593
+ }
6594
+ const { controller, timer } = makeAbort();
6405
6595
  let res;
6406
6596
  try {
6407
6597
  res = await fetchImpl(url, {
@@ -6410,35 +6600,28 @@ async function createStatelessConnection(options) {
6410
6600
  body,
6411
6601
  signal: controller.signal
6412
6602
  });
6603
+ } catch (err) {
6604
+ clearTimeout(timer);
6605
+ if (controller.signal.aborted && !externalSignal?.aborted) {
6606
+ throw new McpTimeoutError2(method, timeoutMs);
6607
+ }
6608
+ throw err;
6413
6609
  } finally {
6414
6610
  clearTimeout(timer);
6415
6611
  }
6416
- if (res.status === 401) {
6417
- throw new StatelessHttpError(401, "Unauthorized", method, "");
6418
- }
6419
- if (res.status >= 400) {
6420
- const text = await res.text();
6421
- let rpcError3;
6422
- try {
6423
- const parsed = JSON.parse(text);
6424
- if (parsed.error && typeof parsed.error.code === "number") {
6425
- rpcError3 = parsed.error;
6426
- }
6427
- } catch {
6428
- }
6429
- if (rpcError3) {
6430
- throw new StatelessRpcError(rpcError3.code, rpcError3.message, rpcError3.data);
6431
- }
6432
- throw new StatelessHttpError(res.status, res.statusText, method, text.slice(0, 512));
6612
+ if (verbose?.enabled) {
6613
+ verbose.http("<", `HTTP/1.1 ${res.status} ${res.statusText}`);
6614
+ logHeaders("<", res.headers.entries());
6433
6615
  }
6616
+ if (res.status >= 400) await handleErrorStatus(res, method);
6434
6617
  const ct = res.headers.get("content-type") ?? "";
6435
6618
  let frame;
6436
6619
  if (ct.toLowerCase().includes("text/event-stream")) {
6437
- const bytes = new Uint8Array(await res.arrayBuffer());
6438
- frame = selectSseFrame(bytes, id);
6620
+ frame = selectSseFrame(new Uint8Array(await res.arrayBuffer()), id);
6439
6621
  } else {
6440
6622
  frame = await res.json();
6441
6623
  }
6624
+ if (verbose?.enabled) verbose.response(JSON.stringify(frame));
6442
6625
  if (frame.error) {
6443
6626
  throw new StatelessRpcError(frame.error.code, frame.error.message, frame.error.data);
6444
6627
  }
@@ -6458,28 +6641,26 @@ async function createStatelessConnection(options) {
6458
6641
  return {
6459
6642
  async info() {
6460
6643
  if (serverInfoCache) return serverInfoCache;
6461
- const result = discoverResult2;
6644
+ const r = discoverResult2;
6462
6645
  serverInfoCache = {
6463
- name: result?.serverInfo?.name,
6464
- version: result?.serverInfo?.version,
6465
- title: result?.serverInfo?.title,
6646
+ name: r?.serverInfo?.name,
6647
+ version: r?.serverInfo?.version,
6648
+ title: r?.serverInfo?.title,
6466
6649
  protocolVersion: STATELESS_PROTOCOL_VERSION,
6467
- capabilities: result?.capabilities ?? {},
6468
- instructions: result?.instructions
6650
+ capabilities: r?.capabilities ?? {},
6651
+ instructions: r?.instructions
6469
6652
  };
6470
6653
  return serverInfoCache;
6471
6654
  },
6472
6655
  async listTools() {
6473
- const result = await request("tools/list", {});
6474
- return result?.tools ?? [];
6656
+ return (await request("tools/list", {}))?.tools ?? [];
6475
6657
  },
6476
- async callTool(name, args) {
6658
+ async callTool(name, args, _callOptions) {
6477
6659
  const result = await request("tools/call", {
6478
6660
  name,
6479
6661
  arguments: args ?? {}
6480
6662
  });
6481
- const resultType = result.resultType ?? "complete";
6482
- if (resultType === "input_required") {
6663
+ if ((result.resultType ?? "complete") === "input_required") {
6483
6664
  throw new InputRequiredError(
6484
6665
  JSON.stringify({ name, arguments: args ?? {}, result }, null, 2)
6485
6666
  );
@@ -6491,28 +6672,138 @@ async function createStatelessConnection(options) {
6491
6672
  };
6492
6673
  }
6493
6674
 
6675
+ // src/client/pi-connection.ts
6676
+ function buildStderrHandler(options) {
6677
+ if (!options.verbose?.enabled) return void 0;
6678
+ return (chunk) => {
6679
+ for (const l of chunk.split("\n")) {
6680
+ if (l.trim()) options.verbose.server(l);
6681
+ }
6682
+ };
6683
+ }
6684
+ function buildTransport(options) {
6685
+ if (options.url) {
6686
+ return new StreamableHttpTransport({
6687
+ url: options.url,
6688
+ headers: options.headers,
6689
+ fetch: options.fetchImpl
6690
+ });
6691
+ }
6692
+ if (!options.command) throw new Error("PiConnection requires either a url or a command");
6693
+ if (options.platform) {
6694
+ return new PlatformStdioTransport({
6695
+ command: options.command,
6696
+ args: options.args,
6697
+ env: options.env,
6698
+ cwd: options.cwd,
6699
+ inheritEnv: true,
6700
+ platform: options.platform,
6701
+ onStderr: buildStderrHandler(options)
6702
+ });
6703
+ }
6704
+ return new StdioTransport({
6705
+ command: options.command,
6706
+ args: options.args,
6707
+ env: options.env,
6708
+ cwd: options.cwd,
6709
+ inheritEnv: true,
6710
+ stderr: "pipe",
6711
+ onStderr: buildStderrHandler(options)
6712
+ });
6713
+ }
6714
+ async function createPiConnection(options) {
6715
+ const clientOptions = {
6716
+ name: "mcpcat",
6717
+ version: options.version ?? "0.0.0",
6718
+ requestTimeoutMs: options.timeoutMs
6719
+ };
6720
+ const client = new McpClient(clientOptions);
6721
+ const transport = buildTransport(options);
6722
+ if (options.verbose?.enabled) {
6723
+ transport.onMessage((msg) => {
6724
+ options.verbose.response(JSON.stringify(msg));
6725
+ });
6726
+ }
6727
+ try {
6728
+ let reqOpts2 = function() {
6729
+ const o = {};
6730
+ if (options.signal) o.signal = options.signal;
6731
+ if (options.verbose?.enabled) {
6732
+ o.onProgress = (p) => {
6733
+ options.verbose.progress(`progress: ${p.message ?? ""}`);
6734
+ };
6735
+ }
6736
+ return o;
6737
+ };
6738
+ var reqOpts = reqOpts2;
6739
+ const initResult = await client.connect(transport);
6740
+ return {
6741
+ async info() {
6742
+ return {
6743
+ name: initResult.serverInfo?.name,
6744
+ version: initResult.serverInfo?.version,
6745
+ title: initResult.serverInfo?.title,
6746
+ protocolVersion: initResult.protocolVersion,
6747
+ capabilities: initResult.capabilities,
6748
+ instructions: initResult.instructions
6749
+ };
6750
+ },
6751
+ async listTools() {
6752
+ return client.listTools(reqOpts2());
6753
+ },
6754
+ async callTool(name, args, callOptions) {
6755
+ const o = reqOpts2();
6756
+ if (callOptions?.onProgress) {
6757
+ const outer = callOptions.onProgress;
6758
+ const inner = o.onProgress;
6759
+ o.onProgress = (p) => {
6760
+ outer(p.progress, p.total, p.message);
6761
+ inner?.(p);
6762
+ };
6763
+ }
6764
+ return client.callTool(name, args, o);
6765
+ },
6766
+ async close() {
6767
+ await client.close();
6768
+ }
6769
+ };
6770
+ } catch (err) {
6771
+ const msg = err.message ?? "";
6772
+ if (msg.includes("401") || msg.includes("403") || msg.includes("Unauthorized")) {
6773
+ throw new McpAuthError(401, options.url ?? options.command ?? "");
6774
+ }
6775
+ if (msg.includes("timeout") || msg.includes("Timeout") || msg.includes("ETIMEDOUT")) {
6776
+ throw new McpTimeoutError2("connect", options.timeoutMs ?? 6e4);
6777
+ }
6778
+ throw err;
6779
+ }
6780
+ }
6781
+
6494
6782
  // src/client/connect.ts
6495
6783
  async function connect(platform2, options) {
6784
+ const shared = {
6785
+ version: options.version,
6786
+ timeoutMs: options.timeoutMs,
6787
+ signal: options.signal,
6788
+ verbose: options.verbose,
6789
+ platform: platform2
6790
+ };
6496
6791
  if (options.command) {
6497
6792
  return createPiConnection({
6498
6793
  command: options.command,
6499
6794
  args: options.args,
6500
6795
  env: options.env,
6501
6796
  cwd: options.cwd,
6502
- version: options.version,
6503
- platform: platform2
6797
+ ...shared
6504
6798
  });
6505
6799
  }
6506
- if (!options.url) {
6507
- throw new Error("connect requires either a url or a command");
6508
- }
6800
+ if (!options.url) throw new Error("connect requires either a url or a command");
6509
6801
  if (options.protocolVersion && !options.protocolVersion.startsWith("2026")) {
6510
6802
  return createPiConnection({
6511
6803
  url: options.url,
6512
6804
  headers: options.headers,
6513
- version: options.version,
6514
6805
  fetchImpl: platform2.fetch,
6515
- platform: platform2
6806
+ ...shared
6516
6807
  });
6517
6808
  }
6518
6809
  try {
@@ -6520,20 +6811,20 @@ async function connect(platform2, options) {
6520
6811
  url: options.url,
6521
6812
  headers: options.headers,
6522
6813
  fetchImpl: platform2.fetch,
6523
- clientInfo: { name: "mcpcat", version: options.version ?? "0.0.0" }
6814
+ clientInfo: { name: "mcpcat", version: options.version ?? "0.0.0" },
6815
+ timeoutMs: options.timeoutMs,
6816
+ signal: options.signal,
6817
+ verbose: options.verbose
6524
6818
  });
6525
6819
  } catch (err) {
6526
6820
  const isMethodNotFound = err instanceof StatelessRpcError && err.code === -32601 || err instanceof StatelessHttpError && err.status >= 400 && err.status < 500;
6527
- if (options.protocolVersion?.startsWith("2026")) {
6528
- throw err;
6529
- }
6821
+ if (options.protocolVersion?.startsWith("2026")) throw err;
6530
6822
  if (isMethodNotFound) {
6531
6823
  return createPiConnection({
6532
6824
  url: options.url,
6533
6825
  headers: options.headers,
6534
- version: options.version,
6535
6826
  fetchImpl: platform2.fetch,
6536
- platform: platform2
6827
+ ...shared
6537
6828
  });
6538
6829
  }
6539
6830
  throw err;
@@ -7085,6 +7376,28 @@ async function readStream(stream) {
7085
7376
  return new TextDecoder().decode(merged);
7086
7377
  }
7087
7378
 
7379
+ // src/config/expand.ts
7380
+ var PLACEHOLDER = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
7381
+ var PLACEHOLDER_TEST = /\$\{[A-Za-z_][A-Za-z0-9_]*\}/;
7382
+ function containsPlaceholders(value) {
7383
+ return PLACEHOLDER_TEST.test(value);
7384
+ }
7385
+ function expandPlaceholders(value, env2) {
7386
+ return value.replace(PLACEHOLDER, (_, name) => env2[name] ?? "");
7387
+ }
7388
+ function expandPlaceholdersStrict(value, env2) {
7389
+ let hasUndefined = false;
7390
+ const result = value.replace(PLACEHOLDER, (_, name) => {
7391
+ if (env2[name] === void 0) {
7392
+ hasUndefined = true;
7393
+ return "";
7394
+ }
7395
+ return env2[name];
7396
+ });
7397
+ if (hasUndefined) return void 0;
7398
+ return result;
7399
+ }
7400
+
7088
7401
  // src/config/parse.ts
7089
7402
  var SERVER_NAME = /^[A-Za-z0-9_-]+$/;
7090
7403
  var EXPOSURES = ["codemode", "deferred", "direct", "hidden"];
@@ -8321,23 +8634,98 @@ async function runDiscover(platform2, args) {
8321
8634
  return 0;
8322
8635
  }
8323
8636
 
8324
- // src/config/expand.ts
8325
- var PLACEHOLDER = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
8326
- var PLACEHOLDER_TEST = /\$\{[A-Za-z_][A-Za-z0-9_]*\}/;
8327
- function containsPlaceholders(value) {
8328
- return PLACEHOLDER_TEST.test(value);
8637
+ // src/render/result.ts
8638
+ function formatBytes(n) {
8639
+ if (n < 1024) return `${n} bytes`;
8640
+ const kb = n / 1024;
8641
+ if (kb < 1024) return `${kb.toFixed(1)} KB`;
8642
+ return `${(kb / 1024).toFixed(1)} MB`;
8643
+ }
8644
+ function truncateMiddle(text, max) {
8645
+ if (text.length <= max) return text;
8646
+ const half = Math.floor((max - 5) / 2);
8647
+ return `${text.slice(0, half)}
8648
+ [\u2026]
8649
+ ${text.slice(-half)}`;
8650
+ }
8651
+ function renderContentBlock(block) {
8652
+ if (block.type === "text") return block.text;
8653
+ if (block.type === "image") {
8654
+ const size = formatBytes(Math.ceil(block.data.length * 3 / 4));
8655
+ return `[image: ${block.mimeType}, ${size}]`;
8656
+ }
8657
+ if (block.type === "audio") {
8658
+ const audioBlock = block;
8659
+ const size = formatBytes(Math.ceil(audioBlock.data.length * 3 / 4));
8660
+ return `[audio: ${audioBlock.mimeType}, ${size}]`;
8661
+ }
8662
+ if (block.type === "resource_link") return `[resource: ${block.uri}]`;
8663
+ if (block.type === "resource") {
8664
+ return "text" in block.resource ? block.resource.text : `[blob: ${block.resource.uri}]`;
8665
+ }
8666
+ return void 0;
8329
8667
  }
8330
- function expandPlaceholdersStrict(value, env2) {
8331
- let hasUndefined = false;
8332
- const result = value.replace(PLACEHOLDER, (_, name) => {
8333
- if (env2[name] === void 0) {
8334
- hasUndefined = true;
8335
- return "";
8668
+ function renderCallResultText(result, options) {
8669
+ const parts = [];
8670
+ if (result.content) {
8671
+ for (const block of result.content) {
8672
+ const rendered = renderContentBlock(block);
8673
+ if (rendered !== void 0) parts.push(rendered);
8336
8674
  }
8337
- return env2[name];
8338
- });
8339
- if (hasUndefined) return void 0;
8340
- return result;
8675
+ }
8676
+ if (parts.length === 0 && result.structuredContent !== void 0) {
8677
+ parts.push(JSON.stringify(result.structuredContent, null, 2));
8678
+ }
8679
+ let text = parts.join("\n");
8680
+ if (options?.maxChars && text.length > options.maxChars) {
8681
+ text = truncateMiddle(text, options.maxChars);
8682
+ }
8683
+ return text;
8684
+ }
8685
+ function renderCallResultJson(result) {
8686
+ return JSON.stringify(result, null, 2);
8687
+ }
8688
+ async function renderCallResult(platform2, result, options) {
8689
+ if (options?.json) {
8690
+ const json = renderCallResultJson(result);
8691
+ if (result.isError) {
8692
+ await platform2.write(
8693
+ 2,
8694
+ `${JSON.stringify({ error: { kind: "tool_error", message: json } }, null, 2)}
8695
+ `
8696
+ );
8697
+ return 1;
8698
+ }
8699
+ await platform2.write(1, `${json}
8700
+ `);
8701
+ return 0;
8702
+ }
8703
+ const text = renderCallResultText(result, options);
8704
+ if (result.isError) {
8705
+ await platform2.write(2, `${text || "(tool reported error)"}
8706
+ `);
8707
+ return 1;
8708
+ }
8709
+ if (text) {
8710
+ await platform2.write(1, `${text}
8711
+ `);
8712
+ }
8713
+ return 0;
8714
+ }
8715
+ async function renderToolsJson(platform2, tools) {
8716
+ await platform2.write(1, `${JSON.stringify({ tools }, null, 2)}
8717
+ `);
8718
+ }
8719
+ async function renderInfoJson(platform2, info) {
8720
+ await platform2.write(1, `${JSON.stringify(info, null, 2)}
8721
+ `);
8722
+ }
8723
+ async function renderErrorJson(platform2, kind, message, code, data) {
8724
+ const errorObj = { kind, message };
8725
+ if (code !== void 0) errorObj.code = code;
8726
+ if (data !== void 0) errorObj.data = data;
8727
+ await platform2.write(2, `${JSON.stringify({ error: errorObj }, null, 2)}
8728
+ `);
8341
8729
  }
8342
8730
 
8343
8731
  // src/serve/catalog.ts
@@ -11006,6 +11394,91 @@ bridge does not survive a reload; runs in the foreground (start with &)
11006
11394
  return runServe(platform2, serveOptions, version);
11007
11395
  }
11008
11396
 
11397
+ // src/cli/exit-codes.ts
11398
+ var EXIT_OK = 0;
11399
+ var EXIT_TOOL_ERROR = 1;
11400
+ var EXIT_USAGE = 2;
11401
+ var EXIT_CONNECTION = 3;
11402
+ var EXIT_AUTH = 4;
11403
+ var EXIT_RPC_ERROR = 5;
11404
+ var EXIT_INPUT_REQUIRED = 6;
11405
+ var EXIT_TIMEOUT = 124;
11406
+ var EXIT_INTERRUPTED = 130;
11407
+ var McpcatError = class extends Error {
11408
+ exitCode;
11409
+ constructor(exitCode, message) {
11410
+ super(message);
11411
+ this.name = "McpcatError";
11412
+ this.exitCode = exitCode;
11413
+ }
11414
+ };
11415
+
11416
+ // src/cli/verbose.ts
11417
+ var SENSITIVE_HEADER_RE = /^(authorization|cookie|proxy-authorization)$/i;
11418
+ var SENSITIVE_NAME_RE = /token|key|secret|password|session/i;
11419
+ function isSensitiveHeader(name) {
11420
+ return SENSITIVE_HEADER_RE.test(name) || SENSITIVE_NAME_RE.test(name);
11421
+ }
11422
+ function redact(text, secrets) {
11423
+ let result = text;
11424
+ for (const secret of secrets) {
11425
+ if (secret.length > 0) {
11426
+ while (result.includes(secret)) {
11427
+ result = result.replace(secret, "[REDACTED]");
11428
+ }
11429
+ }
11430
+ }
11431
+ return result;
11432
+ }
11433
+ function createVerboseLogger(platform2, enabled) {
11434
+ const secrets = /* @__PURE__ */ new Set();
11435
+ function write(line) {
11436
+ const safe = redact(line, secrets);
11437
+ platform2.write(2, `${safe}
11438
+ `).catch(() => {
11439
+ });
11440
+ }
11441
+ return {
11442
+ enabled,
11443
+ secrets,
11444
+ request(method, body) {
11445
+ if (!enabled) return;
11446
+ write(`> ${method}`);
11447
+ for (const line of body.split("\n")) {
11448
+ write(`> ${line}`);
11449
+ }
11450
+ },
11451
+ response(body) {
11452
+ if (!enabled) return;
11453
+ for (const line of body.split("\n")) {
11454
+ write(`< ${line}`);
11455
+ }
11456
+ },
11457
+ http(direction, line) {
11458
+ if (!enabled) return;
11459
+ write(`${direction} ${line}`);
11460
+ },
11461
+ server(line) {
11462
+ if (!enabled) return;
11463
+ write(`[server] ${line}`);
11464
+ },
11465
+ headerOut(name, value) {
11466
+ if (!enabled) return;
11467
+ const display = isSensitiveHeader(name) ? "[REDACTED]" : redact(value, secrets);
11468
+ write(`> ${name}: ${display}`);
11469
+ },
11470
+ headerIn(name, value) {
11471
+ if (!enabled) return;
11472
+ const display = isSensitiveHeader(name) ? "[REDACTED]" : redact(value, secrets);
11473
+ write(`< ${name}: ${display}`);
11474
+ },
11475
+ progress(message) {
11476
+ if (!enabled) return;
11477
+ write(`* ${message}`);
11478
+ }
11479
+ };
11480
+ }
11481
+
11009
11482
  // src/cli/main.ts
11010
11483
  var VERBS = [
11011
11484
  "info",
@@ -11123,6 +11596,8 @@ function parseFlags(args) {
11123
11596
  const serverTargets = [];
11124
11597
  let allowShell = false;
11125
11598
  let timeoutMs;
11599
+ let verbose = false;
11600
+ let silent = false;
11126
11601
  const positional = [];
11127
11602
  let trailingCommand;
11128
11603
  for (let i = 0; i < args.length; i++) {
@@ -11159,6 +11634,10 @@ function parseFlags(args) {
11159
11634
  serverTargets.push(args[++i]);
11160
11635
  } else if (arg === "--allow-shell") {
11161
11636
  allowShell = true;
11637
+ } else if (arg === "-v" || arg === "--verbose") {
11638
+ verbose = true;
11639
+ } else if (arg === "--silent") {
11640
+ silent = true;
11162
11641
  } else if ((arg === "-m" || arg === "--timeout") && args[i + 1]) {
11163
11642
  timeoutMs = Number(args[++i]) * 1e3;
11164
11643
  } else if (!arg.startsWith("-")) {
@@ -11179,6 +11658,8 @@ function parseFlags(args) {
11179
11658
  serverTargets,
11180
11659
  allowShell,
11181
11660
  timeoutMs,
11661
+ verbose,
11662
+ silent,
11182
11663
  positional,
11183
11664
  trailingCommand
11184
11665
  };
@@ -11227,7 +11708,9 @@ function parseArgs(args) {
11227
11708
  inlineSource: flags.inlineSource,
11228
11709
  serverTargets: flags.serverTargets,
11229
11710
  allowShell: flags.allowShell,
11230
- timeoutMs: flags.timeoutMs
11711
+ timeoutMs: flags.timeoutMs,
11712
+ verbose: flags.verbose,
11713
+ silent: flags.silent
11231
11714
  };
11232
11715
  }
11233
11716
  function formatToolLine(tool) {
@@ -11251,26 +11734,6 @@ function formatToolSchema(tool) {
11251
11734
  }
11252
11735
  return lines.join("\n");
11253
11736
  }
11254
- function renderCallResult(result) {
11255
- const parts = [];
11256
- if (result.content) {
11257
- for (const block of result.content) {
11258
- if (block.type === "text") parts.push(block.text);
11259
- else if (block.type === "image")
11260
- parts.push(`[image: ${block.mimeType}, ${Math.ceil(block.data.length * 3 / 4)} bytes]`);
11261
- else if (block.type === "resource_link") parts.push(`[resource: ${block.uri}]`);
11262
- else if (block.type === "resource") {
11263
- parts.push(
11264
- "text" in block.resource ? block.resource.text : `[blob: ${block.resource.uri}]`
11265
- );
11266
- }
11267
- }
11268
- }
11269
- if (parts.length === 0 && result.structuredContent !== void 0) {
11270
- parts.push(JSON.stringify(result.structuredContent, null, 2));
11271
- }
11272
- return parts.join("\n");
11273
- }
11274
11737
  var NETWORK_HINT = "connection failed (use slicc-node, slicc-swift, or the browser extension for servers without CORS)";
11275
11738
  function transportHint(err) {
11276
11739
  if (err instanceof TypeError) {
@@ -11313,8 +11776,12 @@ async function readDataArg(platform2, dataArg) {
11313
11776
  }
11314
11777
  return JSON.parse(raw);
11315
11778
  }
11316
- async function runInfo(platform2, conn) {
11779
+ async function runInfo(platform2, conn, json) {
11317
11780
  const info = await conn.info();
11781
+ if (json) {
11782
+ await renderInfoJson(platform2, info);
11783
+ return EXIT_OK;
11784
+ }
11318
11785
  const lines = [];
11319
11786
  if (info.name) lines.push(`Server: ${info.name}${info.version ? ` ${info.version}` : ""}`);
11320
11787
  if (info.title) lines.push(`Title: ${info.title}`);
@@ -11331,48 +11798,66 @@ async function runInfo(platform2, conn) {
11331
11798
  }
11332
11799
  await platform2.write(1, `${lines.join("\n")}
11333
11800
  `);
11334
- return 0;
11801
+ return EXIT_OK;
11335
11802
  }
11336
- async function runTools(platform2, conn) {
11803
+ async function runTools(platform2, conn, json) {
11337
11804
  const tools = await conn.listTools();
11338
- if (tools.length === 0) {
11339
- await platform2.write(1, "(no tools)\n");
11340
- } else {
11341
- await platform2.write(1, `${tools.map(formatToolLine).join("\n")}
11342
- `);
11805
+ if (json) {
11806
+ await renderToolsJson(platform2, tools);
11807
+ return EXIT_OK;
11343
11808
  }
11344
- return 0;
11809
+ await platform2.write(
11810
+ 1,
11811
+ tools.length === 0 ? "(no tools)\n" : `${tools.map(formatToolLine).join("\n")}
11812
+ `
11813
+ );
11814
+ return EXIT_OK;
11345
11815
  }
11346
- async function runCall(platform2, conn, toolName, dataArg) {
11347
- let toolArgs = {};
11816
+ async function runCall(platform2, conn, toolName, dataArg, toolArgs, opts) {
11817
+ let base = {};
11348
11818
  if (dataArg) {
11349
11819
  try {
11350
- toolArgs = await readDataArg(platform2, dataArg);
11820
+ base = await readDataArg(platform2, dataArg);
11351
11821
  } catch (err) {
11352
- await platform2.write(2, `mcpcat: invalid -d argument: ${err.message}
11353
- `);
11354
- return 2;
11822
+ await writeErr(
11823
+ platform2,
11824
+ opts.json ?? false,
11825
+ "usage",
11826
+ `invalid -d argument: ${err.message}`
11827
+ );
11828
+ return EXIT_USAGE;
11355
11829
  }
11356
11830
  }
11357
- const result = await conn.callTool(toolName, toolArgs);
11358
- const output = renderCallResult(result);
11359
- if (result.isError) {
11360
- await platform2.write(2, `${output}
11361
- `);
11362
- return 1;
11831
+ let fin = base;
11832
+ if (toolArgs.length > 0) {
11833
+ const tools = await conn.listTools();
11834
+ const tool = tools.find((t) => t.name === toolName);
11835
+ const schema = tool?.inputSchema ?? { type: "object", properties: {} };
11836
+ const c = coerceArgsBySchema(toolArgs, schema, { noValidate: opts.noValidate });
11837
+ if (!c.ok) {
11838
+ await writeErr(platform2, opts.json ?? false, "usage", c.error);
11839
+ return EXIT_USAGE;
11840
+ }
11841
+ fin = mergeArgs(base, c.value);
11363
11842
  }
11364
- await platform2.write(1, `${output}
11365
- `);
11366
- return 0;
11843
+ return renderCallResult(
11844
+ platform2,
11845
+ await conn.callTool(toolName, fin, { onProgress: opts.onProgress }),
11846
+ { json: opts.json }
11847
+ );
11367
11848
  }
11368
- async function runDescribe(platform2, conn, toolName, showSchema) {
11849
+ async function runDescribe(platform2, conn, toolName, showSchema, json) {
11369
11850
  const tools = await conn.listTools();
11370
11851
  if (toolName) {
11371
11852
  const tool = tools.find((t) => t.name === toolName);
11372
11853
  if (!tool) {
11373
- await platform2.write(2, `mcpcat: tool '${toolName}' not found
11854
+ await writeErr(platform2, json, "usage", `tool '${toolName}' not found`);
11855
+ return EXIT_USAGE;
11856
+ }
11857
+ if (json) {
11858
+ await platform2.write(1, `${JSON.stringify(tool, null, 2)}
11374
11859
  `);
11375
- return 2;
11860
+ return EXIT_OK;
11376
11861
  }
11377
11862
  if (showSchema && tool.inputSchema) {
11378
11863
  await platform2.write(1, `${JSON.stringify(tool.inputSchema, null, 2)}
@@ -11382,28 +11867,36 @@ async function runDescribe(platform2, conn, toolName, showSchema) {
11382
11867
  `);
11383
11868
  }
11384
11869
  } else {
11870
+ if (json) {
11871
+ await platform2.write(1, `${JSON.stringify({ tools }, null, 2)}
11872
+ `);
11873
+ return EXIT_OK;
11874
+ }
11385
11875
  for (const tool of tools) {
11386
11876
  await platform2.write(1, `${formatToolSchema(tool)}
11387
11877
 
11388
11878
  `);
11389
11879
  }
11390
11880
  }
11391
- return 0;
11881
+ return EXIT_OK;
11392
11882
  }
11393
- async function runVerb(platform2, parsed, conn) {
11883
+ async function runVerb(platform2, parsed, conn, onProgress) {
11394
11884
  switch (parsed.verb) {
11395
11885
  case "info":
11396
- return runInfo(platform2, conn);
11886
+ return runInfo(platform2, conn, parsed.jsonOutput);
11397
11887
  case "tools":
11398
- return runTools(platform2, conn);
11888
+ return runTools(platform2, conn, parsed.jsonOutput);
11399
11889
  case "call":
11400
- return runCall(platform2, conn, parsed.toolName, parsed.dataArg);
11890
+ return runCall(platform2, conn, parsed.toolName, parsed.dataArg, [], {
11891
+ json: parsed.jsonOutput,
11892
+ onProgress
11893
+ });
11401
11894
  case "describe":
11402
- return runDescribe(platform2, conn, parsed.toolName, parsed.showSchema);
11895
+ return runDescribe(platform2, conn, parsed.toolName, parsed.showSchema, parsed.jsonOutput);
11403
11896
  default:
11404
11897
  await platform2.write(2, `mcpcat: unknown command '${parsed.verb}'
11405
11898
  `);
11406
- return 2;
11899
+ return EXIT_USAGE;
11407
11900
  }
11408
11901
  }
11409
11902
  async function probeServer(_name, config, platform2) {
@@ -11652,7 +12145,7 @@ async function run(platform2, version, args) {
11652
12145
  if (UNIMPLEMENTED_VERBS.has(parsed.verb)) {
11653
12146
  await platform2.write(2, `mcpcat: '${parsed.verb}' is not implemented yet
11654
12147
  `);
11655
- return 1;
12148
+ return EXIT_TOOL_ERROR;
11656
12149
  }
11657
12150
  if (parsed.verb === "serve") {
11658
12151
  return dispatchServeVerb(platform2, args, version);
@@ -11674,44 +12167,100 @@ async function run(platform2, version, args) {
11674
12167
  `);
11675
12168
  return 2;
11676
12169
  }
12170
+ return runClientVerb(platform2, version, parsed);
12171
+ }
12172
+ function expandAndCollect(headers, env2) {
12173
+ const secrets = [];
12174
+ const result = {};
12175
+ for (const [k, v] of Object.entries(headers)) {
12176
+ const e = expandPlaceholders(v, env2);
12177
+ result[k] = e;
12178
+ if (e !== v) secrets.push(e);
12179
+ if (v.length > 0) secrets.push(v);
12180
+ }
12181
+ return { expanded: result, secrets };
12182
+ }
12183
+ function classifyError(err) {
12184
+ if (err instanceof InputRequiredError) {
12185
+ return { code: EXIT_INPUT_REQUIRED, kind: "input_required", msg: err.requestJson };
12186
+ }
12187
+ if (err instanceof McpTimeoutError2) {
12188
+ return { code: EXIT_TIMEOUT, kind: "timeout", msg: err.message };
12189
+ }
12190
+ if (err instanceof McpAuthError) {
12191
+ return { code: EXIT_AUTH, kind: "auth", msg: err.message };
12192
+ }
12193
+ if (err instanceof McpcatError) {
12194
+ return { code: err.exitCode, kind: "error", msg: err.message };
12195
+ }
12196
+ const msg = err.message ?? String(err);
12197
+ const name = err.name ?? "";
12198
+ if (name === "McpTimeoutError" || msg.includes("timed out") || msg.includes("timeout") || msg.includes("Timeout")) {
12199
+ return { code: EXIT_TIMEOUT, kind: "timeout", msg };
12200
+ }
12201
+ return { code: EXIT_RPC_ERROR, kind: "rpc_error", msg };
12202
+ }
12203
+ async function runClientVerb(platform2, version, parsed) {
12204
+ const { expanded, secrets } = expandAndCollect(parsed.headers, platform2.env);
12205
+ const verbose = createVerboseLogger(platform2, parsed.verbose);
12206
+ for (const s of secrets) verbose.secrets.add(s);
12207
+ const timeoutMs = parsed.timeoutMs;
12208
+ const sigintController = new AbortController();
12209
+ let interrupted = false;
12210
+ platform2.onSignal?.("SIGINT", () => {
12211
+ interrupted = true;
12212
+ sigintController.abort();
12213
+ });
12214
+ const showProgress = !parsed.silent && (parsed.verbose || platform2.isatty(2));
12215
+ const onProgress = (progress, total, message) => {
12216
+ if (!showProgress) return;
12217
+ const pct = total ? ` ${Math.round(progress / total * 100)}%` : "";
12218
+ const msg = message ? ` ${message}` : "";
12219
+ platform2.write(2, `* progress:${pct}${msg}
12220
+ `).catch(() => {
12221
+ });
12222
+ };
11677
12223
  const config = await MergedConfigSource.load(platform2);
12224
+ const target = parsed.target === "__trailing__" ? "" : parsed.target;
11678
12225
  let resolved;
11679
12226
  try {
11680
- resolved = await resolveTarget(
11681
- parsed.target === "__trailing__" ? "" : parsed.target,
11682
- parsed.trailingCommand,
11683
- config
11684
- );
12227
+ resolved = await resolveTarget(target, parsed.trailingCommand, config);
11685
12228
  } catch (err) {
11686
- await platform2.write(2, `mcpcat: ${err.message}
11687
- `);
11688
- return 2;
12229
+ await writeErr(platform2, parsed.jsonOutput, "usage", err.message);
12230
+ return EXIT_USAGE;
11689
12231
  }
11690
12232
  let conn;
11691
12233
  try {
11692
12234
  conn = await connect(platform2, {
11693
12235
  ...resolved,
11694
- headers: { ...resolved.headers, ...parsed.headers },
12236
+ headers: { ...resolved.headers, ...expanded },
11695
12237
  protocolVersion: parsed.protocolVersion,
11696
- version
12238
+ version,
12239
+ timeoutMs,
12240
+ signal: sigintController.signal,
12241
+ verbose
11697
12242
  });
11698
12243
  } catch (err) {
12244
+ if (err instanceof McpAuthError) {
12245
+ await writeErr(platform2, parsed.jsonOutput, "auth", err.message);
12246
+ return EXIT_AUTH;
12247
+ }
11699
12248
  const msg = transportHint(err) ?? `connection failed: ${err.message}`;
11700
- await platform2.write(2, `mcpcat: ${msg}
11701
- `);
11702
- return 3;
12249
+ await writeErr(platform2, parsed.jsonOutput, "connection", msg);
12250
+ return EXIT_CONNECTION;
11703
12251
  }
11704
12252
  try {
11705
- return await runVerb(platform2, parsed, conn);
12253
+ return await runVerb(platform2, parsed, conn, onProgress);
11706
12254
  } catch (err) {
11707
- if (err instanceof InputRequiredError) {
11708
- await platform2.write(2, `${err.requestJson}
11709
- `);
11710
- return 6;
12255
+ if (interrupted) {
12256
+ await platform2.write(2, "mcpcat: interrupted\n");
12257
+ return EXIT_INTERRUPTED;
11711
12258
  }
11712
- await platform2.write(2, `mcpcat: ${err.message}
12259
+ const e = classifyError(err);
12260
+ if (err instanceof InputRequiredError) await platform2.write(2, `${e.msg}
11713
12261
  `);
11714
- return 5;
12262
+ else await writeErr(platform2, parsed.jsonOutput, e.kind, e.msg);
12263
+ return e.code;
11715
12264
  } finally {
11716
12265
  try {
11717
12266
  await conn.close();
@@ -11719,6 +12268,11 @@ async function run(platform2, version, args) {
11719
12268
  }
11720
12269
  }
11721
12270
  }
12271
+ async function writeErr(p, json, kind, msg) {
12272
+ if (json) await renderErrorJson(p, kind, msg);
12273
+ else await p.write(2, `mcpcat: ${msg}
12274
+ `);
12275
+ }
11722
12276
 
11723
12277
  // src/platform/kernel.ts
11724
12278
  function kernelFs(ctx) {
@@ -11835,7 +12389,7 @@ async function main(ctx) {
11835
12389
  packageDir: "/node_modules/@ai-ecoverse/mcpcat"
11836
12390
  });
11837
12391
  const args = ctx.argv.slice(1);
11838
- return run(platform2, "0.0.0", args);
12392
+ return run(platform2, "0.9.0", args);
11839
12393
  }
11840
12394
  export {
11841
12395
  main as default