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