@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.
@@ -5298,6 +5298,197 @@ var init_portable = __esm({
5298
5298
  }
5299
5299
  });
5300
5300
 
5301
+ // src/args/coerce.ts
5302
+ function asSchemaObject(schema) {
5303
+ return schema ?? {};
5304
+ }
5305
+ function emptyArgObject() {
5306
+ return /* @__PURE__ */ Object.create(null);
5307
+ }
5308
+ function isUnsafeObjectKey(key) {
5309
+ return key === "__proto__" || key === "prototype" || key === "constructor";
5310
+ }
5311
+ function isPlainObject(value) {
5312
+ return typeof value === "object" && value !== null && !Array.isArray(value) && value !== Object.prototype;
5313
+ }
5314
+ function splitFlag(a) {
5315
+ const eq = a.indexOf("=");
5316
+ if (eq > 2) return { key: a.slice(2, eq), inlineValue: a.slice(eq + 1) };
5317
+ return { key: a.slice(2), inlineValue: void 0 };
5318
+ }
5319
+ function coerceScalar(raw, type) {
5320
+ switch (type) {
5321
+ case "integer": {
5322
+ if (!/^-?\d+$/.test(raw)) return { ok: false, error: `expected integer, got "${raw}"` };
5323
+ return { ok: true, value: Number(raw) };
5324
+ }
5325
+ case "number": {
5326
+ const n = Number(raw);
5327
+ if (!Number.isFinite(n)) return { ok: false, error: `expected number, got "${raw}"` };
5328
+ return { ok: true, value: n };
5329
+ }
5330
+ case "boolean": {
5331
+ if (raw === "true" || raw === "1" || raw === "yes") return { ok: true, value: true };
5332
+ if (raw === "false" || raw === "0" || raw === "no") return { ok: true, value: false };
5333
+ return { ok: false, error: `expected boolean, got "${raw}"` };
5334
+ }
5335
+ case "object":
5336
+ return coerceJsonObject(raw);
5337
+ default:
5338
+ return { ok: true, value: raw };
5339
+ }
5340
+ }
5341
+ function coerceJsonObject(raw) {
5342
+ let parsed;
5343
+ try {
5344
+ parsed = JSON.parse(raw);
5345
+ } catch {
5346
+ return { ok: false, error: `expected object JSON, got "${raw}"` };
5347
+ }
5348
+ if (!isPlainObject(parsed)) {
5349
+ return { ok: false, error: `expected object JSON, got "${raw}"` };
5350
+ }
5351
+ return { ok: true, value: parsed };
5352
+ }
5353
+ function resolveToolFlag(key, properties) {
5354
+ if (Object.hasOwn(properties, key)) {
5355
+ return { ok: true, rootKey: key, dottedPath: null, meta: properties[key] ?? {} };
5356
+ }
5357
+ const dot = key.indexOf(".");
5358
+ if (dot <= 0) return { ok: false, error: `unknown flag: --${key}` };
5359
+ const rootKey = key.slice(0, dot);
5360
+ const rest = key.slice(dot + 1);
5361
+ if (!Object.hasOwn(properties, rootKey) || rest.length === 0) {
5362
+ return { ok: false, error: `unknown flag: --${key}` };
5363
+ }
5364
+ const parts = rest.split(".");
5365
+ if (isUnsafeObjectKey(rootKey) || parts.some((p) => p.length === 0 || isUnsafeObjectKey(p))) {
5366
+ return { ok: false, error: `unknown flag: --${key}` };
5367
+ }
5368
+ return { ok: true, rootKey, dottedPath: parts, meta: properties[rootKey] ?? {} };
5369
+ }
5370
+ function readFlagRaw(args2, i, key, inlineValue, type, dottedPath) {
5371
+ if (inlineValue !== void 0) {
5372
+ return { ok: true, raw: inlineValue, nextIndex: i + 1, bareBoolean: false };
5373
+ }
5374
+ const next = args2[i + 1];
5375
+ if (!dottedPath && type === "boolean" && (next === void 0 || next.startsWith("--"))) {
5376
+ return { ok: true, raw: "", nextIndex: i + 1, bareBoolean: true };
5377
+ }
5378
+ if (next === void 0) {
5379
+ return { ok: false, error: `flag --${key} requires a value` };
5380
+ }
5381
+ return { ok: true, raw: next, nextIndex: i + 2, bareBoolean: false };
5382
+ }
5383
+ function setDotted(out, rootKey, path, value) {
5384
+ if (isUnsafeObjectKey(rootKey) || path.some((p) => p.length === 0 || isUnsafeObjectKey(p))) {
5385
+ return { ok: false, error: `unknown flag: --${rootKey}.${path.join(".")}` };
5386
+ }
5387
+ const existing = Object.hasOwn(out, rootKey) ? out[rootKey] : void 0;
5388
+ if (existing === void 0) {
5389
+ out[rootKey] = emptyArgObject();
5390
+ } else if (!isPlainObject(existing)) {
5391
+ return { ok: false, error: `--${rootKey}: cannot nest into a ${typeof existing} value` };
5392
+ }
5393
+ let cursor = out[rootKey];
5394
+ for (let i = 0; i < path.length - 1; i++) {
5395
+ const seg = path[i];
5396
+ const next = Object.hasOwn(cursor, seg) ? cursor[seg] : void 0;
5397
+ if (next === void 0) {
5398
+ cursor[seg] = emptyArgObject();
5399
+ } else if (!isPlainObject(next)) {
5400
+ return {
5401
+ ok: false,
5402
+ error: `--${rootKey}.${path.slice(0, i + 1).join(".")}: cannot nest into a ${typeof next} value`
5403
+ };
5404
+ }
5405
+ cursor = cursor[seg];
5406
+ }
5407
+ cursor[path[path.length - 1]] = value;
5408
+ return { ok: true };
5409
+ }
5410
+ function assignFlagValue(out, rootKey, dottedPath, value, isArray) {
5411
+ if (dottedPath) return setDotted(out, rootKey, dottedPath, value);
5412
+ if (isArray) {
5413
+ const prev = out[rootKey];
5414
+ if (Array.isArray(prev)) prev.push(value);
5415
+ else out[rootKey] = [value];
5416
+ return { ok: true };
5417
+ }
5418
+ const existing = out[rootKey];
5419
+ if (isPlainObject(existing) && isPlainObject(value)) {
5420
+ Object.assign(existing, value);
5421
+ return { ok: true };
5422
+ }
5423
+ out[rootKey] = value;
5424
+ return { ok: true };
5425
+ }
5426
+ function parseOneFlag(args2, i, properties, out) {
5427
+ const a = args2[i];
5428
+ if (a === "--") return { ok: true, nextIndex: args2.length };
5429
+ if (a.startsWith("--no-") && a.length > 5) {
5430
+ const key2 = a.slice(5);
5431
+ if (Object.hasOwn(properties, key2) && properties[key2]?.type === "boolean") {
5432
+ out[key2] = false;
5433
+ return { ok: true, nextIndex: i + 1 };
5434
+ }
5435
+ return { ok: false, error: `unknown flag: ${a}` };
5436
+ }
5437
+ if (!a.startsWith("--")) {
5438
+ return { ok: false, error: `unexpected positional argument "${a}"` };
5439
+ }
5440
+ const { key, inlineValue } = splitFlag(a);
5441
+ const resolved = resolveToolFlag(key, properties);
5442
+ if (!resolved.ok) return resolved;
5443
+ const { rootKey, dottedPath, meta } = resolved;
5444
+ const declaredType = meta.type;
5445
+ const type = declaredType ?? "string";
5446
+ const isArray = type === "array";
5447
+ const itemType = (isArray ? meta.items?.type : void 0) ?? "string";
5448
+ if (dottedPath && declaredType !== void 0 && declaredType !== "object") {
5449
+ return { ok: false, error: `unknown flag: --${key}` };
5450
+ }
5451
+ const taken = readFlagRaw(args2, i, key, inlineValue, type, dottedPath);
5452
+ if (!taken.ok) return taken;
5453
+ if (taken.bareBoolean) {
5454
+ out[rootKey] = true;
5455
+ return { ok: true, nextIndex: taken.nextIndex };
5456
+ }
5457
+ const valueType = dottedPath ? "string" : isArray ? itemType : type;
5458
+ const coerced = coerceScalar(taken.raw, valueType);
5459
+ if (!coerced.ok) return { ok: false, error: `--${key}: ${coerced.error}` };
5460
+ const assigned = assignFlagValue(out, rootKey, dottedPath, coerced.value, isArray);
5461
+ if (!assigned.ok) return assigned;
5462
+ return { ok: true, nextIndex: taken.nextIndex };
5463
+ }
5464
+ function coerceArgsBySchema(args2, schema, options) {
5465
+ const s = asSchemaObject(schema);
5466
+ const properties = s.properties ?? {};
5467
+ const required = new Set(Array.isArray(s.required) ? s.required : []);
5468
+ const out = emptyArgObject();
5469
+ let i = 0;
5470
+ while (i < args2.length) {
5471
+ const result = parseOneFlag(args2, i, properties, out);
5472
+ if (!result.ok) return result;
5473
+ i = result.nextIndex;
5474
+ }
5475
+ if (!options?.noValidate) {
5476
+ for (const r of required) {
5477
+ if (!Object.hasOwn(out, r)) {
5478
+ return { ok: false, error: `missing required flag --${r}` };
5479
+ }
5480
+ }
5481
+ }
5482
+ return { ok: true, value: out };
5483
+ }
5484
+ function mergeArgs(base, flags) {
5485
+ const result = { ...base };
5486
+ for (const [k, v] of Object.entries(flags)) {
5487
+ result[k] = v;
5488
+ }
5489
+ return result;
5490
+ }
5491
+
5301
5492
  // node_modules/@earendil-works/pi-mcp/dist/protocol/jsonrpc.js
5302
5493
  var JSON_RPC_ERROR_CODES = {
5303
5494
  parseError: -32700,
@@ -6651,67 +6842,6 @@ var PlatformStdioTransport = class {
6651
6842
  }
6652
6843
  };
6653
6844
 
6654
- // src/client/pi-connection.ts
6655
- async function createPiConnection(options) {
6656
- const clientOptions = {
6657
- name: "mcpcat",
6658
- version: options.version ?? "0.0.0"
6659
- };
6660
- const client = new McpClient(clientOptions);
6661
- let transport;
6662
- if (options.url) {
6663
- transport = new StreamableHttpTransport({
6664
- url: options.url,
6665
- headers: options.headers,
6666
- fetch: options.fetchImpl
6667
- });
6668
- } else if (options.command) {
6669
- if (options.platform) {
6670
- transport = new PlatformStdioTransport({
6671
- command: options.command,
6672
- args: options.args,
6673
- env: options.env,
6674
- cwd: options.cwd,
6675
- inheritEnv: true,
6676
- platform: options.platform
6677
- });
6678
- } else {
6679
- transport = new StdioTransport({
6680
- command: options.command,
6681
- args: options.args,
6682
- env: options.env,
6683
- cwd: options.cwd,
6684
- inheritEnv: true,
6685
- stderr: "pipe"
6686
- });
6687
- }
6688
- } else {
6689
- throw new Error("PiConnection requires either a url or a command");
6690
- }
6691
- const initResult = await client.connect(transport);
6692
- return {
6693
- async info() {
6694
- return {
6695
- name: initResult.serverInfo?.name,
6696
- version: initResult.serverInfo?.version,
6697
- title: initResult.serverInfo?.title,
6698
- protocolVersion: initResult.protocolVersion,
6699
- capabilities: initResult.capabilities,
6700
- instructions: initResult.instructions
6701
- };
6702
- },
6703
- async listTools() {
6704
- return client.listTools();
6705
- },
6706
- async callTool(name, args2) {
6707
- return client.callTool(name, args2);
6708
- },
6709
- async close() {
6710
- await client.close();
6711
- }
6712
- };
6713
- }
6714
-
6715
6845
  // src/client/stateless-connection.ts
6716
6846
  var STATELESS_PROTOCOL_VERSION = "2026-07-28";
6717
6847
  var StatelessRpcError = class extends Error {
@@ -6740,6 +6870,22 @@ var InputRequiredError = class extends Error {
6740
6870
  this.requestJson = requestJson;
6741
6871
  }
6742
6872
  };
6873
+ var McpTimeoutError2 = class extends Error {
6874
+ constructor(method, ms) {
6875
+ super(`timeout after ${ms}ms waiting for ${method}`);
6876
+ this.name = "McpTimeoutError";
6877
+ }
6878
+ };
6879
+ var McpAuthError = class extends Error {
6880
+ status;
6881
+ target;
6882
+ constructor(status, target) {
6883
+ super(`HTTP ${status}: authentication required. Run: mcpcat login ${target}`);
6884
+ this.name = "McpAuthError";
6885
+ this.status = status;
6886
+ this.target = target;
6887
+ }
6888
+ };
6743
6889
  function selectSseFrame(body, targetId) {
6744
6890
  const text = new TextDecoder().decode(body);
6745
6891
  const blocks = text.replace(/\r\n/g, "\n").split(/\n\n/);
@@ -6770,25 +6916,66 @@ async function createStatelessConnection(options) {
6770
6916
  headers: extraHeaders,
6771
6917
  fetchImpl,
6772
6918
  timeoutMs = 6e4,
6773
- clientInfo = { name: "mcpcat", version: "0.0.0" }
6919
+ clientInfo = { name: "mcpcat", version: "0.0.0" },
6920
+ signal: externalSignal,
6921
+ verbose
6774
6922
  } = options;
6775
6923
  let nextId2 = 1;
6776
6924
  let negotiatedVersion;
6777
- async function request(method, params) {
6778
- const id = nextId2++;
6779
- const headers = {
6925
+ function buildHeaders(method, params) {
6926
+ const h = {
6780
6927
  "Content-Type": "application/json",
6781
6928
  Accept: "application/json, text/event-stream",
6782
6929
  "Mcp-Method": method,
6783
6930
  ...extraHeaders
6784
6931
  };
6785
- const mcpName = mcpNameForMethod(method, params);
6786
- if (mcpName) {
6787
- headers["Mcp-Name"] = mcpName;
6932
+ const n = mcpNameForMethod(method, params);
6933
+ if (n) h["Mcp-Name"] = n;
6934
+ if (negotiatedVersion) h["Mcp-Protocol-Version"] = negotiatedVersion;
6935
+ return h;
6936
+ }
6937
+ function makeAbort() {
6938
+ const controller = new AbortController();
6939
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
6940
+ if (externalSignal) {
6941
+ if (externalSignal.aborted) {
6942
+ clearTimeout(timer);
6943
+ controller.abort();
6944
+ } else {
6945
+ externalSignal.addEventListener(
6946
+ "abort",
6947
+ () => {
6948
+ clearTimeout(timer);
6949
+ controller.abort();
6950
+ },
6951
+ { once: true }
6952
+ );
6953
+ }
6788
6954
  }
6789
- if (negotiatedVersion) {
6790
- headers["Mcp-Protocol-Version"] = negotiatedVersion;
6955
+ return { controller, timer };
6956
+ }
6957
+ function logHeaders(dir, entries) {
6958
+ if (!verbose?.enabled) return;
6959
+ for (const [k, v] of entries) {
6960
+ if (dir === ">") verbose.headerOut(k, v);
6961
+ else verbose.headerIn(k, v);
6962
+ }
6963
+ }
6964
+ async function handleErrorStatus(res, method) {
6965
+ if (res.status === 401 || res.status === 403) throw new McpAuthError(res.status, url);
6966
+ const text = await res.text();
6967
+ let rpc;
6968
+ try {
6969
+ const p = JSON.parse(text);
6970
+ if (p.error && typeof p.error.code === "number") rpc = p.error;
6971
+ } catch {
6791
6972
  }
6973
+ if (rpc) throw new StatelessRpcError(rpc.code, rpc.message, rpc.data);
6974
+ throw new StatelessHttpError(res.status, res.statusText, method, text.slice(0, 512));
6975
+ }
6976
+ async function request(method, params) {
6977
+ const id = nextId2++;
6978
+ const headers = buildHeaders(method, params);
6792
6979
  const meta = {
6793
6980
  protocolVersion: negotiatedVersion ?? STATELESS_PROTOCOL_VERSION,
6794
6981
  clientInfo
@@ -6799,8 +6986,11 @@ async function createStatelessConnection(options) {
6799
6986
  method,
6800
6987
  params: { ...params, _meta: meta }
6801
6988
  });
6802
- const controller = new AbortController();
6803
- const timer = setTimeout(() => controller.abort(), timeoutMs);
6989
+ if (verbose?.enabled) {
6990
+ verbose.request(method, body);
6991
+ logHeaders(">", Object.entries(headers));
6992
+ }
6993
+ const { controller, timer } = makeAbort();
6804
6994
  let res;
6805
6995
  try {
6806
6996
  res = await fetchImpl(url, {
@@ -6809,35 +6999,28 @@ async function createStatelessConnection(options) {
6809
6999
  body,
6810
7000
  signal: controller.signal
6811
7001
  });
7002
+ } catch (err) {
7003
+ clearTimeout(timer);
7004
+ if (controller.signal.aborted && !externalSignal?.aborted) {
7005
+ throw new McpTimeoutError2(method, timeoutMs);
7006
+ }
7007
+ throw err;
6812
7008
  } finally {
6813
7009
  clearTimeout(timer);
6814
7010
  }
6815
- if (res.status === 401) {
6816
- throw new StatelessHttpError(401, "Unauthorized", method, "");
6817
- }
6818
- if (res.status >= 400) {
6819
- const text = await res.text();
6820
- let rpcError3;
6821
- try {
6822
- const parsed = JSON.parse(text);
6823
- if (parsed.error && typeof parsed.error.code === "number") {
6824
- rpcError3 = parsed.error;
6825
- }
6826
- } catch {
6827
- }
6828
- if (rpcError3) {
6829
- throw new StatelessRpcError(rpcError3.code, rpcError3.message, rpcError3.data);
6830
- }
6831
- throw new StatelessHttpError(res.status, res.statusText, method, text.slice(0, 512));
7011
+ if (verbose?.enabled) {
7012
+ verbose.http("<", `HTTP/1.1 ${res.status} ${res.statusText}`);
7013
+ logHeaders("<", res.headers.entries());
6832
7014
  }
7015
+ if (res.status >= 400) await handleErrorStatus(res, method);
6833
7016
  const ct = res.headers.get("content-type") ?? "";
6834
7017
  let frame;
6835
7018
  if (ct.toLowerCase().includes("text/event-stream")) {
6836
- const bytes = new Uint8Array(await res.arrayBuffer());
6837
- frame = selectSseFrame(bytes, id);
7019
+ frame = selectSseFrame(new Uint8Array(await res.arrayBuffer()), id);
6838
7020
  } else {
6839
7021
  frame = await res.json();
6840
7022
  }
7023
+ if (verbose?.enabled) verbose.response(JSON.stringify(frame));
6841
7024
  if (frame.error) {
6842
7025
  throw new StatelessRpcError(frame.error.code, frame.error.message, frame.error.data);
6843
7026
  }
@@ -6857,28 +7040,26 @@ async function createStatelessConnection(options) {
6857
7040
  return {
6858
7041
  async info() {
6859
7042
  if (serverInfoCache) return serverInfoCache;
6860
- const result = discoverResult2;
7043
+ const r = discoverResult2;
6861
7044
  serverInfoCache = {
6862
- name: result?.serverInfo?.name,
6863
- version: result?.serverInfo?.version,
6864
- title: result?.serverInfo?.title,
7045
+ name: r?.serverInfo?.name,
7046
+ version: r?.serverInfo?.version,
7047
+ title: r?.serverInfo?.title,
6865
7048
  protocolVersion: STATELESS_PROTOCOL_VERSION,
6866
- capabilities: result?.capabilities ?? {},
6867
- instructions: result?.instructions
7049
+ capabilities: r?.capabilities ?? {},
7050
+ instructions: r?.instructions
6868
7051
  };
6869
7052
  return serverInfoCache;
6870
7053
  },
6871
7054
  async listTools() {
6872
- const result = await request("tools/list", {});
6873
- return result?.tools ?? [];
7055
+ return (await request("tools/list", {}))?.tools ?? [];
6874
7056
  },
6875
- async callTool(name, args2) {
7057
+ async callTool(name, args2, _callOptions) {
6876
7058
  const result = await request("tools/call", {
6877
7059
  name,
6878
7060
  arguments: args2 ?? {}
6879
7061
  });
6880
- const resultType = result.resultType ?? "complete";
6881
- if (resultType === "input_required") {
7062
+ if ((result.resultType ?? "complete") === "input_required") {
6882
7063
  throw new InputRequiredError(
6883
7064
  JSON.stringify({ name, arguments: args2 ?? {}, result }, null, 2)
6884
7065
  );
@@ -6890,28 +7071,138 @@ async function createStatelessConnection(options) {
6890
7071
  };
6891
7072
  }
6892
7073
 
7074
+ // src/client/pi-connection.ts
7075
+ function buildStderrHandler(options) {
7076
+ if (!options.verbose?.enabled) return void 0;
7077
+ return (chunk) => {
7078
+ for (const l of chunk.split("\n")) {
7079
+ if (l.trim()) options.verbose.server(l);
7080
+ }
7081
+ };
7082
+ }
7083
+ function buildTransport(options) {
7084
+ if (options.url) {
7085
+ return new StreamableHttpTransport({
7086
+ url: options.url,
7087
+ headers: options.headers,
7088
+ fetch: options.fetchImpl
7089
+ });
7090
+ }
7091
+ if (!options.command) throw new Error("PiConnection requires either a url or a command");
7092
+ if (options.platform) {
7093
+ return new PlatformStdioTransport({
7094
+ command: options.command,
7095
+ args: options.args,
7096
+ env: options.env,
7097
+ cwd: options.cwd,
7098
+ inheritEnv: true,
7099
+ platform: options.platform,
7100
+ onStderr: buildStderrHandler(options)
7101
+ });
7102
+ }
7103
+ return new StdioTransport({
7104
+ command: options.command,
7105
+ args: options.args,
7106
+ env: options.env,
7107
+ cwd: options.cwd,
7108
+ inheritEnv: true,
7109
+ stderr: "pipe",
7110
+ onStderr: buildStderrHandler(options)
7111
+ });
7112
+ }
7113
+ async function createPiConnection(options) {
7114
+ const clientOptions = {
7115
+ name: "mcpcat",
7116
+ version: options.version ?? "0.0.0",
7117
+ requestTimeoutMs: options.timeoutMs
7118
+ };
7119
+ const client = new McpClient(clientOptions);
7120
+ const transport = buildTransport(options);
7121
+ if (options.verbose?.enabled) {
7122
+ transport.onMessage((msg) => {
7123
+ options.verbose.response(JSON.stringify(msg));
7124
+ });
7125
+ }
7126
+ try {
7127
+ let reqOpts2 = function() {
7128
+ const o = {};
7129
+ if (options.signal) o.signal = options.signal;
7130
+ if (options.verbose?.enabled) {
7131
+ o.onProgress = (p) => {
7132
+ options.verbose.progress(`progress: ${p.message ?? ""}`);
7133
+ };
7134
+ }
7135
+ return o;
7136
+ };
7137
+ var reqOpts = reqOpts2;
7138
+ const initResult = await client.connect(transport);
7139
+ return {
7140
+ async info() {
7141
+ return {
7142
+ name: initResult.serverInfo?.name,
7143
+ version: initResult.serverInfo?.version,
7144
+ title: initResult.serverInfo?.title,
7145
+ protocolVersion: initResult.protocolVersion,
7146
+ capabilities: initResult.capabilities,
7147
+ instructions: initResult.instructions
7148
+ };
7149
+ },
7150
+ async listTools() {
7151
+ return client.listTools(reqOpts2());
7152
+ },
7153
+ async callTool(name, args2, callOptions) {
7154
+ const o = reqOpts2();
7155
+ if (callOptions?.onProgress) {
7156
+ const outer = callOptions.onProgress;
7157
+ const inner = o.onProgress;
7158
+ o.onProgress = (p) => {
7159
+ outer(p.progress, p.total, p.message);
7160
+ inner?.(p);
7161
+ };
7162
+ }
7163
+ return client.callTool(name, args2, o);
7164
+ },
7165
+ async close() {
7166
+ await client.close();
7167
+ }
7168
+ };
7169
+ } catch (err) {
7170
+ const msg = err.message ?? "";
7171
+ if (msg.includes("401") || msg.includes("403") || msg.includes("Unauthorized")) {
7172
+ throw new McpAuthError(401, options.url ?? options.command ?? "");
7173
+ }
7174
+ if (msg.includes("timeout") || msg.includes("Timeout") || msg.includes("ETIMEDOUT")) {
7175
+ throw new McpTimeoutError2("connect", options.timeoutMs ?? 6e4);
7176
+ }
7177
+ throw err;
7178
+ }
7179
+ }
7180
+
6893
7181
  // src/client/connect.ts
6894
7182
  async function connect(platform2, options) {
7183
+ const shared = {
7184
+ version: options.version,
7185
+ timeoutMs: options.timeoutMs,
7186
+ signal: options.signal,
7187
+ verbose: options.verbose,
7188
+ platform: platform2
7189
+ };
6895
7190
  if (options.command) {
6896
7191
  return createPiConnection({
6897
7192
  command: options.command,
6898
7193
  args: options.args,
6899
7194
  env: options.env,
6900
7195
  cwd: options.cwd,
6901
- version: options.version,
6902
- platform: platform2
7196
+ ...shared
6903
7197
  });
6904
7198
  }
6905
- if (!options.url) {
6906
- throw new Error("connect requires either a url or a command");
6907
- }
7199
+ if (!options.url) throw new Error("connect requires either a url or a command");
6908
7200
  if (options.protocolVersion && !options.protocolVersion.startsWith("2026")) {
6909
7201
  return createPiConnection({
6910
7202
  url: options.url,
6911
7203
  headers: options.headers,
6912
- version: options.version,
6913
7204
  fetchImpl: platform2.fetch,
6914
- platform: platform2
7205
+ ...shared
6915
7206
  });
6916
7207
  }
6917
7208
  try {
@@ -6919,20 +7210,20 @@ async function connect(platform2, options) {
6919
7210
  url: options.url,
6920
7211
  headers: options.headers,
6921
7212
  fetchImpl: platform2.fetch,
6922
- clientInfo: { name: "mcpcat", version: options.version ?? "0.0.0" }
7213
+ clientInfo: { name: "mcpcat", version: options.version ?? "0.0.0" },
7214
+ timeoutMs: options.timeoutMs,
7215
+ signal: options.signal,
7216
+ verbose: options.verbose
6923
7217
  });
6924
7218
  } catch (err) {
6925
7219
  const isMethodNotFound = err instanceof StatelessRpcError && err.code === -32601 || err instanceof StatelessHttpError && err.status >= 400 && err.status < 500;
6926
- if (options.protocolVersion?.startsWith("2026")) {
6927
- throw err;
6928
- }
7220
+ if (options.protocolVersion?.startsWith("2026")) throw err;
6929
7221
  if (isMethodNotFound) {
6930
7222
  return createPiConnection({
6931
7223
  url: options.url,
6932
7224
  headers: options.headers,
6933
- version: options.version,
6934
7225
  fetchImpl: platform2.fetch,
6935
- platform: platform2
7226
+ ...shared
6936
7227
  });
6937
7228
  }
6938
7229
  throw err;
@@ -7484,6 +7775,28 @@ async function readStream(stream) {
7484
7775
  return new TextDecoder().decode(merged);
7485
7776
  }
7486
7777
 
7778
+ // src/config/expand.ts
7779
+ var PLACEHOLDER = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
7780
+ var PLACEHOLDER_TEST = /\$\{[A-Za-z_][A-Za-z0-9_]*\}/;
7781
+ function containsPlaceholders(value) {
7782
+ return PLACEHOLDER_TEST.test(value);
7783
+ }
7784
+ function expandPlaceholders(value, env) {
7785
+ return value.replace(PLACEHOLDER, (_, name) => env[name] ?? "");
7786
+ }
7787
+ function expandPlaceholdersStrict(value, env) {
7788
+ let hasUndefined = false;
7789
+ const result = value.replace(PLACEHOLDER, (_, name) => {
7790
+ if (env[name] === void 0) {
7791
+ hasUndefined = true;
7792
+ return "";
7793
+ }
7794
+ return env[name];
7795
+ });
7796
+ if (hasUndefined) return void 0;
7797
+ return result;
7798
+ }
7799
+
7487
7800
  // src/config/parse.ts
7488
7801
  var SERVER_NAME = /^[A-Za-z0-9_-]+$/;
7489
7802
  var EXPOSURES = ["codemode", "deferred", "direct", "hidden"];
@@ -8720,23 +9033,98 @@ async function runDiscover(platform2, args2) {
8720
9033
  return 0;
8721
9034
  }
8722
9035
 
8723
- // src/config/expand.ts
8724
- var PLACEHOLDER = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
8725
- var PLACEHOLDER_TEST = /\$\{[A-Za-z_][A-Za-z0-9_]*\}/;
8726
- function containsPlaceholders(value) {
8727
- return PLACEHOLDER_TEST.test(value);
9036
+ // src/render/result.ts
9037
+ function formatBytes(n) {
9038
+ if (n < 1024) return `${n} bytes`;
9039
+ const kb = n / 1024;
9040
+ if (kb < 1024) return `${kb.toFixed(1)} KB`;
9041
+ return `${(kb / 1024).toFixed(1)} MB`;
9042
+ }
9043
+ function truncateMiddle(text, max) {
9044
+ if (text.length <= max) return text;
9045
+ const half = Math.floor((max - 5) / 2);
9046
+ return `${text.slice(0, half)}
9047
+ [\u2026]
9048
+ ${text.slice(-half)}`;
9049
+ }
9050
+ function renderContentBlock(block) {
9051
+ if (block.type === "text") return block.text;
9052
+ if (block.type === "image") {
9053
+ const size = formatBytes(Math.ceil(block.data.length * 3 / 4));
9054
+ return `[image: ${block.mimeType}, ${size}]`;
9055
+ }
9056
+ if (block.type === "audio") {
9057
+ const audioBlock = block;
9058
+ const size = formatBytes(Math.ceil(audioBlock.data.length * 3 / 4));
9059
+ return `[audio: ${audioBlock.mimeType}, ${size}]`;
9060
+ }
9061
+ if (block.type === "resource_link") return `[resource: ${block.uri}]`;
9062
+ if (block.type === "resource") {
9063
+ return "text" in block.resource ? block.resource.text : `[blob: ${block.resource.uri}]`;
9064
+ }
9065
+ return void 0;
8728
9066
  }
8729
- function expandPlaceholdersStrict(value, env) {
8730
- let hasUndefined = false;
8731
- const result = value.replace(PLACEHOLDER, (_, name) => {
8732
- if (env[name] === void 0) {
8733
- hasUndefined = true;
8734
- return "";
9067
+ function renderCallResultText(result, options) {
9068
+ const parts = [];
9069
+ if (result.content) {
9070
+ for (const block of result.content) {
9071
+ const rendered = renderContentBlock(block);
9072
+ if (rendered !== void 0) parts.push(rendered);
8735
9073
  }
8736
- return env[name];
8737
- });
8738
- if (hasUndefined) return void 0;
8739
- return result;
9074
+ }
9075
+ if (parts.length === 0 && result.structuredContent !== void 0) {
9076
+ parts.push(JSON.stringify(result.structuredContent, null, 2));
9077
+ }
9078
+ let text = parts.join("\n");
9079
+ if (options?.maxChars && text.length > options.maxChars) {
9080
+ text = truncateMiddle(text, options.maxChars);
9081
+ }
9082
+ return text;
9083
+ }
9084
+ function renderCallResultJson(result) {
9085
+ return JSON.stringify(result, null, 2);
9086
+ }
9087
+ async function renderCallResult(platform2, result, options) {
9088
+ if (options?.json) {
9089
+ const json = renderCallResultJson(result);
9090
+ if (result.isError) {
9091
+ await platform2.write(
9092
+ 2,
9093
+ `${JSON.stringify({ error: { kind: "tool_error", message: json } }, null, 2)}
9094
+ `
9095
+ );
9096
+ return 1;
9097
+ }
9098
+ await platform2.write(1, `${json}
9099
+ `);
9100
+ return 0;
9101
+ }
9102
+ const text = renderCallResultText(result, options);
9103
+ if (result.isError) {
9104
+ await platform2.write(2, `${text || "(tool reported error)"}
9105
+ `);
9106
+ return 1;
9107
+ }
9108
+ if (text) {
9109
+ await platform2.write(1, `${text}
9110
+ `);
9111
+ }
9112
+ return 0;
9113
+ }
9114
+ async function renderToolsJson(platform2, tools) {
9115
+ await platform2.write(1, `${JSON.stringify({ tools }, null, 2)}
9116
+ `);
9117
+ }
9118
+ async function renderInfoJson(platform2, info) {
9119
+ await platform2.write(1, `${JSON.stringify(info, null, 2)}
9120
+ `);
9121
+ }
9122
+ async function renderErrorJson(platform2, kind, message, code2, data) {
9123
+ const errorObj = { kind, message };
9124
+ if (code2 !== void 0) errorObj.code = code2;
9125
+ if (data !== void 0) errorObj.data = data;
9126
+ await platform2.write(2, `${JSON.stringify({ error: errorObj }, null, 2)}
9127
+ `);
8740
9128
  }
8741
9129
 
8742
9130
  // src/serve/catalog.ts
@@ -11405,6 +11793,91 @@ bridge does not survive a reload; runs in the foreground (start with &)
11405
11793
  return runServe(platform2, serveOptions, version);
11406
11794
  }
11407
11795
 
11796
+ // src/cli/exit-codes.ts
11797
+ var EXIT_OK = 0;
11798
+ var EXIT_TOOL_ERROR = 1;
11799
+ var EXIT_USAGE = 2;
11800
+ var EXIT_CONNECTION = 3;
11801
+ var EXIT_AUTH = 4;
11802
+ var EXIT_RPC_ERROR = 5;
11803
+ var EXIT_INPUT_REQUIRED = 6;
11804
+ var EXIT_TIMEOUT = 124;
11805
+ var EXIT_INTERRUPTED = 130;
11806
+ var McpcatError = class extends Error {
11807
+ exitCode;
11808
+ constructor(exitCode, message) {
11809
+ super(message);
11810
+ this.name = "McpcatError";
11811
+ this.exitCode = exitCode;
11812
+ }
11813
+ };
11814
+
11815
+ // src/cli/verbose.ts
11816
+ var SENSITIVE_HEADER_RE = /^(authorization|cookie|proxy-authorization)$/i;
11817
+ var SENSITIVE_NAME_RE = /token|key|secret|password|session/i;
11818
+ function isSensitiveHeader(name) {
11819
+ return SENSITIVE_HEADER_RE.test(name) || SENSITIVE_NAME_RE.test(name);
11820
+ }
11821
+ function redact(text, secrets) {
11822
+ let result = text;
11823
+ for (const secret of secrets) {
11824
+ if (secret.length > 0) {
11825
+ while (result.includes(secret)) {
11826
+ result = result.replace(secret, "[REDACTED]");
11827
+ }
11828
+ }
11829
+ }
11830
+ return result;
11831
+ }
11832
+ function createVerboseLogger(platform2, enabled) {
11833
+ const secrets = /* @__PURE__ */ new Set();
11834
+ function write(line) {
11835
+ const safe = redact(line, secrets);
11836
+ platform2.write(2, `${safe}
11837
+ `).catch(() => {
11838
+ });
11839
+ }
11840
+ return {
11841
+ enabled,
11842
+ secrets,
11843
+ request(method, body) {
11844
+ if (!enabled) return;
11845
+ write(`> ${method}`);
11846
+ for (const line of body.split("\n")) {
11847
+ write(`> ${line}`);
11848
+ }
11849
+ },
11850
+ response(body) {
11851
+ if (!enabled) return;
11852
+ for (const line of body.split("\n")) {
11853
+ write(`< ${line}`);
11854
+ }
11855
+ },
11856
+ http(direction, line) {
11857
+ if (!enabled) return;
11858
+ write(`${direction} ${line}`);
11859
+ },
11860
+ server(line) {
11861
+ if (!enabled) return;
11862
+ write(`[server] ${line}`);
11863
+ },
11864
+ headerOut(name, value) {
11865
+ if (!enabled) return;
11866
+ const display = isSensitiveHeader(name) ? "[REDACTED]" : redact(value, secrets);
11867
+ write(`> ${name}: ${display}`);
11868
+ },
11869
+ headerIn(name, value) {
11870
+ if (!enabled) return;
11871
+ const display = isSensitiveHeader(name) ? "[REDACTED]" : redact(value, secrets);
11872
+ write(`< ${name}: ${display}`);
11873
+ },
11874
+ progress(message) {
11875
+ if (!enabled) return;
11876
+ write(`* ${message}`);
11877
+ }
11878
+ };
11879
+ }
11880
+
11408
11881
  // src/cli/main.ts
11409
11882
  var VERBS = [
11410
11883
  "info",
@@ -11522,6 +11995,8 @@ function parseFlags(args2) {
11522
11995
  const serverTargets = [];
11523
11996
  let allowShell = false;
11524
11997
  let timeoutMs;
11998
+ let verbose = false;
11999
+ let silent = false;
11525
12000
  const positional = [];
11526
12001
  let trailingCommand;
11527
12002
  for (let i = 0; i < args2.length; i++) {
@@ -11558,6 +12033,10 @@ function parseFlags(args2) {
11558
12033
  serverTargets.push(args2[++i]);
11559
12034
  } else if (arg === "--allow-shell") {
11560
12035
  allowShell = true;
12036
+ } else if (arg === "-v" || arg === "--verbose") {
12037
+ verbose = true;
12038
+ } else if (arg === "--silent") {
12039
+ silent = true;
11561
12040
  } else if ((arg === "-m" || arg === "--timeout") && args2[i + 1]) {
11562
12041
  timeoutMs = Number(args2[++i]) * 1e3;
11563
12042
  } else if (!arg.startsWith("-")) {
@@ -11578,6 +12057,8 @@ function parseFlags(args2) {
11578
12057
  serverTargets,
11579
12058
  allowShell,
11580
12059
  timeoutMs,
12060
+ verbose,
12061
+ silent,
11581
12062
  positional,
11582
12063
  trailingCommand
11583
12064
  };
@@ -11626,7 +12107,9 @@ function parseArgs(args2) {
11626
12107
  inlineSource: flags.inlineSource,
11627
12108
  serverTargets: flags.serverTargets,
11628
12109
  allowShell: flags.allowShell,
11629
- timeoutMs: flags.timeoutMs
12110
+ timeoutMs: flags.timeoutMs,
12111
+ verbose: flags.verbose,
12112
+ silent: flags.silent
11630
12113
  };
11631
12114
  }
11632
12115
  function formatToolLine(tool) {
@@ -11650,26 +12133,6 @@ function formatToolSchema(tool) {
11650
12133
  }
11651
12134
  return lines.join("\n");
11652
12135
  }
11653
- function renderCallResult(result) {
11654
- const parts = [];
11655
- if (result.content) {
11656
- for (const block of result.content) {
11657
- if (block.type === "text") parts.push(block.text);
11658
- else if (block.type === "image")
11659
- parts.push(`[image: ${block.mimeType}, ${Math.ceil(block.data.length * 3 / 4)} bytes]`);
11660
- else if (block.type === "resource_link") parts.push(`[resource: ${block.uri}]`);
11661
- else if (block.type === "resource") {
11662
- parts.push(
11663
- "text" in block.resource ? block.resource.text : `[blob: ${block.resource.uri}]`
11664
- );
11665
- }
11666
- }
11667
- }
11668
- if (parts.length === 0 && result.structuredContent !== void 0) {
11669
- parts.push(JSON.stringify(result.structuredContent, null, 2));
11670
- }
11671
- return parts.join("\n");
11672
- }
11673
12136
  var NETWORK_HINT = "connection failed (use slicc-node, slicc-swift, or the browser extension for servers without CORS)";
11674
12137
  function transportHint(err) {
11675
12138
  if (err instanceof TypeError) {
@@ -11712,8 +12175,12 @@ async function readDataArg(platform2, dataArg) {
11712
12175
  }
11713
12176
  return JSON.parse(raw);
11714
12177
  }
11715
- async function runInfo(platform2, conn) {
12178
+ async function runInfo(platform2, conn, json) {
11716
12179
  const info = await conn.info();
12180
+ if (json) {
12181
+ await renderInfoJson(platform2, info);
12182
+ return EXIT_OK;
12183
+ }
11717
12184
  const lines = [];
11718
12185
  if (info.name) lines.push(`Server: ${info.name}${info.version ? ` ${info.version}` : ""}`);
11719
12186
  if (info.title) lines.push(`Title: ${info.title}`);
@@ -11730,48 +12197,66 @@ async function runInfo(platform2, conn) {
11730
12197
  }
11731
12198
  await platform2.write(1, `${lines.join("\n")}
11732
12199
  `);
11733
- return 0;
12200
+ return EXIT_OK;
11734
12201
  }
11735
- async function runTools(platform2, conn) {
12202
+ async function runTools(platform2, conn, json) {
11736
12203
  const tools = await conn.listTools();
11737
- if (tools.length === 0) {
11738
- await platform2.write(1, "(no tools)\n");
11739
- } else {
11740
- await platform2.write(1, `${tools.map(formatToolLine).join("\n")}
11741
- `);
12204
+ if (json) {
12205
+ await renderToolsJson(platform2, tools);
12206
+ return EXIT_OK;
11742
12207
  }
11743
- return 0;
12208
+ await platform2.write(
12209
+ 1,
12210
+ tools.length === 0 ? "(no tools)\n" : `${tools.map(formatToolLine).join("\n")}
12211
+ `
12212
+ );
12213
+ return EXIT_OK;
11744
12214
  }
11745
- async function runCall(platform2, conn, toolName, dataArg) {
11746
- let toolArgs = {};
12215
+ async function runCall(platform2, conn, toolName, dataArg, toolArgs, opts) {
12216
+ let base = {};
11747
12217
  if (dataArg) {
11748
12218
  try {
11749
- toolArgs = await readDataArg(platform2, dataArg);
12219
+ base = await readDataArg(platform2, dataArg);
11750
12220
  } catch (err) {
11751
- await platform2.write(2, `mcpcat: invalid -d argument: ${err.message}
11752
- `);
11753
- return 2;
12221
+ await writeErr(
12222
+ platform2,
12223
+ opts.json ?? false,
12224
+ "usage",
12225
+ `invalid -d argument: ${err.message}`
12226
+ );
12227
+ return EXIT_USAGE;
11754
12228
  }
11755
12229
  }
11756
- const result = await conn.callTool(toolName, toolArgs);
11757
- const output = renderCallResult(result);
11758
- if (result.isError) {
11759
- await platform2.write(2, `${output}
11760
- `);
11761
- return 1;
12230
+ let fin = base;
12231
+ if (toolArgs.length > 0) {
12232
+ const tools = await conn.listTools();
12233
+ const tool = tools.find((t) => t.name === toolName);
12234
+ const schema = tool?.inputSchema ?? { type: "object", properties: {} };
12235
+ const c = coerceArgsBySchema(toolArgs, schema, { noValidate: opts.noValidate });
12236
+ if (!c.ok) {
12237
+ await writeErr(platform2, opts.json ?? false, "usage", c.error);
12238
+ return EXIT_USAGE;
12239
+ }
12240
+ fin = mergeArgs(base, c.value);
11762
12241
  }
11763
- await platform2.write(1, `${output}
11764
- `);
11765
- return 0;
12242
+ return renderCallResult(
12243
+ platform2,
12244
+ await conn.callTool(toolName, fin, { onProgress: opts.onProgress }),
12245
+ { json: opts.json }
12246
+ );
11766
12247
  }
11767
- async function runDescribe(platform2, conn, toolName, showSchema) {
12248
+ async function runDescribe(platform2, conn, toolName, showSchema, json) {
11768
12249
  const tools = await conn.listTools();
11769
12250
  if (toolName) {
11770
12251
  const tool = tools.find((t) => t.name === toolName);
11771
12252
  if (!tool) {
11772
- await platform2.write(2, `mcpcat: tool '${toolName}' not found
12253
+ await writeErr(platform2, json, "usage", `tool '${toolName}' not found`);
12254
+ return EXIT_USAGE;
12255
+ }
12256
+ if (json) {
12257
+ await platform2.write(1, `${JSON.stringify(tool, null, 2)}
11773
12258
  `);
11774
- return 2;
12259
+ return EXIT_OK;
11775
12260
  }
11776
12261
  if (showSchema && tool.inputSchema) {
11777
12262
  await platform2.write(1, `${JSON.stringify(tool.inputSchema, null, 2)}
@@ -11781,28 +12266,36 @@ async function runDescribe(platform2, conn, toolName, showSchema) {
11781
12266
  `);
11782
12267
  }
11783
12268
  } else {
12269
+ if (json) {
12270
+ await platform2.write(1, `${JSON.stringify({ tools }, null, 2)}
12271
+ `);
12272
+ return EXIT_OK;
12273
+ }
11784
12274
  for (const tool of tools) {
11785
12275
  await platform2.write(1, `${formatToolSchema(tool)}
11786
12276
 
11787
12277
  `);
11788
12278
  }
11789
12279
  }
11790
- return 0;
12280
+ return EXIT_OK;
11791
12281
  }
11792
- async function runVerb(platform2, parsed, conn) {
12282
+ async function runVerb(platform2, parsed, conn, onProgress) {
11793
12283
  switch (parsed.verb) {
11794
12284
  case "info":
11795
- return runInfo(platform2, conn);
12285
+ return runInfo(platform2, conn, parsed.jsonOutput);
11796
12286
  case "tools":
11797
- return runTools(platform2, conn);
12287
+ return runTools(platform2, conn, parsed.jsonOutput);
11798
12288
  case "call":
11799
- return runCall(platform2, conn, parsed.toolName, parsed.dataArg);
12289
+ return runCall(platform2, conn, parsed.toolName, parsed.dataArg, [], {
12290
+ json: parsed.jsonOutput,
12291
+ onProgress
12292
+ });
11800
12293
  case "describe":
11801
- return runDescribe(platform2, conn, parsed.toolName, parsed.showSchema);
12294
+ return runDescribe(platform2, conn, parsed.toolName, parsed.showSchema, parsed.jsonOutput);
11802
12295
  default:
11803
12296
  await platform2.write(2, `mcpcat: unknown command '${parsed.verb}'
11804
12297
  `);
11805
- return 2;
12298
+ return EXIT_USAGE;
11806
12299
  }
11807
12300
  }
11808
12301
  async function probeServer(_name, config, platform2) {
@@ -12051,7 +12544,7 @@ async function run(platform2, version, args2) {
12051
12544
  if (UNIMPLEMENTED_VERBS.has(parsed.verb)) {
12052
12545
  await platform2.write(2, `mcpcat: '${parsed.verb}' is not implemented yet
12053
12546
  `);
12054
- return 1;
12547
+ return EXIT_TOOL_ERROR;
12055
12548
  }
12056
12549
  if (parsed.verb === "serve") {
12057
12550
  return dispatchServeVerb(platform2, args2, version);
@@ -12073,44 +12566,100 @@ async function run(platform2, version, args2) {
12073
12566
  `);
12074
12567
  return 2;
12075
12568
  }
12569
+ return runClientVerb(platform2, version, parsed);
12570
+ }
12571
+ function expandAndCollect(headers, env) {
12572
+ const secrets = [];
12573
+ const result = {};
12574
+ for (const [k, v] of Object.entries(headers)) {
12575
+ const e = expandPlaceholders(v, env);
12576
+ result[k] = e;
12577
+ if (e !== v) secrets.push(e);
12578
+ if (v.length > 0) secrets.push(v);
12579
+ }
12580
+ return { expanded: result, secrets };
12581
+ }
12582
+ function classifyError(err) {
12583
+ if (err instanceof InputRequiredError) {
12584
+ return { code: EXIT_INPUT_REQUIRED, kind: "input_required", msg: err.requestJson };
12585
+ }
12586
+ if (err instanceof McpTimeoutError2) {
12587
+ return { code: EXIT_TIMEOUT, kind: "timeout", msg: err.message };
12588
+ }
12589
+ if (err instanceof McpAuthError) {
12590
+ return { code: EXIT_AUTH, kind: "auth", msg: err.message };
12591
+ }
12592
+ if (err instanceof McpcatError) {
12593
+ return { code: err.exitCode, kind: "error", msg: err.message };
12594
+ }
12595
+ const msg = err.message ?? String(err);
12596
+ const name = err.name ?? "";
12597
+ if (name === "McpTimeoutError" || msg.includes("timed out") || msg.includes("timeout") || msg.includes("Timeout")) {
12598
+ return { code: EXIT_TIMEOUT, kind: "timeout", msg };
12599
+ }
12600
+ return { code: EXIT_RPC_ERROR, kind: "rpc_error", msg };
12601
+ }
12602
+ async function runClientVerb(platform2, version, parsed) {
12603
+ const { expanded, secrets } = expandAndCollect(parsed.headers, platform2.env);
12604
+ const verbose = createVerboseLogger(platform2, parsed.verbose);
12605
+ for (const s of secrets) verbose.secrets.add(s);
12606
+ const timeoutMs = parsed.timeoutMs;
12607
+ const sigintController = new AbortController();
12608
+ let interrupted = false;
12609
+ platform2.onSignal?.("SIGINT", () => {
12610
+ interrupted = true;
12611
+ sigintController.abort();
12612
+ });
12613
+ const showProgress = !parsed.silent && (parsed.verbose || platform2.isatty(2));
12614
+ const onProgress = (progress, total, message) => {
12615
+ if (!showProgress) return;
12616
+ const pct = total ? ` ${Math.round(progress / total * 100)}%` : "";
12617
+ const msg = message ? ` ${message}` : "";
12618
+ platform2.write(2, `* progress:${pct}${msg}
12619
+ `).catch(() => {
12620
+ });
12621
+ };
12076
12622
  const config = await MergedConfigSource.load(platform2);
12623
+ const target = parsed.target === "__trailing__" ? "" : parsed.target;
12077
12624
  let resolved;
12078
12625
  try {
12079
- resolved = await resolveTarget(
12080
- parsed.target === "__trailing__" ? "" : parsed.target,
12081
- parsed.trailingCommand,
12082
- config
12083
- );
12626
+ resolved = await resolveTarget(target, parsed.trailingCommand, config);
12084
12627
  } catch (err) {
12085
- await platform2.write(2, `mcpcat: ${err.message}
12086
- `);
12087
- return 2;
12628
+ await writeErr(platform2, parsed.jsonOutput, "usage", err.message);
12629
+ return EXIT_USAGE;
12088
12630
  }
12089
12631
  let conn;
12090
12632
  try {
12091
12633
  conn = await connect(platform2, {
12092
12634
  ...resolved,
12093
- headers: { ...resolved.headers, ...parsed.headers },
12635
+ headers: { ...resolved.headers, ...expanded },
12094
12636
  protocolVersion: parsed.protocolVersion,
12095
- version
12637
+ version,
12638
+ timeoutMs,
12639
+ signal: sigintController.signal,
12640
+ verbose
12096
12641
  });
12097
12642
  } catch (err) {
12643
+ if (err instanceof McpAuthError) {
12644
+ await writeErr(platform2, parsed.jsonOutput, "auth", err.message);
12645
+ return EXIT_AUTH;
12646
+ }
12098
12647
  const msg = transportHint(err) ?? `connection failed: ${err.message}`;
12099
- await platform2.write(2, `mcpcat: ${msg}
12100
- `);
12101
- return 3;
12648
+ await writeErr(platform2, parsed.jsonOutput, "connection", msg);
12649
+ return EXIT_CONNECTION;
12102
12650
  }
12103
12651
  try {
12104
- return await runVerb(platform2, parsed, conn);
12652
+ return await runVerb(platform2, parsed, conn, onProgress);
12105
12653
  } catch (err) {
12106
- if (err instanceof InputRequiredError) {
12107
- await platform2.write(2, `${err.requestJson}
12108
- `);
12109
- return 6;
12654
+ if (interrupted) {
12655
+ await platform2.write(2, "mcpcat: interrupted\n");
12656
+ return EXIT_INTERRUPTED;
12110
12657
  }
12111
- await platform2.write(2, `mcpcat: ${err.message}
12658
+ const e = classifyError(err);
12659
+ if (err instanceof InputRequiredError) await platform2.write(2, `${e.msg}
12112
12660
  `);
12113
- return 5;
12661
+ else await writeErr(platform2, parsed.jsonOutput, e.kind, e.msg);
12662
+ return e.code;
12114
12663
  } finally {
12115
12664
  try {
12116
12665
  await conn.close();
@@ -12118,6 +12667,11 @@ async function run(platform2, version, args2) {
12118
12667
  }
12119
12668
  }
12120
12669
  }
12670
+ async function writeErr(p, json, kind, msg) {
12671
+ if (json) await renderErrorJson(p, kind, msg);
12672
+ else await p.write(2, `mcpcat: ${msg}
12673
+ `);
12674
+ }
12121
12675
 
12122
12676
  // src/platform/host.ts
12123
12677
  import { spawn as cpSpawn } from "node:child_process";
@@ -12319,5 +12873,5 @@ function createHostPlatform() {
12319
12873
  // src/host.ts
12320
12874
  var platform = createHostPlatform();
12321
12875
  var args = process.argv.slice(2);
12322
- var code = await run(platform, "0.0.0", args);
12876
+ var code = await run(platform, "0.9.0", args);
12323
12877
  process.exit(code);