@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.
- package/dist/host/mcpcat.mjs +761 -207
- package/dist/kernel/mcpcat.mjs +761 -207
- package/package.json +2 -2
package/dist/host/mcpcat.mjs
CHANGED
|
@@ -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
|
-
|
|
6778
|
-
const
|
|
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
|
|
6786
|
-
if (
|
|
6787
|
-
|
|
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
|
-
|
|
6790
|
-
|
|
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
|
-
|
|
6803
|
-
|
|
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 (
|
|
6816
|
-
|
|
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
|
-
|
|
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
|
|
7043
|
+
const r = discoverResult2;
|
|
6861
7044
|
serverInfoCache = {
|
|
6862
|
-
name:
|
|
6863
|
-
version:
|
|
6864
|
-
title:
|
|
7045
|
+
name: r?.serverInfo?.name,
|
|
7046
|
+
version: r?.serverInfo?.version,
|
|
7047
|
+
title: r?.serverInfo?.title,
|
|
6865
7048
|
protocolVersion: STATELESS_PROTOCOL_VERSION,
|
|
6866
|
-
capabilities:
|
|
6867
|
-
instructions:
|
|
7049
|
+
capabilities: r?.capabilities ?? {},
|
|
7050
|
+
instructions: r?.instructions
|
|
6868
7051
|
};
|
|
6869
7052
|
return serverInfoCache;
|
|
6870
7053
|
},
|
|
6871
7054
|
async listTools() {
|
|
6872
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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/
|
|
8724
|
-
|
|
8725
|
-
|
|
8726
|
-
|
|
8727
|
-
return
|
|
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
|
|
8730
|
-
|
|
8731
|
-
|
|
8732
|
-
|
|
8733
|
-
|
|
8734
|
-
|
|
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
|
-
|
|
8737
|
-
|
|
8738
|
-
|
|
8739
|
-
|
|
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
|
|
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 (
|
|
11738
|
-
await platform2
|
|
11739
|
-
|
|
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
|
-
|
|
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
|
|
12215
|
+
async function runCall(platform2, conn, toolName, dataArg, toolArgs, opts) {
|
|
12216
|
+
let base = {};
|
|
11747
12217
|
if (dataArg) {
|
|
11748
12218
|
try {
|
|
11749
|
-
|
|
12219
|
+
base = await readDataArg(platform2, dataArg);
|
|
11750
12220
|
} catch (err) {
|
|
11751
|
-
await
|
|
11752
|
-
|
|
11753
|
-
|
|
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
|
-
|
|
11757
|
-
|
|
11758
|
-
|
|
11759
|
-
|
|
11760
|
-
|
|
11761
|
-
|
|
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
|
-
|
|
11764
|
-
|
|
11765
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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, ...
|
|
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.
|
|
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 (
|
|
12107
|
-
await platform2.write(2,
|
|
12108
|
-
|
|
12109
|
-
return 6;
|
|
12654
|
+
if (interrupted) {
|
|
12655
|
+
await platform2.write(2, "mcpcat: interrupted\n");
|
|
12656
|
+
return EXIT_INTERRUPTED;
|
|
12110
12657
|
}
|
|
12111
|
-
|
|
12658
|
+
const e = classifyError(err);
|
|
12659
|
+
if (err instanceof InputRequiredError) await platform2.write(2, `${e.msg}
|
|
12112
12660
|
`);
|
|
12113
|
-
|
|
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.
|
|
12876
|
+
var code = await run(platform, "0.9.0", args);
|
|
12323
12877
|
process.exit(code);
|