@ai-ecoverse/mcpcat 0.11.0 → 0.13.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/README.md +8 -2
- package/dist/THIRD_PARTY_NOTICES +173 -0
- package/dist/host/mcpcat.mjs +1707 -227
- package/dist/kernel/mcpcat.mjs +1833 -218
- package/package.json +1 -1
package/dist/host/mcpcat.mjs
CHANGED
|
@@ -6344,6 +6344,133 @@ var McpOAuthProvider = class {
|
|
|
6344
6344
|
}
|
|
6345
6345
|
};
|
|
6346
6346
|
|
|
6347
|
+
// src/auth/jerk-token-source.ts
|
|
6348
|
+
var MIN_VERSION = [0, 13, 0];
|
|
6349
|
+
var VERSION_TIMEOUT_MS = 2e3;
|
|
6350
|
+
var cachedAvailable;
|
|
6351
|
+
function parseVersion(raw) {
|
|
6352
|
+
const match = raw.trim().match(/(\d+)\.(\d+)\.(\d+)/);
|
|
6353
|
+
if (!match) return void 0;
|
|
6354
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
6355
|
+
}
|
|
6356
|
+
function versionSatisfied(actual) {
|
|
6357
|
+
for (let i = 0; i < 3; i++) {
|
|
6358
|
+
if (actual[i] > MIN_VERSION[i]) return true;
|
|
6359
|
+
if (actual[i] < MIN_VERSION[i]) return false;
|
|
6360
|
+
}
|
|
6361
|
+
return true;
|
|
6362
|
+
}
|
|
6363
|
+
async function readAll(stream) {
|
|
6364
|
+
const reader = stream.getReader();
|
|
6365
|
+
const chunks = [];
|
|
6366
|
+
for (; ; ) {
|
|
6367
|
+
const { done, value } = await reader.read();
|
|
6368
|
+
if (done) break;
|
|
6369
|
+
chunks.push(value);
|
|
6370
|
+
}
|
|
6371
|
+
const total = chunks.reduce((s, c) => s + c.length, 0);
|
|
6372
|
+
const buf = new Uint8Array(total);
|
|
6373
|
+
let off = 0;
|
|
6374
|
+
for (const c of chunks) {
|
|
6375
|
+
buf.set(c, off);
|
|
6376
|
+
off += c.length;
|
|
6377
|
+
}
|
|
6378
|
+
return new TextDecoder().decode(buf);
|
|
6379
|
+
}
|
|
6380
|
+
async function jerkOnPath(platform2) {
|
|
6381
|
+
const pathVar = platform2.env.PATH ?? platform2.env.Path ?? "";
|
|
6382
|
+
const sep = pathVar.includes(";") ? ";" : ":";
|
|
6383
|
+
const dirs = pathVar.split(sep).filter(Boolean);
|
|
6384
|
+
for (const dir of dirs) {
|
|
6385
|
+
if (await platform2.fs.exists(`${dir}/jerk`)) return true;
|
|
6386
|
+
}
|
|
6387
|
+
return false;
|
|
6388
|
+
}
|
|
6389
|
+
async function jerkAvailable(platform2) {
|
|
6390
|
+
if (cachedAvailable !== void 0) return cachedAvailable;
|
|
6391
|
+
if (!await jerkOnPath(platform2)) {
|
|
6392
|
+
cachedAvailable = false;
|
|
6393
|
+
return false;
|
|
6394
|
+
}
|
|
6395
|
+
try {
|
|
6396
|
+
const proc = await platform2.spawn({ argv: ["jerk", "--version"] });
|
|
6397
|
+
const timeout = new Promise((_, reject) => {
|
|
6398
|
+
setTimeout(() => reject(new Error("timeout")), VERSION_TIMEOUT_MS);
|
|
6399
|
+
});
|
|
6400
|
+
const work = Promise.all([readAll(proc.stdout), proc.wait()]).then(([out, result]) => {
|
|
6401
|
+
proc.stderr.cancel().catch(() => {
|
|
6402
|
+
});
|
|
6403
|
+
if (result.status !== 0) return false;
|
|
6404
|
+
const ver = parseVersion(out);
|
|
6405
|
+
return ver !== void 0 && versionSatisfied(ver);
|
|
6406
|
+
});
|
|
6407
|
+
cachedAvailable = await Promise.race([work, timeout]);
|
|
6408
|
+
} catch {
|
|
6409
|
+
cachedAvailable = false;
|
|
6410
|
+
}
|
|
6411
|
+
return cachedAvailable;
|
|
6412
|
+
}
|
|
6413
|
+
function jerkId(name) {
|
|
6414
|
+
if (name.startsWith("mcp:")) return name;
|
|
6415
|
+
if (name.startsWith("url:")) return `mcp:${name}`;
|
|
6416
|
+
return `mcp:${name}`;
|
|
6417
|
+
}
|
|
6418
|
+
var JERK_EXIT_SIGN_IN = 3;
|
|
6419
|
+
var JERK_EXIT_UNAVAILABLE = 4;
|
|
6420
|
+
var JerkSignInRequiredError = class extends Error {
|
|
6421
|
+
id;
|
|
6422
|
+
scope;
|
|
6423
|
+
constructor(id, scope) {
|
|
6424
|
+
const scopeHint = scope ? ` --scope ${scope}` : "";
|
|
6425
|
+
super(`sign-in required. Run: jerk ${id} create${scopeHint}`);
|
|
6426
|
+
this.name = "JerkSignInRequiredError";
|
|
6427
|
+
this.id = id;
|
|
6428
|
+
this.scope = scope;
|
|
6429
|
+
}
|
|
6430
|
+
};
|
|
6431
|
+
var JerkTokenSource = class {
|
|
6432
|
+
platform;
|
|
6433
|
+
constructor(platform2) {
|
|
6434
|
+
this.platform = platform2;
|
|
6435
|
+
}
|
|
6436
|
+
async getToken(name, serverUrl, scope) {
|
|
6437
|
+
const id = jerkId(name);
|
|
6438
|
+
const argv = ["jerk", id, "get", "--reveal", "--json"];
|
|
6439
|
+
if (scope) {
|
|
6440
|
+
argv.push("--scope", scope);
|
|
6441
|
+
}
|
|
6442
|
+
const proc = await this.platform.spawn({ argv });
|
|
6443
|
+
const [outRaw, , result] = await Promise.all([
|
|
6444
|
+
readAll(proc.stdout),
|
|
6445
|
+
readAll(proc.stderr),
|
|
6446
|
+
proc.wait()
|
|
6447
|
+
]);
|
|
6448
|
+
if (result.status === JERK_EXIT_SIGN_IN) {
|
|
6449
|
+
throw new JerkSignInRequiredError(id, scope);
|
|
6450
|
+
}
|
|
6451
|
+
if (result.status === JERK_EXIT_UNAVAILABLE) {
|
|
6452
|
+
return void 0;
|
|
6453
|
+
}
|
|
6454
|
+
if (result.status !== 0) {
|
|
6455
|
+
return void 0;
|
|
6456
|
+
}
|
|
6457
|
+
const parsed = JSON.parse(outRaw.trim());
|
|
6458
|
+
if (!parsed.token) return void 0;
|
|
6459
|
+
return {
|
|
6460
|
+
accessToken: parsed.token,
|
|
6461
|
+
expiresAt: parsed.expires_at
|
|
6462
|
+
};
|
|
6463
|
+
}
|
|
6464
|
+
async load(_name, _serverUrl) {
|
|
6465
|
+
return void 0;
|
|
6466
|
+
}
|
|
6467
|
+
async save(_name, _serverUrl, _state) {
|
|
6468
|
+
}
|
|
6469
|
+
async remove(_name, _serverUrl) {
|
|
6470
|
+
return false;
|
|
6471
|
+
}
|
|
6472
|
+
};
|
|
6473
|
+
|
|
6347
6474
|
// src/auth/auth-provider.ts
|
|
6348
6475
|
var CIMD_URL = "https://mcpcat.sliccy.ai/oauth/client.json";
|
|
6349
6476
|
var CLIENT_NAME = "mcpcat";
|
|
@@ -6396,6 +6523,47 @@ var AuthStepUpError = class extends Error {
|
|
|
6396
6523
|
}
|
|
6397
6524
|
};
|
|
6398
6525
|
function createMcpAuthProvider(options) {
|
|
6526
|
+
if (options.source instanceof JerkTokenSource) {
|
|
6527
|
+
return createJerkAuthProvider({ ...options, source: options.source });
|
|
6528
|
+
}
|
|
6529
|
+
return createPiAuthProvider(options);
|
|
6530
|
+
}
|
|
6531
|
+
function createJerkAuthProvider(options) {
|
|
6532
|
+
const { name, serverUrl, source } = options;
|
|
6533
|
+
let currentToken;
|
|
6534
|
+
return {
|
|
6535
|
+
async token() {
|
|
6536
|
+
const result = await source.getToken(name, serverUrl);
|
|
6537
|
+
currentToken = result?.accessToken;
|
|
6538
|
+
return currentToken;
|
|
6539
|
+
},
|
|
6540
|
+
async onUnauthorized(context) {
|
|
6541
|
+
const challenge = parseWwwAuthenticate(context.response.headers.get("www-authenticate"));
|
|
6542
|
+
if (challenge.error === "insufficient_scope") {
|
|
6543
|
+
try {
|
|
6544
|
+
const result2 = await source.getToken(name, serverUrl, challenge.scope);
|
|
6545
|
+
if (result2) {
|
|
6546
|
+
currentToken = result2.accessToken;
|
|
6547
|
+
return;
|
|
6548
|
+
}
|
|
6549
|
+
} catch (err) {
|
|
6550
|
+
if (err instanceof JerkSignInRequiredError) {
|
|
6551
|
+
throw new AuthStepUpError(challenge.scope, options.name);
|
|
6552
|
+
}
|
|
6553
|
+
throw err;
|
|
6554
|
+
}
|
|
6555
|
+
throw new AuthStepUpError(challenge.scope, options.name);
|
|
6556
|
+
}
|
|
6557
|
+
const result = await source.getToken(name, serverUrl);
|
|
6558
|
+
if (result) {
|
|
6559
|
+
currentToken = result.accessToken;
|
|
6560
|
+
return;
|
|
6561
|
+
}
|
|
6562
|
+
throw new McpOAuthAuthorizationRequiredError();
|
|
6563
|
+
}
|
|
6564
|
+
};
|
|
6565
|
+
}
|
|
6566
|
+
function createPiAuthProvider(options) {
|
|
6399
6567
|
const { name, serverUrl, source, oauth, platform: platform2 } = options;
|
|
6400
6568
|
const store = new SourceBackedStore(source, name, serverUrl);
|
|
6401
6569
|
let refreshing;
|
|
@@ -7841,6 +8009,53 @@ function buildServerInfo(discoverResult2) {
|
|
|
7841
8009
|
instructions: discoverResult2?.instructions
|
|
7842
8010
|
};
|
|
7843
8011
|
}
|
|
8012
|
+
function buildStatelessMethods(requestFn, discoverResult2) {
|
|
8013
|
+
let serverInfoCache;
|
|
8014
|
+
return {
|
|
8015
|
+
async info() {
|
|
8016
|
+
if (serverInfoCache) return serverInfoCache;
|
|
8017
|
+
serverInfoCache = buildServerInfo(discoverResult2);
|
|
8018
|
+
return serverInfoCache;
|
|
8019
|
+
},
|
|
8020
|
+
async listTools() {
|
|
8021
|
+
return (await requestFn("tools/list", {}))?.tools ?? [];
|
|
8022
|
+
},
|
|
8023
|
+
async callTool(name, args2, _callOptions) {
|
|
8024
|
+
const params = {
|
|
8025
|
+
name,
|
|
8026
|
+
arguments: args2 ?? {}
|
|
8027
|
+
};
|
|
8028
|
+
if (_callOptions?.inputResponses) {
|
|
8029
|
+
params._inputResponses = _callOptions.inputResponses;
|
|
8030
|
+
}
|
|
8031
|
+
const result = await requestFn("tools/call", params);
|
|
8032
|
+
if ((result.resultType ?? "complete") === "input_required") {
|
|
8033
|
+
throw new InputRequiredError(
|
|
8034
|
+
JSON.stringify({ name, arguments: args2 ?? {}, result }, null, 2)
|
|
8035
|
+
);
|
|
8036
|
+
}
|
|
8037
|
+
return result;
|
|
8038
|
+
},
|
|
8039
|
+
async listResources() {
|
|
8040
|
+
return (await requestFn("resources/list", {}))?.resources ?? [];
|
|
8041
|
+
},
|
|
8042
|
+
async listResourceTemplates() {
|
|
8043
|
+
return (await requestFn("resources/templates/list", {}))?.resourceTemplates ?? [];
|
|
8044
|
+
},
|
|
8045
|
+
async readResource(uri) {
|
|
8046
|
+
return await requestFn("resources/read", { uri });
|
|
8047
|
+
},
|
|
8048
|
+
async listPrompts() {
|
|
8049
|
+
return (await requestFn("prompts/list", {}))?.prompts ?? [];
|
|
8050
|
+
},
|
|
8051
|
+
async getPrompt(name, args2) {
|
|
8052
|
+
return await requestFn("prompts/get", {
|
|
8053
|
+
name,
|
|
8054
|
+
arguments: args2 ?? {}
|
|
8055
|
+
});
|
|
8056
|
+
}
|
|
8057
|
+
};
|
|
8058
|
+
}
|
|
7844
8059
|
async function createStatelessConnection(options) {
|
|
7845
8060
|
const {
|
|
7846
8061
|
url,
|
|
@@ -7944,11 +8159,16 @@ async function createStatelessConnection(options) {
|
|
|
7944
8159
|
protocolVersion: negotiatedVersion ?? STATELESS_PROTOCOL_VERSION,
|
|
7945
8160
|
clientInfo
|
|
7946
8161
|
};
|
|
8162
|
+
const cleanParams = { ...params };
|
|
8163
|
+
if (cleanParams._inputResponses) {
|
|
8164
|
+
meta.inputResponses = cleanParams._inputResponses;
|
|
8165
|
+
delete cleanParams._inputResponses;
|
|
8166
|
+
}
|
|
7947
8167
|
const body = JSON.stringify({
|
|
7948
8168
|
jsonrpc: "2.0",
|
|
7949
8169
|
id,
|
|
7950
8170
|
method,
|
|
7951
|
-
params: { ...
|
|
8171
|
+
params: { ...cleanParams, _meta: meta }
|
|
7952
8172
|
});
|
|
7953
8173
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
7954
8174
|
const { res, token } = await doFetch(method, params, body);
|
|
@@ -7977,28 +8197,9 @@ async function createStatelessConnection(options) {
|
|
|
7977
8197
|
);
|
|
7978
8198
|
}
|
|
7979
8199
|
negotiatedVersion = STATELESS_PROTOCOL_VERSION;
|
|
7980
|
-
|
|
8200
|
+
const extra = buildStatelessMethods(request, discoverResult2);
|
|
7981
8201
|
return {
|
|
7982
|
-
|
|
7983
|
-
if (serverInfoCache) return serverInfoCache;
|
|
7984
|
-
serverInfoCache = buildServerInfo(discoverResult2);
|
|
7985
|
-
return serverInfoCache;
|
|
7986
|
-
},
|
|
7987
|
-
async listTools() {
|
|
7988
|
-
return (await request("tools/list", {}))?.tools ?? [];
|
|
7989
|
-
},
|
|
7990
|
-
async callTool(name, args2, _callOptions) {
|
|
7991
|
-
const result = await request("tools/call", {
|
|
7992
|
-
name,
|
|
7993
|
-
arguments: args2 ?? {}
|
|
7994
|
-
});
|
|
7995
|
-
if ((result.resultType ?? "complete") === "input_required") {
|
|
7996
|
-
throw new InputRequiredError(
|
|
7997
|
-
JSON.stringify({ name, arguments: args2 ?? {}, result }, null, 2)
|
|
7998
|
-
);
|
|
7999
|
-
}
|
|
8000
|
-
return result;
|
|
8001
|
-
},
|
|
8202
|
+
...extra,
|
|
8002
8203
|
async close() {
|
|
8003
8204
|
}
|
|
8004
8205
|
};
|
|
@@ -8048,7 +8249,9 @@ async function createPiConnection(options) {
|
|
|
8048
8249
|
const clientOptions = {
|
|
8049
8250
|
name: "mcpcat",
|
|
8050
8251
|
version: options.version ?? "0.0.0",
|
|
8051
|
-
requestTimeoutMs: options.timeoutMs
|
|
8252
|
+
requestTimeoutMs: options.timeoutMs,
|
|
8253
|
+
capabilities: { elicitation: {} },
|
|
8254
|
+
roots: options.roots
|
|
8052
8255
|
};
|
|
8053
8256
|
const client = new McpClient(clientOptions);
|
|
8054
8257
|
const transport = buildTransport(options);
|
|
@@ -8094,7 +8297,32 @@ async function createPiConnection(options) {
|
|
|
8094
8297
|
inner?.(p);
|
|
8095
8298
|
};
|
|
8096
8299
|
}
|
|
8097
|
-
|
|
8300
|
+
const callArgs = callOptions?.inputResponses ? { ...args2, _meta: { inputResponses: callOptions.inputResponses } } : args2;
|
|
8301
|
+
return client.callTool(name, callArgs, o);
|
|
8302
|
+
},
|
|
8303
|
+
async listResources() {
|
|
8304
|
+
return client.listResources(reqOpts2());
|
|
8305
|
+
},
|
|
8306
|
+
async listResourceTemplates() {
|
|
8307
|
+
return client.listResourceTemplates(reqOpts2());
|
|
8308
|
+
},
|
|
8309
|
+
async readResource(uri) {
|
|
8310
|
+
return client.readResource(uri, reqOpts2());
|
|
8311
|
+
},
|
|
8312
|
+
async listPrompts() {
|
|
8313
|
+
const result = await client.request(
|
|
8314
|
+
"prompts/list",
|
|
8315
|
+
{},
|
|
8316
|
+
reqOpts2()
|
|
8317
|
+
);
|
|
8318
|
+
return result?.prompts ?? [];
|
|
8319
|
+
},
|
|
8320
|
+
async getPrompt(name, args2) {
|
|
8321
|
+
return client.request(
|
|
8322
|
+
"prompts/get",
|
|
8323
|
+
{ name, arguments: args2 ?? {} },
|
|
8324
|
+
reqOpts2()
|
|
8325
|
+
);
|
|
8098
8326
|
},
|
|
8099
8327
|
async close() {
|
|
8100
8328
|
await client.close();
|
|
@@ -8112,6 +8340,192 @@ async function createPiConnection(options) {
|
|
|
8112
8340
|
}
|
|
8113
8341
|
}
|
|
8114
8342
|
|
|
8343
|
+
// src/client/stdio-stateless.ts
|
|
8344
|
+
var PROTOCOL_VERSION = "2026-07-28";
|
|
8345
|
+
function createStdioJsonRpc(proc, platform2, options) {
|
|
8346
|
+
const encoder2 = new TextEncoder();
|
|
8347
|
+
const decoder3 = new TextDecoder();
|
|
8348
|
+
let nextId2 = 1;
|
|
8349
|
+
let readBuffer = "";
|
|
8350
|
+
const reader = proc.stdout.getReader();
|
|
8351
|
+
let negotiatedVersion;
|
|
8352
|
+
const { version = "0.0.0", timeoutMs = 6e4, signal: externalSignal, verbose } = options;
|
|
8353
|
+
async function readLine2() {
|
|
8354
|
+
while (true) {
|
|
8355
|
+
const nlIdx = readBuffer.indexOf("\n");
|
|
8356
|
+
if (nlIdx >= 0) {
|
|
8357
|
+
const line = readBuffer.slice(0, nlIdx).trim();
|
|
8358
|
+
readBuffer = readBuffer.slice(nlIdx + 1);
|
|
8359
|
+
if (line) return line;
|
|
8360
|
+
continue;
|
|
8361
|
+
}
|
|
8362
|
+
const { done, value } = await reader.read();
|
|
8363
|
+
if (done) throw new Error("stdio server closed unexpectedly");
|
|
8364
|
+
readBuffer += decoder3.decode(value, { stream: true });
|
|
8365
|
+
}
|
|
8366
|
+
}
|
|
8367
|
+
async function send(msg) {
|
|
8368
|
+
const writer = proc.stdin.getWriter();
|
|
8369
|
+
try {
|
|
8370
|
+
await writer.write(encoder2.encode(`${msg}
|
|
8371
|
+
`));
|
|
8372
|
+
} finally {
|
|
8373
|
+
writer.releaseLock();
|
|
8374
|
+
}
|
|
8375
|
+
}
|
|
8376
|
+
async function request(method, params) {
|
|
8377
|
+
const id = nextId2++;
|
|
8378
|
+
const meta = {
|
|
8379
|
+
protocolVersion: negotiatedVersion ?? PROTOCOL_VERSION,
|
|
8380
|
+
clientInfo: { name: "mcpcat", version }
|
|
8381
|
+
};
|
|
8382
|
+
const cleanParams = { ...params };
|
|
8383
|
+
if (cleanParams._inputResponses) {
|
|
8384
|
+
meta.inputResponses = cleanParams._inputResponses;
|
|
8385
|
+
delete cleanParams._inputResponses;
|
|
8386
|
+
}
|
|
8387
|
+
const body = JSON.stringify({
|
|
8388
|
+
jsonrpc: "2.0",
|
|
8389
|
+
id,
|
|
8390
|
+
method,
|
|
8391
|
+
params: { ...cleanParams, _meta: meta }
|
|
8392
|
+
});
|
|
8393
|
+
if (verbose?.enabled) verbose.request(method, body);
|
|
8394
|
+
await send(body);
|
|
8395
|
+
const deadline = Date.now() + timeoutMs;
|
|
8396
|
+
while (Date.now() < deadline) {
|
|
8397
|
+
const remaining = deadline - Date.now();
|
|
8398
|
+
if (externalSignal?.aborted) throw new McpTimeoutError2(method, timeoutMs);
|
|
8399
|
+
const linePromise = readLine2();
|
|
8400
|
+
const timeoutPromise = new Promise(
|
|
8401
|
+
(_, reject) => setTimeout(() => reject(new McpTimeoutError2(method, timeoutMs)), remaining)
|
|
8402
|
+
);
|
|
8403
|
+
const line = await Promise.race([linePromise, timeoutPromise]);
|
|
8404
|
+
const parsed = JSON.parse(line);
|
|
8405
|
+
if (verbose?.enabled) verbose.response(line);
|
|
8406
|
+
if (parsed.id === id) {
|
|
8407
|
+
if (parsed.error) {
|
|
8408
|
+
throw new StatelessRpcError(parsed.error.code, parsed.error.message, parsed.error.data);
|
|
8409
|
+
}
|
|
8410
|
+
return parsed.result;
|
|
8411
|
+
}
|
|
8412
|
+
}
|
|
8413
|
+
throw new McpTimeoutError2(method, timeoutMs);
|
|
8414
|
+
}
|
|
8415
|
+
return {
|
|
8416
|
+
request,
|
|
8417
|
+
close() {
|
|
8418
|
+
try {
|
|
8419
|
+
reader.releaseLock();
|
|
8420
|
+
} catch {
|
|
8421
|
+
}
|
|
8422
|
+
}
|
|
8423
|
+
};
|
|
8424
|
+
}
|
|
8425
|
+
function buildServerInfo2(discoverResult2) {
|
|
8426
|
+
return {
|
|
8427
|
+
name: discoverResult2?.serverInfo?.name,
|
|
8428
|
+
version: discoverResult2?.serverInfo?.version,
|
|
8429
|
+
title: discoverResult2?.serverInfo?.title,
|
|
8430
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
8431
|
+
capabilities: discoverResult2?.capabilities ?? {},
|
|
8432
|
+
instructions: discoverResult2?.instructions
|
|
8433
|
+
};
|
|
8434
|
+
}
|
|
8435
|
+
function buildConnectionMethods(rpc, discoverResult2, proc) {
|
|
8436
|
+
return {
|
|
8437
|
+
async info() {
|
|
8438
|
+
return buildServerInfo2(discoverResult2);
|
|
8439
|
+
},
|
|
8440
|
+
async listTools() {
|
|
8441
|
+
return (await rpc.request("tools/list", {}))?.tools ?? [];
|
|
8442
|
+
},
|
|
8443
|
+
async callTool(name, args2, callOptions) {
|
|
8444
|
+
const params = { name, arguments: args2 ?? {} };
|
|
8445
|
+
if (callOptions?.inputResponses) {
|
|
8446
|
+
params._inputResponses = callOptions.inputResponses;
|
|
8447
|
+
}
|
|
8448
|
+
const result = await rpc.request("tools/call", params);
|
|
8449
|
+
if ((result.resultType ?? "complete") === "input_required") {
|
|
8450
|
+
throw new InputRequiredError(
|
|
8451
|
+
JSON.stringify({ name, arguments: args2 ?? {}, result }, null, 2)
|
|
8452
|
+
);
|
|
8453
|
+
}
|
|
8454
|
+
return result;
|
|
8455
|
+
},
|
|
8456
|
+
async listResources() {
|
|
8457
|
+
return (await rpc.request("resources/list", {}))?.resources ?? [];
|
|
8458
|
+
},
|
|
8459
|
+
async listResourceTemplates() {
|
|
8460
|
+
return (await rpc.request("resources/templates/list", {}))?.resourceTemplates ?? [];
|
|
8461
|
+
},
|
|
8462
|
+
async readResource(uri) {
|
|
8463
|
+
return await rpc.request("resources/read", { uri });
|
|
8464
|
+
},
|
|
8465
|
+
async listPrompts() {
|
|
8466
|
+
return (await rpc.request("prompts/list", {}))?.prompts ?? [];
|
|
8467
|
+
},
|
|
8468
|
+
async getPrompt(name, args2) {
|
|
8469
|
+
return await rpc.request("prompts/get", { name, arguments: args2 ?? {} });
|
|
8470
|
+
},
|
|
8471
|
+
async close() {
|
|
8472
|
+
rpc.close();
|
|
8473
|
+
try {
|
|
8474
|
+
proc.kill("SIGTERM");
|
|
8475
|
+
} catch {
|
|
8476
|
+
}
|
|
8477
|
+
}
|
|
8478
|
+
};
|
|
8479
|
+
}
|
|
8480
|
+
async function createStdioStatelessConnection(options) {
|
|
8481
|
+
const inheritedEnv = {
|
|
8482
|
+
...options.platform.env,
|
|
8483
|
+
...options.env
|
|
8484
|
+
};
|
|
8485
|
+
const proc = await options.platform.spawn({
|
|
8486
|
+
argv: [options.command, ...options.args ?? []],
|
|
8487
|
+
env: inheritedEnv,
|
|
8488
|
+
cwd: options.cwd
|
|
8489
|
+
});
|
|
8490
|
+
void readStderr(proc, options.verbose);
|
|
8491
|
+
const rpc = createStdioJsonRpc(proc, options.platform, {
|
|
8492
|
+
version: options.version,
|
|
8493
|
+
timeoutMs: options.timeoutMs,
|
|
8494
|
+
signal: options.signal,
|
|
8495
|
+
verbose: options.verbose
|
|
8496
|
+
});
|
|
8497
|
+
const discoverRaw = await rpc.request("server/discover", {
|
|
8498
|
+
protocolVersion: PROTOCOL_VERSION
|
|
8499
|
+
});
|
|
8500
|
+
const discoverResult2 = discoverRaw;
|
|
8501
|
+
const supported = (discoverResult2?.supportedVersions ?? []).filter(
|
|
8502
|
+
(v) => typeof v === "string"
|
|
8503
|
+
);
|
|
8504
|
+
if (!supported.includes(PROTOCOL_VERSION)) {
|
|
8505
|
+
proc.kill("SIGTERM");
|
|
8506
|
+
throw new Error(
|
|
8507
|
+
`Server does not support ${PROTOCOL_VERSION} (advertised: ${supported.join(", ") || "none"})`
|
|
8508
|
+
);
|
|
8509
|
+
}
|
|
8510
|
+
return buildConnectionMethods(rpc, discoverResult2, proc);
|
|
8511
|
+
}
|
|
8512
|
+
async function readStderr(proc, verbose) {
|
|
8513
|
+
if (!verbose?.enabled) return;
|
|
8514
|
+
const reader = proc.stderr.getReader();
|
|
8515
|
+
const decoder3 = new TextDecoder();
|
|
8516
|
+
try {
|
|
8517
|
+
while (true) {
|
|
8518
|
+
const { done, value } = await reader.read();
|
|
8519
|
+
if (done) break;
|
|
8520
|
+
const chunk = decoder3.decode(value, { stream: true });
|
|
8521
|
+
for (const line of chunk.split("\n")) {
|
|
8522
|
+
if (line.trim()) verbose.server(line);
|
|
8523
|
+
}
|
|
8524
|
+
}
|
|
8525
|
+
} catch {
|
|
8526
|
+
}
|
|
8527
|
+
}
|
|
8528
|
+
|
|
8115
8529
|
// src/client/connect.ts
|
|
8116
8530
|
async function connect(platform2, options) {
|
|
8117
8531
|
const shared = {
|
|
@@ -8122,11 +8536,32 @@ async function connect(platform2, options) {
|
|
|
8122
8536
|
platform: platform2
|
|
8123
8537
|
};
|
|
8124
8538
|
if (options.command) {
|
|
8539
|
+
if (!options.protocolVersion || options.protocolVersion.startsWith("2026")) {
|
|
8540
|
+
try {
|
|
8541
|
+
return await createStdioStatelessConnection({
|
|
8542
|
+
command: options.command,
|
|
8543
|
+
args: options.args,
|
|
8544
|
+
env: options.env,
|
|
8545
|
+
cwd: options.cwd,
|
|
8546
|
+
platform: platform2,
|
|
8547
|
+
version: options.version,
|
|
8548
|
+
timeoutMs: options.timeoutMs,
|
|
8549
|
+
signal: options.signal,
|
|
8550
|
+
verbose: options.verbose
|
|
8551
|
+
});
|
|
8552
|
+
} catch (err) {
|
|
8553
|
+
const isMethodNotFound = err instanceof StatelessRpcError && err.code === -32601;
|
|
8554
|
+
const isUnsupported = err instanceof Error && err.message.includes("does not support");
|
|
8555
|
+
if (options.protocolVersion?.startsWith("2026")) throw err;
|
|
8556
|
+
if (!isMethodNotFound && !isUnsupported) throw err;
|
|
8557
|
+
}
|
|
8558
|
+
}
|
|
8125
8559
|
return createPiConnection({
|
|
8126
8560
|
command: options.command,
|
|
8127
8561
|
args: options.args,
|
|
8128
8562
|
env: options.env,
|
|
8129
8563
|
cwd: options.cwd,
|
|
8564
|
+
roots: options.roots,
|
|
8130
8565
|
...shared
|
|
8131
8566
|
});
|
|
8132
8567
|
}
|
|
@@ -8169,6 +8604,102 @@ async function connect(platform2, options) {
|
|
|
8169
8604
|
}
|
|
8170
8605
|
}
|
|
8171
8606
|
|
|
8607
|
+
// src/client/elicit.ts
|
|
8608
|
+
function readLine(platform2) {
|
|
8609
|
+
return new Promise((resolve, reject) => {
|
|
8610
|
+
const reader = platform2.stdin.getReader();
|
|
8611
|
+
const chunks = [];
|
|
8612
|
+
function pump() {
|
|
8613
|
+
reader.read().then(({ done, value }) => {
|
|
8614
|
+
if (done) {
|
|
8615
|
+
reader.releaseLock();
|
|
8616
|
+
resolve(new TextDecoder().decode(concatChunks(chunks)).split("\n")[0] ?? "");
|
|
8617
|
+
return;
|
|
8618
|
+
}
|
|
8619
|
+
chunks.push(value);
|
|
8620
|
+
const text = new TextDecoder().decode(concatChunks(chunks));
|
|
8621
|
+
if (text.includes("\n")) {
|
|
8622
|
+
reader.releaseLock();
|
|
8623
|
+
resolve(text.split("\n")[0]);
|
|
8624
|
+
return;
|
|
8625
|
+
}
|
|
8626
|
+
pump();
|
|
8627
|
+
}, reject);
|
|
8628
|
+
}
|
|
8629
|
+
pump();
|
|
8630
|
+
});
|
|
8631
|
+
}
|
|
8632
|
+
function concatChunks(chunks) {
|
|
8633
|
+
const total = chunks.reduce((s, c) => s + c.length, 0);
|
|
8634
|
+
const merged = new Uint8Array(total);
|
|
8635
|
+
let offset = 0;
|
|
8636
|
+
for (const c of chunks) {
|
|
8637
|
+
merged.set(c, offset);
|
|
8638
|
+
offset += c.length;
|
|
8639
|
+
}
|
|
8640
|
+
return merged;
|
|
8641
|
+
}
|
|
8642
|
+
function coerceValue(raw, type) {
|
|
8643
|
+
if (!type || type === "string") return raw;
|
|
8644
|
+
if (type === "integer") return Number.parseInt(raw, 10);
|
|
8645
|
+
if (type === "number") return Number.parseFloat(raw);
|
|
8646
|
+
if (type === "boolean") return raw === "true" || raw === "1" || raw === "yes";
|
|
8647
|
+
return raw;
|
|
8648
|
+
}
|
|
8649
|
+
async function promptForSchema(platform2, schema) {
|
|
8650
|
+
const answers = {};
|
|
8651
|
+
const properties = schema.properties ?? {};
|
|
8652
|
+
const required = new Set(schema.required ?? []);
|
|
8653
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
8654
|
+
const req = required.has(key) ? " (required)" : "";
|
|
8655
|
+
const desc = prop.description ? ` - ${prop.description}` : "";
|
|
8656
|
+
const def = prop.default !== void 0 ? ` [${String(prop.default)}]` : "";
|
|
8657
|
+
const choices = prop.enum ? ` (${prop.enum.join("|")})` : "";
|
|
8658
|
+
await platform2.write(2, `${key}${req}${desc}${choices}${def}: `);
|
|
8659
|
+
const line = await readLine(platform2);
|
|
8660
|
+
const value = line.trim();
|
|
8661
|
+
if (value === "" && prop.default !== void 0) {
|
|
8662
|
+
answers[key] = prop.default;
|
|
8663
|
+
} else if (value !== "") {
|
|
8664
|
+
answers[key] = coerceValue(value, prop.type);
|
|
8665
|
+
}
|
|
8666
|
+
}
|
|
8667
|
+
return answers;
|
|
8668
|
+
}
|
|
8669
|
+
function parseInputRequest(result) {
|
|
8670
|
+
const ir = result.inputRequest;
|
|
8671
|
+
if (!ir && !result.inputRequests) return void 0;
|
|
8672
|
+
if (ir) return ir;
|
|
8673
|
+
const reqs = result.inputRequests;
|
|
8674
|
+
if (Array.isArray(reqs) && reqs.length > 0) return reqs[0];
|
|
8675
|
+
return void 0;
|
|
8676
|
+
}
|
|
8677
|
+
async function handleElicitation(platform2, inputRequest, isTty, respondData) {
|
|
8678
|
+
if (inputRequest.url) {
|
|
8679
|
+
if (isTty) {
|
|
8680
|
+
await platform2.write(2, `Open this URL to continue: ${inputRequest.url}
|
|
8681
|
+
`);
|
|
8682
|
+
await platform2.write(2, "Press Enter when done...");
|
|
8683
|
+
await readLine(platform2);
|
|
8684
|
+
return { answers: {} };
|
|
8685
|
+
}
|
|
8686
|
+
return { exitCode: 6 };
|
|
8687
|
+
}
|
|
8688
|
+
if (respondData) {
|
|
8689
|
+
return { answers: respondData };
|
|
8690
|
+
}
|
|
8691
|
+
if (!isTty) {
|
|
8692
|
+
return { exitCode: 6 };
|
|
8693
|
+
}
|
|
8694
|
+
const schema = inputRequest.schema ?? { type: "object", properties: {} };
|
|
8695
|
+
if (inputRequest.message) {
|
|
8696
|
+
await platform2.write(2, `${inputRequest.message}
|
|
8697
|
+
`);
|
|
8698
|
+
}
|
|
8699
|
+
const answers = await promptForSchema(platform2, schema);
|
|
8700
|
+
return { answers };
|
|
8701
|
+
}
|
|
8702
|
+
|
|
8172
8703
|
// src/client/targets.ts
|
|
8173
8704
|
function splitShellStyle(input) {
|
|
8174
8705
|
const result = [];
|
|
@@ -8578,10 +9109,19 @@ function buildShellGlobal(platform2) {
|
|
|
8578
9109
|
}
|
|
8579
9110
|
};
|
|
8580
9111
|
}
|
|
9112
|
+
var DEFAULT_MEMORY_LIMIT_MIB = 256;
|
|
9113
|
+
var MAX_MEMORY_LIMIT_MIB = 1024;
|
|
8581
9114
|
async function runCode(targets, source, options) {
|
|
8582
9115
|
const { platform: platform2, json } = options;
|
|
8583
9116
|
const { code: code2, options: sourceOptions } = parseCodemodeSource(source);
|
|
8584
|
-
const
|
|
9117
|
+
const callerTimeoutMs = options.timeoutMs ?? 3e5;
|
|
9118
|
+
const scriptTimeoutMs = sourceOptions.timeoutMs;
|
|
9119
|
+
const timeoutMs = scriptTimeoutMs !== void 0 ? Math.min(callerTimeoutMs, scriptTimeoutMs) : callerTimeoutMs;
|
|
9120
|
+
const memoryLimitMiB = Math.min(
|
|
9121
|
+
options.memoryLimitMiB ?? DEFAULT_MEMORY_LIMIT_MIB,
|
|
9122
|
+
MAX_MEMORY_LIMIT_MIB
|
|
9123
|
+
);
|
|
9124
|
+
const memoryLimitBytes = memoryLimitMiB * 1024 * 1024;
|
|
8585
9125
|
const { connections, allDeclaredTools, namespaces } = await collectTools(targets);
|
|
8586
9126
|
const searchDocs = buildSearchDocs(allDeclaredTools, namespaces);
|
|
8587
9127
|
const stateDir2 = options.stateDir ?? `${platform2.env.XDG_STATE_HOME ?? `${platform2.env.HOME ?? "."}/.local/state`}/mcpcat`;
|
|
@@ -8591,7 +9131,7 @@ async function runCode(targets, source, options) {
|
|
|
8591
9131
|
...buildDiscoveryGlobals(allDeclaredTools, namespaces, searchDocs),
|
|
8592
9132
|
...options.allowShell ? [buildShellGlobal(platform2)] : []
|
|
8593
9133
|
];
|
|
8594
|
-
const sandbox = await createSandbox(platform2, toolImpls, globals, timeoutMs);
|
|
9134
|
+
const sandbox = await createSandbox(platform2, toolImpls, globals, timeoutMs, memoryLimitBytes);
|
|
8595
9135
|
try {
|
|
8596
9136
|
const result = await sandbox.execute(code2, { store });
|
|
8597
9137
|
if (result.ok) {
|
|
@@ -8635,21 +9175,25 @@ async function formatOutput(platform2, result, json) {
|
|
|
8635
9175
|
const isTimeout = !result.ok && result.error && result.error.kind === "timeout";
|
|
8636
9176
|
return result.ok ? 0 : isTimeout ? 124 : 1;
|
|
8637
9177
|
}
|
|
8638
|
-
async function createSandbox(platform2, tools, globals, timeoutMs) {
|
|
9178
|
+
async function createSandbox(platform2, tools, globals, timeoutMs, memoryLimitBytes) {
|
|
8639
9179
|
const isKernel2 = platform2.packageDir !== void 0;
|
|
8640
9180
|
if (isKernel2) {
|
|
8641
9181
|
const { InlineCodemodeSandbox: InlineCodemodeSandbox2 } = await Promise.resolve().then(() => (init_portable(), portable_exports));
|
|
8642
9182
|
const wasm = await loadWasmKernel(platform2);
|
|
8643
|
-
return new InlineCodemodeSandbox2({ tools, globals, timeoutMs, wasm });
|
|
9183
|
+
return new InlineCodemodeSandbox2({ tools, globals, timeoutMs, wasm, memoryLimitBytes });
|
|
8644
9184
|
}
|
|
8645
9185
|
const { CodemodeSandbox: CodemodeSandbox2, loadQuickJSWasm: loadQuickJSWasm2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
|
|
8646
|
-
const
|
|
9186
|
+
const getSeaDir = globalThis.__mcpcat_get_sea_asset_dir;
|
|
9187
|
+
const seaDir = getSeaDir?.();
|
|
9188
|
+
const wasmPath = seaDir ? `${seaDir}/quickjs.wasm` : await resolveWasmPath(platform2);
|
|
9189
|
+
const workerUrl = seaDir ? new URL(`file://${seaDir}/codemode-worker.mjs`) : new URL("./codemode-worker.mjs", import.meta.url);
|
|
8647
9190
|
return new CodemodeSandbox2({
|
|
8648
9191
|
tools,
|
|
8649
9192
|
globals,
|
|
8650
9193
|
timeoutMs,
|
|
9194
|
+
memoryLimitBytes,
|
|
8651
9195
|
wasm: loadQuickJSWasm2(wasmPath),
|
|
8652
|
-
workerUrl
|
|
9196
|
+
workerUrl
|
|
8653
9197
|
});
|
|
8654
9198
|
}
|
|
8655
9199
|
async function resolveWasmPath(platform2) {
|
|
@@ -8723,6 +9267,14 @@ function containsPlaceholders(value) {
|
|
|
8723
9267
|
function expandPlaceholders(value, env) {
|
|
8724
9268
|
return value.replace(PLACEHOLDER, (_, name) => env[name] ?? "");
|
|
8725
9269
|
}
|
|
9270
|
+
function missingPlaceholders(value, env) {
|
|
9271
|
+
const missing = [];
|
|
9272
|
+
for (const m of value.matchAll(PLACEHOLDER)) {
|
|
9273
|
+
const name = m[1];
|
|
9274
|
+
if (env[name] === void 0 && !missing.includes(name)) missing.push(name);
|
|
9275
|
+
}
|
|
9276
|
+
return missing;
|
|
9277
|
+
}
|
|
8726
9278
|
function expandPlaceholdersStrict(value, env) {
|
|
8727
9279
|
let hasUndefined = false;
|
|
8728
9280
|
const result = value.replace(PLACEHOLDER, (_, name) => {
|
|
@@ -8735,6 +9287,20 @@ function expandPlaceholdersStrict(value, env) {
|
|
|
8735
9287
|
if (hasUndefined) return void 0;
|
|
8736
9288
|
return result;
|
|
8737
9289
|
}
|
|
9290
|
+
function expandHeaders(headers, env, warn) {
|
|
9291
|
+
if (!headers) return void 0;
|
|
9292
|
+
const result = {};
|
|
9293
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
9294
|
+
const missing = missingPlaceholders(v, env);
|
|
9295
|
+
if (missing.length > 0) {
|
|
9296
|
+
if (warn)
|
|
9297
|
+
warn(`header "${k}": unset variable${missing.length > 1 ? "s" : ""} ${missing.join(", ")}`);
|
|
9298
|
+
continue;
|
|
9299
|
+
}
|
|
9300
|
+
result[k] = expandPlaceholders(v, env);
|
|
9301
|
+
}
|
|
9302
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
9303
|
+
}
|
|
8738
9304
|
|
|
8739
9305
|
// src/config/parse.ts
|
|
8740
9306
|
var SERVER_NAME = /^[A-Za-z0-9_-]+$/;
|
|
@@ -8925,7 +9491,12 @@ async function readFile2(fs, path) {
|
|
|
8925
9491
|
try {
|
|
8926
9492
|
const data = await fs.readFile(path);
|
|
8927
9493
|
return new TextDecoder().decode(data);
|
|
8928
|
-
} catch {
|
|
9494
|
+
} catch (err) {
|
|
9495
|
+
if (err.code === "EACCES") {
|
|
9496
|
+
const e = new Error(`permission denied: ${path}`);
|
|
9497
|
+
e.code = "EACCES";
|
|
9498
|
+
throw e;
|
|
9499
|
+
}
|
|
8929
9500
|
return void 0;
|
|
8930
9501
|
}
|
|
8931
9502
|
}
|
|
@@ -9090,6 +9661,13 @@ async function removeStaleLock(fs, lockPath) {
|
|
|
9090
9661
|
async function acquireLock(fs, configPath) {
|
|
9091
9662
|
const lockPath = `${configPath}.lock`;
|
|
9092
9663
|
const start = Date.now();
|
|
9664
|
+
const parentDir = lockPath.slice(0, lockPath.lastIndexOf("/"));
|
|
9665
|
+
if (parentDir) {
|
|
9666
|
+
try {
|
|
9667
|
+
await fs.mkdir(parentDir);
|
|
9668
|
+
} catch {
|
|
9669
|
+
}
|
|
9670
|
+
}
|
|
9093
9671
|
while (true) {
|
|
9094
9672
|
try {
|
|
9095
9673
|
await fs.mkdirExclusive(lockPath);
|
|
@@ -9145,13 +9723,21 @@ async function editMcpServers(fs, path, edit) {
|
|
|
9145
9723
|
const dir = path.slice(0, path.lastIndexOf("/"));
|
|
9146
9724
|
if (dir) {
|
|
9147
9725
|
try {
|
|
9148
|
-
await fs.mkdir(dir);
|
|
9726
|
+
await fs.mkdir(dir);
|
|
9727
|
+
} catch {
|
|
9728
|
+
}
|
|
9729
|
+
}
|
|
9730
|
+
const tmpPath = `${path}.tmp.${Date.now()}`;
|
|
9731
|
+
await fs.writeFile(tmpPath, output);
|
|
9732
|
+
try {
|
|
9733
|
+
await fs.rename(tmpPath, path);
|
|
9734
|
+
} catch (renameErr) {
|
|
9735
|
+
try {
|
|
9736
|
+
if (fs.unlink) await fs.unlink(tmpPath);
|
|
9149
9737
|
} catch {
|
|
9150
9738
|
}
|
|
9739
|
+
throw renameErr;
|
|
9151
9740
|
}
|
|
9152
|
-
const tmpPath = `${path}.tmp.${Date.now()}`;
|
|
9153
|
-
await fs.writeFile(tmpPath, output);
|
|
9154
|
-
await fs.rename(tmpPath, path);
|
|
9155
9741
|
return { changed: true };
|
|
9156
9742
|
} finally {
|
|
9157
9743
|
await release();
|
|
@@ -9528,6 +10114,31 @@ async function getCandidate(fs, stateDir2, id) {
|
|
|
9528
10114
|
}
|
|
9529
10115
|
|
|
9530
10116
|
// src/discovery/probe.ts
|
|
10117
|
+
var LOOPBACK_RE = /^(127\.\d+\.\d+\.\d+|\[?::1\]?|localhost)$/i;
|
|
10118
|
+
var LINK_LOCAL_RE = /^(169\.254\.|fe80:)/i;
|
|
10119
|
+
var PRIVATE_RE = /^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.|fc|fd)/i;
|
|
10120
|
+
var LOCALHOST_SUFFIX_RE = /\.localhost$/i;
|
|
10121
|
+
var SLICC_INTERNAL_RE = /^host\.slicc\.internal$/i;
|
|
10122
|
+
function isPrivateOrLoopback(hostname) {
|
|
10123
|
+
const bare = hostname.replace(/^\[|\]$/g, "");
|
|
10124
|
+
return LOOPBACK_RE.test(bare) || LINK_LOCAL_RE.test(bare) || PRIVATE_RE.test(bare) || LOCALHOST_SUFFIX_RE.test(bare) || SLICC_INTERNAL_RE.test(bare);
|
|
10125
|
+
}
|
|
10126
|
+
function isSameOrigin(base, candidate) {
|
|
10127
|
+
try {
|
|
10128
|
+
const a = new URL(base);
|
|
10129
|
+
const b = new URL(candidate);
|
|
10130
|
+
return a.origin === b.origin;
|
|
10131
|
+
} catch {
|
|
10132
|
+
return false;
|
|
10133
|
+
}
|
|
10134
|
+
}
|
|
10135
|
+
function isUserUrlPrivate(userUrl) {
|
|
10136
|
+
try {
|
|
10137
|
+
return isPrivateOrLoopback(new URL(userUrl).hostname);
|
|
10138
|
+
} catch {
|
|
10139
|
+
return false;
|
|
10140
|
+
}
|
|
10141
|
+
}
|
|
9531
10142
|
function isJsonContentType(ct) {
|
|
9532
10143
|
if (!ct) return false;
|
|
9533
10144
|
const lower = ct.toLowerCase();
|
|
@@ -9662,13 +10273,44 @@ async function probeWellKnown(fetcher, origin, page) {
|
|
|
9662
10273
|
}
|
|
9663
10274
|
return events;
|
|
9664
10275
|
}
|
|
9665
|
-
async function probeLinkHeaders(fetcher, targetUrl, origin, page) {
|
|
10276
|
+
async function probeLinkHeaders(fetcher, targetUrl, origin, page, userUrlIsPrivate) {
|
|
9666
10277
|
const events = [];
|
|
9667
10278
|
const targetResult = await probeUrl(fetcher, targetUrl);
|
|
9668
10279
|
if (!targetResult) return events;
|
|
9669
10280
|
const links = parseLinkHeaders(targetResult.headers);
|
|
9670
10281
|
for (const link of links) {
|
|
9671
10282
|
const resolved = new URL(link.url, targetUrl).href;
|
|
10283
|
+
if (!isSameOrigin(targetUrl, resolved)) {
|
|
10284
|
+
events.push(
|
|
10285
|
+
makeEvent(
|
|
10286
|
+
link.rel,
|
|
10287
|
+
origin,
|
|
10288
|
+
resolved,
|
|
10289
|
+
"link-header",
|
|
10290
|
+
page,
|
|
10291
|
+
"invalid",
|
|
10292
|
+
null,
|
|
10293
|
+
`skipped: cross-origin Link target (${new URL(resolved).origin})`
|
|
10294
|
+
)
|
|
10295
|
+
);
|
|
10296
|
+
continue;
|
|
10297
|
+
}
|
|
10298
|
+
const resolvedHost = new URL(resolved).hostname;
|
|
10299
|
+
if (!userUrlIsPrivate && isPrivateOrLoopback(resolvedHost)) {
|
|
10300
|
+
events.push(
|
|
10301
|
+
makeEvent(
|
|
10302
|
+
link.rel,
|
|
10303
|
+
origin,
|
|
10304
|
+
resolved,
|
|
10305
|
+
"link-header",
|
|
10306
|
+
page,
|
|
10307
|
+
"invalid",
|
|
10308
|
+
null,
|
|
10309
|
+
`skipped: private/loopback target (${resolvedHost})`
|
|
10310
|
+
)
|
|
10311
|
+
);
|
|
10312
|
+
continue;
|
|
10313
|
+
}
|
|
9672
10314
|
const classifier = link.rel === "ard" ? classifyArd : link.rel === "ai-catalog" ? classifyAiCatalog : null;
|
|
9673
10315
|
if (!classifier) continue;
|
|
9674
10316
|
const linkResult = await probeUrl(fetcher, resolved, "application/json");
|
|
@@ -9765,9 +10407,10 @@ async function probeDiscover(platform2, targetUrl) {
|
|
|
9765
10407
|
}
|
|
9766
10408
|
const page = targetUrl;
|
|
9767
10409
|
const fetcher = platform2.fetch;
|
|
10410
|
+
const userPrivate = isUserUrlPrivate(targetUrl);
|
|
9768
10411
|
const events = [
|
|
9769
10412
|
...await probeWellKnown(fetcher, origin, page),
|
|
9770
|
-
...await probeLinkHeaders(fetcher, targetUrl, origin, page),
|
|
10413
|
+
...await probeLinkHeaders(fetcher, targetUrl, origin, page, userPrivate),
|
|
9771
10414
|
...await probeMcp(fetcher, origin, page, targetUrl)
|
|
9772
10415
|
];
|
|
9773
10416
|
return { events };
|
|
@@ -10024,13 +10667,27 @@ function renderCallResultJson(result) {
|
|
|
10024
10667
|
return JSON.stringify(result, null, 2);
|
|
10025
10668
|
}
|
|
10026
10669
|
async function renderCallResult(platform2, result, options) {
|
|
10670
|
+
const r = options?.redact ?? ((s) => s);
|
|
10671
|
+
if (options?.outputPath) {
|
|
10672
|
+
const content = options.json ? renderCallResultJson(result) : renderCallResultText(result, options);
|
|
10673
|
+
await platform2.fs.writeFile(options.outputPath, content);
|
|
10674
|
+
if (result.isError) {
|
|
10675
|
+
await platform2.write(
|
|
10676
|
+
2,
|
|
10677
|
+
r(`${result.isError ? "(tool reported error)" : ""}
|
|
10678
|
+
`)
|
|
10679
|
+
);
|
|
10680
|
+
return 1;
|
|
10681
|
+
}
|
|
10682
|
+
return 0;
|
|
10683
|
+
}
|
|
10027
10684
|
if (options?.json) {
|
|
10028
10685
|
const json = renderCallResultJson(result);
|
|
10029
10686
|
if (result.isError) {
|
|
10030
10687
|
await platform2.write(
|
|
10031
10688
|
2,
|
|
10032
|
-
`${JSON.stringify({ error: { kind: "tool_error", message: json } }, null, 2)}
|
|
10033
|
-
`
|
|
10689
|
+
r(`${JSON.stringify({ error: { kind: "tool_error", message: json } }, null, 2)}
|
|
10690
|
+
`)
|
|
10034
10691
|
);
|
|
10035
10692
|
return 1;
|
|
10036
10693
|
}
|
|
@@ -10040,8 +10697,8 @@ async function renderCallResult(platform2, result, options) {
|
|
|
10040
10697
|
}
|
|
10041
10698
|
const text = renderCallResultText(result, options);
|
|
10042
10699
|
if (result.isError) {
|
|
10043
|
-
await platform2.write(2, `${text || "(tool reported error)"}
|
|
10044
|
-
`);
|
|
10700
|
+
await platform2.write(2, r(`${text || "(tool reported error)"}
|
|
10701
|
+
`));
|
|
10045
10702
|
return 1;
|
|
10046
10703
|
}
|
|
10047
10704
|
if (text) {
|
|
@@ -10058,13 +10715,33 @@ async function renderInfoJson(platform2, info) {
|
|
|
10058
10715
|
await platform2.write(1, `${JSON.stringify(info, null, 2)}
|
|
10059
10716
|
`);
|
|
10060
10717
|
}
|
|
10061
|
-
async function renderErrorJson(platform2, kind, message, code2, data) {
|
|
10062
|
-
const
|
|
10718
|
+
async function renderErrorJson(platform2, kind, message, code2, data, redact2) {
|
|
10719
|
+
const r = redact2 ?? ((s) => s);
|
|
10720
|
+
const errorObj = { kind, message: r(message) };
|
|
10063
10721
|
if (code2 !== void 0) errorObj.code = code2;
|
|
10064
10722
|
if (data !== void 0) errorObj.data = data;
|
|
10065
10723
|
await platform2.write(2, `${JSON.stringify({ error: errorObj }, null, 2)}
|
|
10066
10724
|
`);
|
|
10067
10725
|
}
|
|
10726
|
+
async function renderResourcesJson(platform2, resources, templates) {
|
|
10727
|
+
await platform2.write(
|
|
10728
|
+
1,
|
|
10729
|
+
`${JSON.stringify({ resources, resourceTemplates: templates }, null, 2)}
|
|
10730
|
+
`
|
|
10731
|
+
);
|
|
10732
|
+
}
|
|
10733
|
+
async function renderPromptsJson(platform2, prompts) {
|
|
10734
|
+
await platform2.write(1, `${JSON.stringify({ prompts }, null, 2)}
|
|
10735
|
+
`);
|
|
10736
|
+
}
|
|
10737
|
+
async function renderPromptResultJson(platform2, result) {
|
|
10738
|
+
await platform2.write(1, `${JSON.stringify(result, null, 2)}
|
|
10739
|
+
`);
|
|
10740
|
+
}
|
|
10741
|
+
async function renderReadResourceJson(platform2, result) {
|
|
10742
|
+
await platform2.write(1, `${JSON.stringify(result, null, 2)}
|
|
10743
|
+
`);
|
|
10744
|
+
}
|
|
10068
10745
|
|
|
10069
10746
|
// src/serve/catalog.ts
|
|
10070
10747
|
var NAME_RE = /^[A-Za-z][\w-]*$/;
|
|
@@ -10832,38 +11509,46 @@ function hasOwn(value, key) {
|
|
|
10832
11509
|
}
|
|
10833
11510
|
|
|
10834
11511
|
// src/serve/origin.ts
|
|
10835
|
-
|
|
10836
|
-
|
|
10837
|
-
try {
|
|
10838
|
-
const url = new URL(origin);
|
|
10839
|
-
const hostname = url.hostname;
|
|
10840
|
-
if (LOOPBACK_HOSTS.has(hostname)) return true;
|
|
10841
|
-
if (hostname === "::1") return true;
|
|
10842
|
-
if (hostname.endsWith(".localhost")) return true;
|
|
10843
|
-
return false;
|
|
10844
|
-
} catch {
|
|
10845
|
-
return false;
|
|
10846
|
-
}
|
|
11512
|
+
function isLoopbackBind(host) {
|
|
11513
|
+
return host === "127.0.0.1" || host === "localhost" || host === "::1" || host === "[::1]";
|
|
10847
11514
|
}
|
|
10848
|
-
function
|
|
10849
|
-
const
|
|
10850
|
-
|
|
10851
|
-
|
|
10852
|
-
if (
|
|
10853
|
-
|
|
11515
|
+
function buildServerOrigins(host, port) {
|
|
11516
|
+
const origins = /* @__PURE__ */ new Set();
|
|
11517
|
+
origins.add(`http://${host}:${port}`);
|
|
11518
|
+
origins.add(`http://${port}.kernel.localhost`);
|
|
11519
|
+
if (isLoopbackBind(host)) {
|
|
11520
|
+
origins.add(`http://localhost:${port}`);
|
|
11521
|
+
origins.add(`http://127.0.0.1:${port}`);
|
|
11522
|
+
origins.add(`http://[::1]:${port}`);
|
|
11523
|
+
}
|
|
11524
|
+
return origins;
|
|
10854
11525
|
}
|
|
10855
|
-
function checkOrigin(origin,
|
|
11526
|
+
function checkOrigin(origin, serverOrigins, allowedOrigins) {
|
|
10856
11527
|
if (origin === void 0) return { ok: true };
|
|
11528
|
+
if (serverOrigins.has(origin)) return { ok: true };
|
|
10857
11529
|
if (allowedOrigins.has(origin)) return { ok: true };
|
|
10858
|
-
if (isLoopbackOrigin(origin)) return { ok: true };
|
|
10859
|
-
if (!boundLoopback) return { ok: true };
|
|
10860
11530
|
return { ok: false, reason: `origin ${origin} is not allowed` };
|
|
10861
11531
|
}
|
|
10862
|
-
function
|
|
10863
|
-
|
|
11532
|
+
function buildAllowedHosts(host, port, extraHosts) {
|
|
11533
|
+
const hosts = /* @__PURE__ */ new Set();
|
|
11534
|
+
hosts.add(`${host}:${port}`);
|
|
11535
|
+
hosts.add(host);
|
|
11536
|
+
hosts.add(`${port}.kernel.localhost`);
|
|
11537
|
+
const loopback2 = ["localhost", "127.0.0.1", "[::1]"];
|
|
11538
|
+
for (const h of loopback2) {
|
|
11539
|
+
hosts.add(h);
|
|
11540
|
+
hosts.add(`${h}:${port}`);
|
|
11541
|
+
}
|
|
11542
|
+
for (const h of extraHosts) hosts.add(h);
|
|
11543
|
+
return hosts;
|
|
11544
|
+
}
|
|
11545
|
+
function checkHost(host, allowedHosts) {
|
|
10864
11546
|
if (!host) return { ok: false, reason: "missing Host header" };
|
|
10865
|
-
if (
|
|
10866
|
-
|
|
11547
|
+
if (allowedHosts.has(host)) return { ok: true };
|
|
11548
|
+
const hostname = host.replace(/:\d+$/, "");
|
|
11549
|
+
if (allowedHosts.has(hostname)) return { ok: true };
|
|
11550
|
+
if (hostname.endsWith(".localhost")) return { ok: true };
|
|
11551
|
+
return { ok: false, reason: `host ${host} is not allowed` };
|
|
10867
11552
|
}
|
|
10868
11553
|
|
|
10869
11554
|
// src/serve/proxy.ts
|
|
@@ -11100,6 +11785,8 @@ function parseSingleFlag(args2, i, opts) {
|
|
|
11100
11785
|
return parsed.next;
|
|
11101
11786
|
} else if (arg === "--json") {
|
|
11102
11787
|
opts.json = true;
|
|
11788
|
+
} else if (arg === "--no-auth") {
|
|
11789
|
+
opts.noAuth = true;
|
|
11103
11790
|
} else if (!arg.startsWith("-")) {
|
|
11104
11791
|
opts.commands.push(parseCommandTarget(arg));
|
|
11105
11792
|
} else {
|
|
@@ -11120,6 +11807,9 @@ function parseValueFlag(args2, i, opts) {
|
|
|
11120
11807
|
case "--allow-origin":
|
|
11121
11808
|
opts.allowOrigins.push(args2[++i]);
|
|
11122
11809
|
break;
|
|
11810
|
+
case "--allow-host":
|
|
11811
|
+
opts.allowHosts.push(args2[++i]);
|
|
11812
|
+
break;
|
|
11123
11813
|
case "--env":
|
|
11124
11814
|
opts.envAllow.push(args2[++i]);
|
|
11125
11815
|
break;
|
|
@@ -11158,7 +11848,9 @@ function parseServeFlags(args2) {
|
|
|
11158
11848
|
host: "127.0.0.1",
|
|
11159
11849
|
port: 0,
|
|
11160
11850
|
bearer: void 0,
|
|
11851
|
+
noAuth: false,
|
|
11161
11852
|
allowOrigins: [],
|
|
11853
|
+
allowHosts: [],
|
|
11162
11854
|
envAllow: [],
|
|
11163
11855
|
toolTemplates: [],
|
|
11164
11856
|
callTimeout: CALL_TIMEOUT_MS,
|
|
@@ -11187,6 +11879,20 @@ function constantTimeEqual(a, b) {
|
|
|
11187
11879
|
}
|
|
11188
11880
|
return mismatch === 0;
|
|
11189
11881
|
}
|
|
11882
|
+
function generateToken() {
|
|
11883
|
+
const bytes = new Uint8Array(32);
|
|
11884
|
+
crypto.getRandomValues(bytes);
|
|
11885
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
11886
|
+
}
|
|
11887
|
+
function checkContentType(ct) {
|
|
11888
|
+
if (!ct) return false;
|
|
11889
|
+
return ct.toLowerCase().startsWith("application/json");
|
|
11890
|
+
}
|
|
11891
|
+
function checkAcceptHeader(accept) {
|
|
11892
|
+
if (!accept) return false;
|
|
11893
|
+
const lower = accept.toLowerCase();
|
|
11894
|
+
return lower.includes("application/json") || lower.includes("text/event-stream") || lower.includes("*/*");
|
|
11895
|
+
}
|
|
11190
11896
|
function buildChildEnv(platform2, envAllow) {
|
|
11191
11897
|
const env = {};
|
|
11192
11898
|
for (const key of ALLOWED_ENV_KEYS) {
|
|
@@ -11235,7 +11941,7 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
|
|
|
11235
11941
|
}
|
|
11236
11942
|
const stdoutChunks = [];
|
|
11237
11943
|
const stderrChunks = [];
|
|
11238
|
-
const
|
|
11944
|
+
const readStream3 = async (stream, into) => {
|
|
11239
11945
|
const r = stream.getReader();
|
|
11240
11946
|
try {
|
|
11241
11947
|
for (; ; ) {
|
|
@@ -11251,8 +11957,8 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
|
|
|
11251
11957
|
(resolve) => setTimeout(() => resolve("timeout"), timeoutMs)
|
|
11252
11958
|
);
|
|
11253
11959
|
const work = Promise.all([
|
|
11254
|
-
|
|
11255
|
-
|
|
11960
|
+
readStream3(proc.stdout, stdoutChunks),
|
|
11961
|
+
readStream3(proc.stderr, stderrChunks),
|
|
11256
11962
|
proc.wait()
|
|
11257
11963
|
]);
|
|
11258
11964
|
const result = await Promise.race([work, timer]);
|
|
@@ -11484,7 +12190,10 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
|
|
|
11484
12190
|
}
|
|
11485
12191
|
async function runServe(platform2, options, version) {
|
|
11486
12192
|
let resolvedBearer;
|
|
11487
|
-
|
|
12193
|
+
let generatedBearer = false;
|
|
12194
|
+
if (options._resolvedBearer) {
|
|
12195
|
+
resolvedBearer = options._resolvedBearer;
|
|
12196
|
+
} else if (options.bearer) {
|
|
11488
12197
|
const expanded = expandBearerToken(options.bearer, platform2.env);
|
|
11489
12198
|
if (!expanded) {
|
|
11490
12199
|
await platform2.write(
|
|
@@ -11494,9 +12203,31 @@ async function runServe(platform2, options, version) {
|
|
|
11494
12203
|
return 2;
|
|
11495
12204
|
}
|
|
11496
12205
|
resolvedBearer = expanded;
|
|
12206
|
+
} else if (options.noAuth) {
|
|
12207
|
+
if (options.transport === "http" && !isLoopbackBind(options.host)) {
|
|
12208
|
+
await platform2.write(2, "mcpcat serve: --no-auth is only allowed on loopback\n");
|
|
12209
|
+
return 2;
|
|
12210
|
+
}
|
|
12211
|
+
if (options.transport === "http") {
|
|
12212
|
+
await platform2.write(
|
|
12213
|
+
2,
|
|
12214
|
+
"mcpcat serve: warning: running without authentication on loopback\n"
|
|
12215
|
+
);
|
|
12216
|
+
}
|
|
12217
|
+
resolvedBearer = void 0;
|
|
12218
|
+
} else if (options.transport === "http") {
|
|
12219
|
+
if (platform2.kind === "kernel") {
|
|
12220
|
+
await platform2.write(
|
|
12221
|
+
2,
|
|
12222
|
+
"mcpcat serve: kernel HTTP serve requires --bearer '${VAR}' or --no-auth\n"
|
|
12223
|
+
);
|
|
12224
|
+
return 2;
|
|
12225
|
+
}
|
|
12226
|
+
resolvedBearer = generateToken();
|
|
12227
|
+
generatedBearer = true;
|
|
11497
12228
|
}
|
|
11498
12229
|
if (options.proxy) {
|
|
11499
|
-
return runProxyServe(platform2, options, version, resolvedBearer);
|
|
12230
|
+
return runProxyServe(platform2, options, version, resolvedBearer, generatedBearer);
|
|
11500
12231
|
}
|
|
11501
12232
|
const serveCwd = options.cwd ?? platform2.cwd;
|
|
11502
12233
|
const childEnv = buildChildEnv(platform2, options.envAllow);
|
|
@@ -11533,12 +12264,35 @@ async function runServe(platform2, options, version) {
|
|
|
11533
12264
|
executor.invoke,
|
|
11534
12265
|
serverName,
|
|
11535
12266
|
version,
|
|
11536
|
-
resolvedBearer
|
|
12267
|
+
resolvedBearer,
|
|
12268
|
+
generatedBearer
|
|
11537
12269
|
);
|
|
11538
12270
|
}
|
|
12271
|
+
async function publishBearer(platform2, token, port, listenUrl, json) {
|
|
12272
|
+
if (!json && platform2.isatty(2)) {
|
|
12273
|
+
await platform2.write(2, `mcpcat serve: bearer ${token}
|
|
12274
|
+
`);
|
|
12275
|
+
return void 0;
|
|
12276
|
+
}
|
|
12277
|
+
const dir = stateDir(platform2);
|
|
12278
|
+
await platform2.fs.mkdir(dir, { mode: 448 });
|
|
12279
|
+
const tokenPath = `${dir}/serve-${port}.token`;
|
|
12280
|
+
try {
|
|
12281
|
+
await platform2.fs.unlink?.(tokenPath);
|
|
12282
|
+
} catch {
|
|
12283
|
+
}
|
|
12284
|
+
await platform2.fs.writeFile(tokenPath, token, { mode: 384 });
|
|
12285
|
+
await platform2.write(2, `mcpcat serve: bearer saved to ${tokenPath}
|
|
12286
|
+
`);
|
|
12287
|
+
if (json) {
|
|
12288
|
+
await platform2.write(1, `${JSON.stringify({ url: listenUrl, bearerFile: tokenPath })}
|
|
12289
|
+
`);
|
|
12290
|
+
}
|
|
12291
|
+
return tokenPath;
|
|
12292
|
+
}
|
|
11539
12293
|
function resolveProxyStdio(target, version) {
|
|
11540
12294
|
const rest = target.slice(6);
|
|
11541
|
-
const parts = rest
|
|
12295
|
+
const parts = splitShellStyle(rest);
|
|
11542
12296
|
return { command: parts[0], args: parts.slice(1), version };
|
|
11543
12297
|
}
|
|
11544
12298
|
function resolveProxyUrl(target, headers, version) {
|
|
@@ -11608,7 +12362,7 @@ async function buildProxyConnectOpts(platform2, options, version) {
|
|
|
11608
12362
|
}
|
|
11609
12363
|
return { connectOpts, hiddenValues };
|
|
11610
12364
|
}
|
|
11611
|
-
async function runProxyServe(platform2, options, version, resolvedBearer) {
|
|
12365
|
+
async function runProxyServe(platform2, options, version, resolvedBearer, generatedBearer) {
|
|
11612
12366
|
const built = await buildProxyConnectOpts(platform2, options, version);
|
|
11613
12367
|
if ("error" in built) {
|
|
11614
12368
|
await platform2.write(2, `mcpcat serve --proxy: ${built.error}
|
|
@@ -11626,7 +12380,7 @@ async function runProxyServe(platform2, options, version, resolvedBearer) {
|
|
|
11626
12380
|
if (options.transport === "stdio") {
|
|
11627
12381
|
return runProxyStdio(platform2, proxy);
|
|
11628
12382
|
}
|
|
11629
|
-
return runProxyHttp(platform2, options, proxy, resolvedBearer);
|
|
12383
|
+
return runProxyHttp(platform2, options, proxy, resolvedBearer, generatedBearer);
|
|
11630
12384
|
}
|
|
11631
12385
|
async function runProxyStdio(platform2, proxy) {
|
|
11632
12386
|
const reader = platform2.stdin.getReader();
|
|
@@ -11657,12 +12411,12 @@ async function runProxyStdio(platform2, proxy) {
|
|
|
11657
12411
|
}
|
|
11658
12412
|
return 0;
|
|
11659
12413
|
}
|
|
11660
|
-
async function runProxyHttp(platform2, options, proxy, resolvedBearer) {
|
|
12414
|
+
async function runProxyHttp(platform2, options, proxy, resolvedBearer, generatedBearer) {
|
|
11661
12415
|
if (!platform2.listen) {
|
|
11662
12416
|
await platform2.write(2, "mcpcat serve --proxy: HTTP transport requires platform.listen\n");
|
|
11663
12417
|
return 3;
|
|
11664
12418
|
}
|
|
11665
|
-
const boundLoopback = options.host
|
|
12419
|
+
const boundLoopback = isLoopbackBind(options.host);
|
|
11666
12420
|
if (!boundLoopback && !resolvedBearer) {
|
|
11667
12421
|
await platform2.write(
|
|
11668
12422
|
2,
|
|
@@ -11676,10 +12430,23 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer) {
|
|
|
11676
12430
|
port: options.port
|
|
11677
12431
|
});
|
|
11678
12432
|
const actualPort = listener.port;
|
|
12433
|
+
const serverOrigins = buildServerOrigins(options.host, actualPort);
|
|
12434
|
+
const allowedHosts = buildAllowedHosts(options.host, actualPort, options.allowHosts);
|
|
11679
12435
|
const listenUrl = `http://${options.host}:${actualPort}/mcp`;
|
|
11680
|
-
|
|
11681
|
-
|
|
12436
|
+
let tokenFilePath;
|
|
12437
|
+
if (generatedBearer && resolvedBearer) {
|
|
12438
|
+
tokenFilePath = await publishBearer(
|
|
12439
|
+
platform2,
|
|
12440
|
+
resolvedBearer,
|
|
12441
|
+
actualPort,
|
|
12442
|
+
listenUrl,
|
|
12443
|
+
options.json
|
|
12444
|
+
);
|
|
12445
|
+
} else {
|
|
12446
|
+
if (options.json) {
|
|
12447
|
+
await platform2.write(1, `${JSON.stringify({ url: listenUrl })}
|
|
11682
12448
|
`);
|
|
12449
|
+
}
|
|
11683
12450
|
}
|
|
11684
12451
|
await platform2.write(2, `mcpcat serve --proxy: listening on ${listenUrl}
|
|
11685
12452
|
`);
|
|
@@ -11692,12 +12459,23 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer) {
|
|
|
11692
12459
|
shutdownRequested = true;
|
|
11693
12460
|
listener.close();
|
|
11694
12461
|
void proxy.shutdown();
|
|
12462
|
+
if (tokenFilePath) {
|
|
12463
|
+
platform2.fs.unlink?.(tokenFilePath).catch(() => {
|
|
12464
|
+
});
|
|
12465
|
+
}
|
|
11695
12466
|
};
|
|
11696
12467
|
if (platform2.onSignal) {
|
|
11697
12468
|
platform2.onSignal("SIGINT", shutdown);
|
|
11698
12469
|
platform2.onSignal("SIGTERM", shutdown);
|
|
11699
12470
|
}
|
|
11700
|
-
const handler = (request) => handleProxyMcpRequest(
|
|
12471
|
+
const handler = (request) => handleProxyMcpRequest(
|
|
12472
|
+
request,
|
|
12473
|
+
proxy,
|
|
12474
|
+
serverOrigins,
|
|
12475
|
+
allowedOrigins,
|
|
12476
|
+
allowedHosts,
|
|
12477
|
+
resolvedBearer
|
|
12478
|
+
);
|
|
11701
12479
|
try {
|
|
11702
12480
|
for await (const connection of listener) {
|
|
11703
12481
|
if (shutdownRequested) break;
|
|
@@ -11715,20 +12493,40 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer) {
|
|
|
11715
12493
|
}
|
|
11716
12494
|
return 0;
|
|
11717
12495
|
}
|
|
11718
|
-
async function handleProxyMcpRequest(request, proxy, allowedOrigins,
|
|
11719
|
-
if (
|
|
11720
|
-
|
|
11721
|
-
|
|
11722
|
-
|
|
11723
|
-
|
|
11724
|
-
|
|
11725
|
-
|
|
11726
|
-
|
|
11727
|
-
|
|
11728
|
-
|
|
11729
|
-
|
|
12496
|
+
async function handleProxyMcpRequest(request, proxy, serverOrigins, allowedOrigins, allowedHosts, resolvedBearer) {
|
|
12497
|
+
if (request.target !== "/mcp") {
|
|
12498
|
+
return {
|
|
12499
|
+
status: 404,
|
|
12500
|
+
reason: "Not Found",
|
|
12501
|
+
headers: { "content-type": "text/plain" },
|
|
12502
|
+
body: "Not Found"
|
|
12503
|
+
};
|
|
12504
|
+
}
|
|
12505
|
+
if (request.method !== "POST") {
|
|
12506
|
+
return {
|
|
12507
|
+
status: 405,
|
|
12508
|
+
reason: "Method Not Allowed",
|
|
12509
|
+
headers: { "content-type": "text/plain", allow: "POST" },
|
|
12510
|
+
body: "Method Not Allowed"
|
|
12511
|
+
};
|
|
12512
|
+
}
|
|
12513
|
+
if (!checkContentType(request.headers["content-type"])) {
|
|
12514
|
+
return {
|
|
12515
|
+
status: 415,
|
|
12516
|
+
reason: "Unsupported Media Type",
|
|
12517
|
+
headers: { "content-type": "text/plain" },
|
|
12518
|
+
body: "Content-Type must be application/json"
|
|
12519
|
+
};
|
|
11730
12520
|
}
|
|
11731
|
-
|
|
12521
|
+
if (!checkAcceptHeader(request.headers.accept)) {
|
|
12522
|
+
return {
|
|
12523
|
+
status: 406,
|
|
12524
|
+
reason: "Not Acceptable",
|
|
12525
|
+
headers: { "content-type": "text/plain" },
|
|
12526
|
+
body: "Accept must include application/json or text/event-stream"
|
|
12527
|
+
};
|
|
12528
|
+
}
|
|
12529
|
+
const originCheck = checkOrigin(request.headers.origin, serverOrigins, allowedOrigins);
|
|
11732
12530
|
if (!originCheck.ok) {
|
|
11733
12531
|
return {
|
|
11734
12532
|
status: 403,
|
|
@@ -11737,7 +12535,7 @@ async function handleProxyMcpRequest(request, proxy, allowedOrigins, boundLoopba
|
|
|
11737
12535
|
body: originCheck.reason ?? "Forbidden"
|
|
11738
12536
|
};
|
|
11739
12537
|
}
|
|
11740
|
-
const hostCheck = checkHost(request.headers.host,
|
|
12538
|
+
const hostCheck = checkHost(request.headers.host, allowedHosts);
|
|
11741
12539
|
if (!hostCheck.ok) {
|
|
11742
12540
|
return {
|
|
11743
12541
|
status: 403,
|
|
@@ -11746,21 +12544,17 @@ async function handleProxyMcpRequest(request, proxy, allowedOrigins, boundLoopba
|
|
|
11746
12544
|
body: hostCheck.reason ?? "Forbidden"
|
|
11747
12545
|
};
|
|
11748
12546
|
}
|
|
11749
|
-
if (
|
|
11750
|
-
|
|
11751
|
-
|
|
11752
|
-
|
|
11753
|
-
|
|
11754
|
-
|
|
11755
|
-
|
|
11756
|
-
|
|
11757
|
-
|
|
11758
|
-
|
|
11759
|
-
|
|
11760
|
-
reason: "Method Not Allowed",
|
|
11761
|
-
headers: { "content-type": "text/plain", allow: "POST" },
|
|
11762
|
-
body: "Method Not Allowed"
|
|
11763
|
-
};
|
|
12547
|
+
if (resolvedBearer) {
|
|
12548
|
+
const authHeader = request.headers.authorization;
|
|
12549
|
+
const provided = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : "";
|
|
12550
|
+
if (!provided || !constantTimeEqual(provided, resolvedBearer)) {
|
|
12551
|
+
return {
|
|
12552
|
+
status: 401,
|
|
12553
|
+
reason: "Unauthorized",
|
|
12554
|
+
headers: { "content-type": "text/plain" },
|
|
12555
|
+
body: "Unauthorized"
|
|
12556
|
+
};
|
|
12557
|
+
}
|
|
11764
12558
|
}
|
|
11765
12559
|
const outcome = await proxy.handle(request.body);
|
|
11766
12560
|
return {
|
|
@@ -11798,12 +12592,12 @@ async function runStdioServer(platform2, tools, call, serverName, version) {
|
|
|
11798
12592
|
}
|
|
11799
12593
|
return 0;
|
|
11800
12594
|
}
|
|
11801
|
-
async function runHttpServer(platform2, options, tools, call, serverName, version, resolvedBearer) {
|
|
12595
|
+
async function runHttpServer(platform2, options, tools, call, serverName, version, resolvedBearer, generatedBearer) {
|
|
11802
12596
|
if (!platform2.listen) {
|
|
11803
12597
|
await platform2.write(2, "mcpcat serve: HTTP transport requires platform.listen\n");
|
|
11804
12598
|
return 3;
|
|
11805
12599
|
}
|
|
11806
|
-
const boundLoopback = options.host
|
|
12600
|
+
const boundLoopback = isLoopbackBind(options.host);
|
|
11807
12601
|
if (!boundLoopback && !resolvedBearer) {
|
|
11808
12602
|
await platform2.write(2, "mcpcat serve: --bearer is required when binding off loopback\n");
|
|
11809
12603
|
return 2;
|
|
@@ -11811,10 +12605,23 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
11811
12605
|
const allowedOrigins = new Set(options.allowOrigins);
|
|
11812
12606
|
const listener = await platform2.listen({ host: options.host, port: options.port });
|
|
11813
12607
|
const actualPort = listener.port;
|
|
12608
|
+
const serverOrigins = buildServerOrigins(options.host, actualPort);
|
|
12609
|
+
const allowedHosts = buildAllowedHosts(options.host, actualPort, options.allowHosts);
|
|
11814
12610
|
const listenUrl = `http://${options.host}:${actualPort}/mcp`;
|
|
11815
|
-
|
|
11816
|
-
|
|
12611
|
+
let tokenFilePath;
|
|
12612
|
+
if (generatedBearer && resolvedBearer) {
|
|
12613
|
+
tokenFilePath = await publishBearer(
|
|
12614
|
+
platform2,
|
|
12615
|
+
resolvedBearer,
|
|
12616
|
+
actualPort,
|
|
12617
|
+
listenUrl,
|
|
12618
|
+
options.json
|
|
12619
|
+
);
|
|
12620
|
+
} else {
|
|
12621
|
+
if (options.json) {
|
|
12622
|
+
await platform2.write(1, `${JSON.stringify({ url: listenUrl })}
|
|
11817
12623
|
`);
|
|
12624
|
+
}
|
|
11818
12625
|
}
|
|
11819
12626
|
await platform2.write(2, `mcpcat serve: listening on ${listenUrl}
|
|
11820
12627
|
`);
|
|
@@ -11824,6 +12631,10 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
11824
12631
|
shutdownRequested = true;
|
|
11825
12632
|
listener.close();
|
|
11826
12633
|
void killInflight();
|
|
12634
|
+
if (tokenFilePath) {
|
|
12635
|
+
platform2.fs.unlink?.(tokenFilePath).catch(() => {
|
|
12636
|
+
});
|
|
12637
|
+
}
|
|
11827
12638
|
};
|
|
11828
12639
|
if (platform2.onSignal) {
|
|
11829
12640
|
platform2.onSignal("SIGINT", shutdown);
|
|
@@ -11835,8 +12646,9 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
11835
12646
|
call,
|
|
11836
12647
|
serverName,
|
|
11837
12648
|
version,
|
|
12649
|
+
serverOrigins,
|
|
11838
12650
|
allowedOrigins,
|
|
11839
|
-
|
|
12651
|
+
allowedHosts,
|
|
11840
12652
|
resolvedBearer
|
|
11841
12653
|
);
|
|
11842
12654
|
try {
|
|
@@ -11856,20 +12668,40 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
11856
12668
|
}
|
|
11857
12669
|
return 0;
|
|
11858
12670
|
}
|
|
11859
|
-
async function handleMcpRequest(request, tools, call, serverName, version, allowedOrigins,
|
|
11860
|
-
if (
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
|
|
12671
|
+
async function handleMcpRequest(request, tools, call, serverName, version, serverOrigins, allowedOrigins, allowedHosts, resolvedBearer) {
|
|
12672
|
+
if (request.target !== "/mcp") {
|
|
12673
|
+
return {
|
|
12674
|
+
status: 404,
|
|
12675
|
+
reason: "Not Found",
|
|
12676
|
+
headers: { "content-type": "text/plain" },
|
|
12677
|
+
body: "Not Found"
|
|
12678
|
+
};
|
|
12679
|
+
}
|
|
12680
|
+
if (request.method !== "POST") {
|
|
12681
|
+
return {
|
|
12682
|
+
status: 405,
|
|
12683
|
+
reason: "Method Not Allowed",
|
|
12684
|
+
headers: { "content-type": "text/plain", allow: "POST" },
|
|
12685
|
+
body: "Method Not Allowed"
|
|
12686
|
+
};
|
|
12687
|
+
}
|
|
12688
|
+
if (!checkContentType(request.headers["content-type"])) {
|
|
12689
|
+
return {
|
|
12690
|
+
status: 415,
|
|
12691
|
+
reason: "Unsupported Media Type",
|
|
12692
|
+
headers: { "content-type": "text/plain" },
|
|
12693
|
+
body: "Content-Type must be application/json"
|
|
12694
|
+
};
|
|
12695
|
+
}
|
|
12696
|
+
if (!checkAcceptHeader(request.headers.accept)) {
|
|
12697
|
+
return {
|
|
12698
|
+
status: 406,
|
|
12699
|
+
reason: "Not Acceptable",
|
|
12700
|
+
headers: { "content-type": "text/plain" },
|
|
12701
|
+
body: "Accept must include application/json or text/event-stream"
|
|
12702
|
+
};
|
|
11871
12703
|
}
|
|
11872
|
-
const originCheck = checkOrigin(request.headers.origin,
|
|
12704
|
+
const originCheck = checkOrigin(request.headers.origin, serverOrigins, allowedOrigins);
|
|
11873
12705
|
if (!originCheck.ok) {
|
|
11874
12706
|
return {
|
|
11875
12707
|
status: 403,
|
|
@@ -11878,7 +12710,7 @@ async function handleMcpRequest(request, tools, call, serverName, version, allow
|
|
|
11878
12710
|
body: originCheck.reason ?? "Forbidden"
|
|
11879
12711
|
};
|
|
11880
12712
|
}
|
|
11881
|
-
const hostCheck = checkHost(request.headers.host,
|
|
12713
|
+
const hostCheck = checkHost(request.headers.host, allowedHosts);
|
|
11882
12714
|
if (!hostCheck.ok) {
|
|
11883
12715
|
return {
|
|
11884
12716
|
status: 403,
|
|
@@ -11887,21 +12719,17 @@ async function handleMcpRequest(request, tools, call, serverName, version, allow
|
|
|
11887
12719
|
body: hostCheck.reason ?? "Forbidden"
|
|
11888
12720
|
};
|
|
11889
12721
|
}
|
|
11890
|
-
if (
|
|
11891
|
-
|
|
11892
|
-
|
|
11893
|
-
|
|
11894
|
-
|
|
11895
|
-
|
|
11896
|
-
|
|
11897
|
-
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
|
|
11901
|
-
reason: "Method Not Allowed",
|
|
11902
|
-
headers: { "content-type": "text/plain", allow: "POST" },
|
|
11903
|
-
body: "Method Not Allowed"
|
|
11904
|
-
};
|
|
12722
|
+
if (resolvedBearer) {
|
|
12723
|
+
const authHeader = request.headers.authorization;
|
|
12724
|
+
const provided = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : "";
|
|
12725
|
+
if (!provided || !constantTimeEqual(provided, resolvedBearer)) {
|
|
12726
|
+
return {
|
|
12727
|
+
status: 401,
|
|
12728
|
+
reason: "Unauthorized",
|
|
12729
|
+
headers: { "content-type": "text/plain" },
|
|
12730
|
+
body: "Unauthorized"
|
|
12731
|
+
};
|
|
12732
|
+
}
|
|
11905
12733
|
}
|
|
11906
12734
|
const outcome = await handleJsonRpc(request.body, tools, call, serverName, version);
|
|
11907
12735
|
return {
|
|
@@ -12228,8 +13056,16 @@ var PiFileTokenSource = class {
|
|
|
12228
13056
|
};
|
|
12229
13057
|
|
|
12230
13058
|
// src/cli/auth-verbs.ts
|
|
12231
|
-
|
|
12232
|
-
|
|
13059
|
+
var resolvedSource;
|
|
13060
|
+
async function createTokenSource(platform2) {
|
|
13061
|
+
if (isKernel(platform2)) return new PiFileTokenSource(platform2);
|
|
13062
|
+
if (resolvedSource) return resolvedSource;
|
|
13063
|
+
if (await jerkAvailable(platform2)) {
|
|
13064
|
+
resolvedSource = new JerkTokenSource(platform2);
|
|
13065
|
+
} else {
|
|
13066
|
+
resolvedSource = new PiFileTokenSource(platform2);
|
|
13067
|
+
}
|
|
13068
|
+
return resolvedSource;
|
|
12233
13069
|
}
|
|
12234
13070
|
function resolveServerName(target) {
|
|
12235
13071
|
try {
|
|
@@ -12259,6 +13095,79 @@ async function resolveLoginTarget(platform2, target) {
|
|
|
12259
13095
|
}
|
|
12260
13096
|
throw new Error(`unknown target '${target}': not a URL and not found in mcp.json`);
|
|
12261
13097
|
}
|
|
13098
|
+
function jerkMcpId(name) {
|
|
13099
|
+
if (name.startsWith("mcp:")) return name;
|
|
13100
|
+
if (name.startsWith("url:")) return `mcp:${name}`;
|
|
13101
|
+
return `mcp:${name}`;
|
|
13102
|
+
}
|
|
13103
|
+
async function readStream2(stream) {
|
|
13104
|
+
const reader = stream.getReader();
|
|
13105
|
+
const chunks = [];
|
|
13106
|
+
for (; ; ) {
|
|
13107
|
+
const { done, value } = await reader.read();
|
|
13108
|
+
if (done) break;
|
|
13109
|
+
chunks.push(value);
|
|
13110
|
+
}
|
|
13111
|
+
const total = chunks.reduce((s, c) => s + c.length, 0);
|
|
13112
|
+
const buf = new Uint8Array(total);
|
|
13113
|
+
let off = 0;
|
|
13114
|
+
for (const c of chunks) {
|
|
13115
|
+
buf.set(c, off);
|
|
13116
|
+
off += c.length;
|
|
13117
|
+
}
|
|
13118
|
+
return new TextDecoder().decode(buf);
|
|
13119
|
+
}
|
|
13120
|
+
async function jerkLogin(platform2, target, args2) {
|
|
13121
|
+
const resolved = await resolveLoginTarget(platform2, target);
|
|
13122
|
+
const id = jerkMcpId(resolved.name);
|
|
13123
|
+
const argv = ["jerk", id, "create"];
|
|
13124
|
+
const scopeIdx = args2.indexOf("--scope");
|
|
13125
|
+
if (scopeIdx !== -1 && args2[scopeIdx + 1]) {
|
|
13126
|
+
argv.push("--scope", args2[scopeIdx + 1]);
|
|
13127
|
+
}
|
|
13128
|
+
if (args2.includes("--no-browser")) {
|
|
13129
|
+
argv.push("--no-browser");
|
|
13130
|
+
}
|
|
13131
|
+
const proc = await platform2.spawn({ argv });
|
|
13132
|
+
proc.stdin.close().catch(() => {
|
|
13133
|
+
});
|
|
13134
|
+
const [, errText, result] = await Promise.all([
|
|
13135
|
+
readStream2(proc.stdout),
|
|
13136
|
+
readStream2(proc.stderr),
|
|
13137
|
+
proc.wait()
|
|
13138
|
+
]);
|
|
13139
|
+
if (result.status === 0) {
|
|
13140
|
+
await platform2.write(1, `Signed in to ${target}
|
|
13141
|
+
`);
|
|
13142
|
+
return 0;
|
|
13143
|
+
}
|
|
13144
|
+
if (errText.trim()) {
|
|
13145
|
+
await platform2.write(2, errText);
|
|
13146
|
+
}
|
|
13147
|
+
if (result.status === JERK_EXIT_SIGN_IN) return 4;
|
|
13148
|
+
return result.status;
|
|
13149
|
+
}
|
|
13150
|
+
async function jerkLogout(platform2, target) {
|
|
13151
|
+
const resolved = await resolveLoginTarget(platform2, target);
|
|
13152
|
+
const id = jerkMcpId(resolved.name);
|
|
13153
|
+
const argv = ["jerk", id, "drop", "--yes"];
|
|
13154
|
+
const proc = await platform2.spawn({ argv });
|
|
13155
|
+
proc.stdin.close().catch(() => {
|
|
13156
|
+
});
|
|
13157
|
+
const [, , result] = await Promise.all([
|
|
13158
|
+
readStream2(proc.stdout),
|
|
13159
|
+
readStream2(proc.stderr),
|
|
13160
|
+
proc.wait()
|
|
13161
|
+
]);
|
|
13162
|
+
if (result.status === 0) {
|
|
13163
|
+
await platform2.write(1, `Signed out of ${target}
|
|
13164
|
+
`);
|
|
13165
|
+
} else {
|
|
13166
|
+
await platform2.write(1, `No credentials stored for ${target}
|
|
13167
|
+
`);
|
|
13168
|
+
}
|
|
13169
|
+
return 0;
|
|
13170
|
+
}
|
|
12262
13171
|
async function runLogin(platform2, args2, source) {
|
|
12263
13172
|
if (isKernel(platform2)) {
|
|
12264
13173
|
await platform2.write(
|
|
@@ -12267,13 +13176,22 @@ async function runLogin(platform2, args2, source) {
|
|
|
12267
13176
|
);
|
|
12268
13177
|
return 4;
|
|
12269
13178
|
}
|
|
12270
|
-
const noBrowser = args2.includes("--no-browser");
|
|
12271
13179
|
const positional = args2.filter((a) => !a.startsWith("-"));
|
|
12272
13180
|
if (positional.length === 0) {
|
|
12273
13181
|
await platform2.write(2, "mcpcat: login requires a target\n");
|
|
12274
13182
|
return 2;
|
|
12275
13183
|
}
|
|
12276
|
-
const target = positional[0];
|
|
13184
|
+
const target = positional[0];
|
|
13185
|
+
if (source instanceof JerkTokenSource) {
|
|
13186
|
+
try {
|
|
13187
|
+
return await jerkLogin(platform2, target, args2);
|
|
13188
|
+
} catch (err) {
|
|
13189
|
+
await platform2.write(2, `mcpcat: login failed: ${err.message}
|
|
13190
|
+
`);
|
|
13191
|
+
return 4;
|
|
13192
|
+
}
|
|
13193
|
+
}
|
|
13194
|
+
const noBrowser = args2.includes("--no-browser");
|
|
12277
13195
|
let resolved;
|
|
12278
13196
|
try {
|
|
12279
13197
|
resolved = await resolveLoginTarget(platform2, target);
|
|
@@ -12355,6 +13273,15 @@ async function runLogout(platform2, args2, source) {
|
|
|
12355
13273
|
return 2;
|
|
12356
13274
|
}
|
|
12357
13275
|
const target = positional[0];
|
|
13276
|
+
if (source instanceof JerkTokenSource) {
|
|
13277
|
+
try {
|
|
13278
|
+
return await jerkLogout(platform2, target);
|
|
13279
|
+
} catch (err) {
|
|
13280
|
+
await platform2.write(2, `mcpcat: logout failed: ${err.message}
|
|
13281
|
+
`);
|
|
13282
|
+
return 1;
|
|
13283
|
+
}
|
|
13284
|
+
}
|
|
12358
13285
|
let resolved;
|
|
12359
13286
|
try {
|
|
12360
13287
|
resolved = await resolveLoginTarget(platform2, target);
|
|
@@ -12727,6 +13654,49 @@ function checkAgentWarnings(name, config) {
|
|
|
12727
13654
|
return warnings;
|
|
12728
13655
|
}
|
|
12729
13656
|
|
|
13657
|
+
// src/serve/seal.ts
|
|
13658
|
+
async function sealSecret(platform2, name, value, options) {
|
|
13659
|
+
if (!platform2.seal) {
|
|
13660
|
+
throw Object.assign(new Error("vault device not available"), { sealErrno: "ENOTCONN" });
|
|
13661
|
+
}
|
|
13662
|
+
const request = JSON.stringify({
|
|
13663
|
+
v: 1,
|
|
13664
|
+
op: "seal",
|
|
13665
|
+
kind: "mcp",
|
|
13666
|
+
name,
|
|
13667
|
+
value,
|
|
13668
|
+
server: options.server,
|
|
13669
|
+
url: options.url
|
|
13670
|
+
});
|
|
13671
|
+
let responseText;
|
|
13672
|
+
try {
|
|
13673
|
+
responseText = await platform2.seal(request);
|
|
13674
|
+
} catch (err) {
|
|
13675
|
+
const code2 = err.code;
|
|
13676
|
+
if (code2 === "ENOENT" || code2 === "ENOTCONN") {
|
|
13677
|
+
throw Object.assign(new Error("vault device not available"), { sealErrno: "ENOTCONN" });
|
|
13678
|
+
}
|
|
13679
|
+
throw Object.assign(new Error(`vault: ${err.message}`), {
|
|
13680
|
+
sealErrno: code2 ?? "EACCES"
|
|
13681
|
+
});
|
|
13682
|
+
}
|
|
13683
|
+
const response = JSON.parse(responseText);
|
|
13684
|
+
if (response.error) {
|
|
13685
|
+
throw Object.assign(new Error(response.error), { sealErrno: response.errno ?? "EACCES" });
|
|
13686
|
+
}
|
|
13687
|
+
return { placeholder: response.placeholder };
|
|
13688
|
+
}
|
|
13689
|
+
function sealExitCode(errno) {
|
|
13690
|
+
if (errno === "EEXIST") return 2;
|
|
13691
|
+
return 4;
|
|
13692
|
+
}
|
|
13693
|
+
function sealErrorMessage(errno, name) {
|
|
13694
|
+
if (errno === "EEXIST") return `a non-bridge secret named ${name} already exists`;
|
|
13695
|
+
if (errno === "ENOTCONN") return "no vault available; use --bearer '${VAR}' or --no-auth";
|
|
13696
|
+
if (errno === "EACCES") return "vault locked; use --bearer '${VAR}' or --no-auth";
|
|
13697
|
+
return `vault error: ${errno}`;
|
|
13698
|
+
}
|
|
13699
|
+
|
|
12730
13700
|
// src/cli/config-verbs.ts
|
|
12731
13701
|
function parseHeader(hdr) {
|
|
12732
13702
|
const colonIdx = hdr.indexOf(":");
|
|
@@ -12748,6 +13718,10 @@ function parseAddFlagAt(flagArgs, i, flags) {
|
|
|
12748
13718
|
flags.noProbe = true;
|
|
12749
13719
|
return { next: i + 1 };
|
|
12750
13720
|
}
|
|
13721
|
+
if (arg === "--no-auth") {
|
|
13722
|
+
flags.noAuth = true;
|
|
13723
|
+
return { next: i + 1 };
|
|
13724
|
+
}
|
|
12751
13725
|
if (arg === "--bridge") {
|
|
12752
13726
|
flags.bridge = true;
|
|
12753
13727
|
return { next: i + 1 };
|
|
@@ -12793,6 +13767,7 @@ function parseAddFlags(args2) {
|
|
|
12793
13767
|
local: false,
|
|
12794
13768
|
noProbe: false,
|
|
12795
13769
|
bridge: false,
|
|
13770
|
+
noAuth: false,
|
|
12796
13771
|
headers: [],
|
|
12797
13772
|
envPairs: [],
|
|
12798
13773
|
positional: [],
|
|
@@ -13155,6 +14130,11 @@ async function runImport(platform2, args2) {
|
|
|
13155
14130
|
}
|
|
13156
14131
|
return 0;
|
|
13157
14132
|
}
|
|
14133
|
+
function generateBridgeToken() {
|
|
14134
|
+
const bytes = new Uint8Array(32);
|
|
14135
|
+
crypto.getRandomValues(bytes);
|
|
14136
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
14137
|
+
}
|
|
13158
14138
|
async function runBridge(platform2, args2, version) {
|
|
13159
14139
|
if (platform2.kind === "host") {
|
|
13160
14140
|
await platform2.write(
|
|
@@ -13181,16 +14161,56 @@ async function runBridge(platform2, args2, version) {
|
|
|
13181
14161
|
if (flags.bearer) {
|
|
13182
14162
|
await platform2.write(
|
|
13183
14163
|
2,
|
|
13184
|
-
"mcpcat add --bridge: --bearer is not
|
|
14164
|
+
"mcpcat add --bridge: --bearer is not supported for bridge; authentication is automatic via the vault\n"
|
|
13185
14165
|
);
|
|
13186
14166
|
return 2;
|
|
13187
14167
|
}
|
|
13188
14168
|
const serveOptions = parseServeFlags(["--proxy", "__bridge__", "--http", "0", "--json"]);
|
|
13189
14169
|
serveOptions.proxyCommand = flags.cmdArgs;
|
|
13190
14170
|
const path = flags.configFile ?? (flags.local ? projectConfigPath(platform2) : userConfigPath(platform2));
|
|
14171
|
+
if (flags.noAuth) {
|
|
14172
|
+
await platform2.write(
|
|
14173
|
+
2,
|
|
14174
|
+
"mcpcat add --bridge: warning: running without authentication on loopback\n"
|
|
14175
|
+
);
|
|
14176
|
+
serveOptions.noAuth = true;
|
|
14177
|
+
serveOptions.onListening = async (actualPort) => {
|
|
14178
|
+
const bridgeUrl = `http://${actualPort}.kernel.localhost/mcp`;
|
|
14179
|
+
const httpConfig = { url: bridgeUrl };
|
|
14180
|
+
if (flags.description) httpConfig.description = flags.description;
|
|
14181
|
+
if (flags.exposure) httpConfig.exposure = flags.exposure;
|
|
14182
|
+
const { replaced } = await addServer(platform2.fs, path, name, httpConfig);
|
|
14183
|
+
await platform2.write(
|
|
14184
|
+
1,
|
|
14185
|
+
`${replaced ? "replaced" : "added"} ${name} in ${path}
|
|
14186
|
+
bridge does not survive a reload; runs in the foreground (start with &)
|
|
14187
|
+
`
|
|
14188
|
+
);
|
|
14189
|
+
};
|
|
14190
|
+
return runServe(platform2, serveOptions, version);
|
|
14191
|
+
}
|
|
14192
|
+
const token = generateBridgeToken();
|
|
14193
|
+
const secretName = `MCP_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_TOKEN`;
|
|
14194
|
+
serveOptions._resolvedBearer = token;
|
|
13191
14195
|
serveOptions.onListening = async (actualPort) => {
|
|
14196
|
+
if (actualPort === 9222) {
|
|
14197
|
+
await platform2.write(2, "mcpcat add --bridge: port 9222 is reserved; please retry\n");
|
|
14198
|
+
return;
|
|
14199
|
+
}
|
|
13192
14200
|
const bridgeUrl = `http://${actualPort}.kernel.localhost/mcp`;
|
|
13193
|
-
|
|
14201
|
+
try {
|
|
14202
|
+
await sealSecret(platform2, secretName, token, { server: name, url: bridgeUrl });
|
|
14203
|
+
} catch (err) {
|
|
14204
|
+
const errno = err.sealErrno ?? "EACCES";
|
|
14205
|
+
const code2 = sealExitCode(errno);
|
|
14206
|
+
await platform2.write(2, `mcpcat add --bridge: ${sealErrorMessage(errno, secretName)}
|
|
14207
|
+
`);
|
|
14208
|
+
platform2.exit(code2);
|
|
14209
|
+
}
|
|
14210
|
+
const httpConfig = {
|
|
14211
|
+
url: bridgeUrl,
|
|
14212
|
+
headers: { Authorization: "Bearer ${" + secretName + "}" }
|
|
14213
|
+
};
|
|
13194
14214
|
if (flags.description) httpConfig.description = flags.description;
|
|
13195
14215
|
if (flags.exposure) httpConfig.exposure = flags.exposure;
|
|
13196
14216
|
const { replaced } = await addServer(platform2.fs, path, name, httpConfig);
|
|
@@ -13327,8 +14347,18 @@ var CONFIG_VERBS = {
|
|
|
13327
14347
|
exposure: true,
|
|
13328
14348
|
import: true
|
|
13329
14349
|
};
|
|
13330
|
-
var UNIMPLEMENTED_VERBS = /* @__PURE__ */ new Set(["
|
|
13331
|
-
var CLIENT_VERBS = /* @__PURE__ */ new Set([
|
|
14350
|
+
var UNIMPLEMENTED_VERBS = /* @__PURE__ */ new Set(["complete", "task"]);
|
|
14351
|
+
var CLIENT_VERBS = /* @__PURE__ */ new Set([
|
|
14352
|
+
"info",
|
|
14353
|
+
"tools",
|
|
14354
|
+
"call",
|
|
14355
|
+
"describe",
|
|
14356
|
+
"serve",
|
|
14357
|
+
"resources",
|
|
14358
|
+
"read",
|
|
14359
|
+
"prompts",
|
|
14360
|
+
"prompt"
|
|
14361
|
+
]);
|
|
13332
14362
|
function helpText(version) {
|
|
13333
14363
|
return `mcpcat ${version} \u2013 a curl for the Model Context Protocol
|
|
13334
14364
|
|
|
@@ -13367,6 +14397,7 @@ Options:
|
|
|
13367
14397
|
-H, --header 'N: V' Extra header
|
|
13368
14398
|
-d <json> Tool arguments as JSON (or @file, @- for stdin)
|
|
13369
14399
|
-m, --timeout <s> Request timeout (default 60)
|
|
14400
|
+
--memory <MiB> Code mode memory limit (default 256, max 1024)
|
|
13370
14401
|
--protocol-version <v> Pin protocol version
|
|
13371
14402
|
--schema Print raw JSON Schema (describe)
|
|
13372
14403
|
--ts TypeScript declarations (describe)
|
|
@@ -13383,6 +14414,31 @@ Options:
|
|
|
13383
14414
|
-h, --help Show this help
|
|
13384
14415
|
`;
|
|
13385
14416
|
}
|
|
14417
|
+
var VERBS_WITH_TOOL = /* @__PURE__ */ new Set(["call", "prompt", "describe", "read"]);
|
|
14418
|
+
var VERBS_WITH_TARGET = /* @__PURE__ */ new Set([
|
|
14419
|
+
"info",
|
|
14420
|
+
"tools",
|
|
14421
|
+
"call",
|
|
14422
|
+
"describe",
|
|
14423
|
+
"serve",
|
|
14424
|
+
"grep",
|
|
14425
|
+
"code",
|
|
14426
|
+
"discover",
|
|
14427
|
+
"login",
|
|
14428
|
+
"logout",
|
|
14429
|
+
"resources",
|
|
14430
|
+
"read",
|
|
14431
|
+
"prompts",
|
|
14432
|
+
"prompt"
|
|
14433
|
+
]);
|
|
14434
|
+
function resolveMaxPositional(positional) {
|
|
14435
|
+
if (positional.length === 0) return 3;
|
|
14436
|
+
const first = positional[0];
|
|
14437
|
+
if (!VERBS.includes(first)) return 2;
|
|
14438
|
+
if (VERBS_WITH_TOOL.has(first)) return 3;
|
|
14439
|
+
if (VERBS_WITH_TARGET.has(first)) return 2;
|
|
14440
|
+
return 2;
|
|
14441
|
+
}
|
|
13386
14442
|
function parseFlags(args2) {
|
|
13387
14443
|
const headers = {};
|
|
13388
14444
|
let dataArg;
|
|
@@ -13397,10 +14453,16 @@ function parseFlags(args2) {
|
|
|
13397
14453
|
const serverTargets = [];
|
|
13398
14454
|
let allowShell = false;
|
|
13399
14455
|
let timeoutMs;
|
|
14456
|
+
let memoryLimitMiB;
|
|
13400
14457
|
let verbose = false;
|
|
13401
14458
|
let silent = false;
|
|
13402
14459
|
const positional = [];
|
|
13403
14460
|
let trailingCommand;
|
|
14461
|
+
let respondArg;
|
|
14462
|
+
const roots = [];
|
|
14463
|
+
let outputPath;
|
|
14464
|
+
let noValidate = false;
|
|
14465
|
+
const rest = [];
|
|
13404
14466
|
for (let i = 0; i < args2.length; i++) {
|
|
13405
14467
|
const arg = args2[i];
|
|
13406
14468
|
if (arg === "--") {
|
|
@@ -13441,8 +14503,41 @@ function parseFlags(args2) {
|
|
|
13441
14503
|
silent = true;
|
|
13442
14504
|
} else if ((arg === "-m" || arg === "--timeout") && args2[i + 1]) {
|
|
13443
14505
|
timeoutMs = Number(args2[++i]) * 1e3;
|
|
14506
|
+
} else if (arg === "--respond" && args2[i + 1]) {
|
|
14507
|
+
respondArg = args2[++i];
|
|
14508
|
+
} else if (arg === "--root" && args2[i + 1]) {
|
|
14509
|
+
roots.push(args2[++i]);
|
|
14510
|
+
} else if ((arg === "-o" || arg === "--output") && args2[i + 1]) {
|
|
14511
|
+
outputPath = args2[++i];
|
|
14512
|
+
} else if (arg === "--memory" && args2[i + 1]) {
|
|
14513
|
+
memoryLimitMiB = Number(args2[++i]);
|
|
14514
|
+
} else if (arg === "--no-validate") {
|
|
14515
|
+
noValidate = true;
|
|
13444
14516
|
} else if (!arg.startsWith("-")) {
|
|
13445
14517
|
positional.push(arg);
|
|
14518
|
+
const maxPositional = resolveMaxPositional(positional);
|
|
14519
|
+
if (positional.length >= maxPositional) {
|
|
14520
|
+
rest.push(...args2.slice(i + 1));
|
|
14521
|
+
break;
|
|
14522
|
+
}
|
|
14523
|
+
}
|
|
14524
|
+
}
|
|
14525
|
+
const toolArgs = [];
|
|
14526
|
+
for (let j = 0; j < rest.length; j++) {
|
|
14527
|
+
const ra = rest[j];
|
|
14528
|
+
if (ra === "-d" && rest[j + 1] !== void 0) {
|
|
14529
|
+
dataArg = rest[++j];
|
|
14530
|
+
} else if (ra === "--json") {
|
|
14531
|
+
jsonOutput = true;
|
|
14532
|
+
} else if (ra === "--no-validate") {
|
|
14533
|
+
noValidate = true;
|
|
14534
|
+
} else if ((ra === "-o" || ra === "--output") && rest[j + 1] !== void 0) {
|
|
14535
|
+
outputPath = rest[++j];
|
|
14536
|
+
} else if (ra === "--help" || ra === "-h") {
|
|
14537
|
+
toolArgs.length = 0;
|
|
14538
|
+
break;
|
|
14539
|
+
} else {
|
|
14540
|
+
toolArgs.push(ra);
|
|
13446
14541
|
}
|
|
13447
14542
|
}
|
|
13448
14543
|
return {
|
|
@@ -13459,10 +14554,16 @@ function parseFlags(args2) {
|
|
|
13459
14554
|
serverTargets,
|
|
13460
14555
|
allowShell,
|
|
13461
14556
|
timeoutMs,
|
|
14557
|
+
memoryLimitMiB,
|
|
13462
14558
|
verbose,
|
|
13463
14559
|
silent,
|
|
13464
14560
|
positional,
|
|
13465
|
-
trailingCommand
|
|
14561
|
+
trailingCommand,
|
|
14562
|
+
respondArg,
|
|
14563
|
+
roots,
|
|
14564
|
+
outputPath,
|
|
14565
|
+
noValidate,
|
|
14566
|
+
rest: toolArgs
|
|
13466
14567
|
};
|
|
13467
14568
|
}
|
|
13468
14569
|
function resolveVerb(positional, trailingCommand) {
|
|
@@ -13497,6 +14598,7 @@ function parseArgs(args2) {
|
|
|
13497
14598
|
const resolved = resolveVerb(flags.positional, flags.trailingCommand);
|
|
13498
14599
|
return {
|
|
13499
14600
|
...resolved,
|
|
14601
|
+
toolArgs: flags.rest,
|
|
13500
14602
|
headers: flags.headers,
|
|
13501
14603
|
dataArg: flags.dataArg,
|
|
13502
14604
|
protocolVersion: flags.protocolVersion,
|
|
@@ -13510,8 +14612,13 @@ function parseArgs(args2) {
|
|
|
13510
14612
|
serverTargets: flags.serverTargets,
|
|
13511
14613
|
allowShell: flags.allowShell,
|
|
13512
14614
|
timeoutMs: flags.timeoutMs,
|
|
14615
|
+
memoryLimitMiB: flags.memoryLimitMiB,
|
|
13513
14616
|
verbose: flags.verbose,
|
|
13514
|
-
silent: flags.silent
|
|
14617
|
+
silent: flags.silent,
|
|
14618
|
+
noValidate: flags.noValidate,
|
|
14619
|
+
respondArg: flags.respondArg,
|
|
14620
|
+
roots: flags.roots,
|
|
14621
|
+
outputPath: flags.outputPath
|
|
13515
14622
|
};
|
|
13516
14623
|
}
|
|
13517
14624
|
function formatToolLine(tool) {
|
|
@@ -13634,7 +14741,8 @@ async function runCall(platform2, conn, toolName, dataArg, toolArgs, opts) {
|
|
|
13634
14741
|
const tools = await conn.listTools();
|
|
13635
14742
|
const tool = tools.find((t) => t.name === toolName);
|
|
13636
14743
|
const schema = tool?.inputSchema ?? { type: "object", properties: {} };
|
|
13637
|
-
const
|
|
14744
|
+
const skipRequired = opts.noValidate || !!dataArg;
|
|
14745
|
+
const c = coerceArgsBySchema(toolArgs, schema, { noValidate: skipRequired });
|
|
13638
14746
|
if (!c.ok) {
|
|
13639
14747
|
await writeErr(platform2, opts.json ?? false, "usage", c.error);
|
|
13640
14748
|
return EXIT_USAGE;
|
|
@@ -13643,8 +14751,11 @@ async function runCall(platform2, conn, toolName, dataArg, toolArgs, opts) {
|
|
|
13643
14751
|
}
|
|
13644
14752
|
return renderCallResult(
|
|
13645
14753
|
platform2,
|
|
13646
|
-
await conn.callTool(toolName, fin, {
|
|
13647
|
-
|
|
14754
|
+
await conn.callTool(toolName, fin, {
|
|
14755
|
+
onProgress: opts.onProgress,
|
|
14756
|
+
inputResponses: opts.inputResponses
|
|
14757
|
+
}),
|
|
14758
|
+
{ json: opts.json, outputPath: opts.outputPath }
|
|
13648
14759
|
);
|
|
13649
14760
|
}
|
|
13650
14761
|
async function runDescribe(platform2, conn, toolName, showSchema, json) {
|
|
@@ -13681,19 +14792,168 @@ async function runDescribe(platform2, conn, toolName, showSchema, json) {
|
|
|
13681
14792
|
}
|
|
13682
14793
|
return EXIT_OK;
|
|
13683
14794
|
}
|
|
13684
|
-
async function
|
|
14795
|
+
async function runResources(platform2, conn, json) {
|
|
14796
|
+
const resources = await conn.listResources();
|
|
14797
|
+
const templates = await conn.listResourceTemplates();
|
|
14798
|
+
if (json) {
|
|
14799
|
+
await renderResourcesJson(platform2, resources, templates);
|
|
14800
|
+
return EXIT_OK;
|
|
14801
|
+
}
|
|
14802
|
+
if (resources.length === 0 && templates.length === 0) {
|
|
14803
|
+
await platform2.write(1, "(no resources)\n");
|
|
14804
|
+
return EXIT_OK;
|
|
14805
|
+
}
|
|
14806
|
+
for (const r of resources) {
|
|
14807
|
+
const desc = r.description ? ` ${r.description}` : "";
|
|
14808
|
+
const mime = r.mimeType ? ` [${r.mimeType}]` : "";
|
|
14809
|
+
await platform2.write(1, `${r.uri} ${r.name}${mime}${desc}
|
|
14810
|
+
`);
|
|
14811
|
+
}
|
|
14812
|
+
if (templates.length > 0) {
|
|
14813
|
+
if (resources.length > 0) await platform2.write(1, "\n");
|
|
14814
|
+
await platform2.write(1, "Templates:\n");
|
|
14815
|
+
for (const t of templates) {
|
|
14816
|
+
const desc = t.description ? ` ${t.description}` : "";
|
|
14817
|
+
await platform2.write(1, ` ${t.uriTemplate} ${t.name}${desc}
|
|
14818
|
+
`);
|
|
14819
|
+
}
|
|
14820
|
+
}
|
|
14821
|
+
return EXIT_OK;
|
|
14822
|
+
}
|
|
14823
|
+
async function runRead(platform2, conn, uri, json, outputPath) {
|
|
14824
|
+
const result = await conn.readResource(uri);
|
|
14825
|
+
if (json) {
|
|
14826
|
+
await renderReadResourceJson(platform2, result);
|
|
14827
|
+
return EXIT_OK;
|
|
14828
|
+
}
|
|
14829
|
+
for (const content of result.contents) {
|
|
14830
|
+
if ("text" in content) {
|
|
14831
|
+
if (outputPath) {
|
|
14832
|
+
await platform2.fs.writeFile(outputPath, content.text);
|
|
14833
|
+
await platform2.write(1, `${outputPath}
|
|
14834
|
+
`);
|
|
14835
|
+
} else {
|
|
14836
|
+
await platform2.write(1, `${content.text}
|
|
14837
|
+
`);
|
|
14838
|
+
}
|
|
14839
|
+
} else if ("blob" in content) {
|
|
14840
|
+
if (outputPath) {
|
|
14841
|
+
const bytes = Uint8Array.from(atob(content.blob), (c) => c.charCodeAt(0));
|
|
14842
|
+
await platform2.fs.writeFile(outputPath, bytes);
|
|
14843
|
+
await platform2.write(1, `${outputPath}
|
|
14844
|
+
`);
|
|
14845
|
+
} else {
|
|
14846
|
+
await platform2.write(1, `${content.blob}
|
|
14847
|
+
`);
|
|
14848
|
+
}
|
|
14849
|
+
}
|
|
14850
|
+
}
|
|
14851
|
+
return EXIT_OK;
|
|
14852
|
+
}
|
|
14853
|
+
async function runPrompts(platform2, conn, json) {
|
|
14854
|
+
const prompts = await conn.listPrompts();
|
|
14855
|
+
if (json) {
|
|
14856
|
+
await renderPromptsJson(platform2, prompts);
|
|
14857
|
+
return EXIT_OK;
|
|
14858
|
+
}
|
|
14859
|
+
if (prompts.length === 0) {
|
|
14860
|
+
await platform2.write(1, "(no prompts)\n");
|
|
14861
|
+
return EXIT_OK;
|
|
14862
|
+
}
|
|
14863
|
+
for (const p of prompts) {
|
|
14864
|
+
const desc = p.description ? ` ${p.description}` : "";
|
|
14865
|
+
await platform2.write(1, `${p.name}${desc}
|
|
14866
|
+
`);
|
|
14867
|
+
}
|
|
14868
|
+
return EXIT_OK;
|
|
14869
|
+
}
|
|
14870
|
+
async function runPrompt(platform2, conn, name, dataArg, toolArgs, json) {
|
|
14871
|
+
let args2 = {};
|
|
14872
|
+
if (dataArg) {
|
|
14873
|
+
args2 = await readDataArg(platform2, dataArg);
|
|
14874
|
+
}
|
|
14875
|
+
const prompts = await conn.listPrompts();
|
|
14876
|
+
const prompt = prompts.find((p) => p.name === name);
|
|
14877
|
+
const schema = {
|
|
14878
|
+
type: "object",
|
|
14879
|
+
properties: {},
|
|
14880
|
+
required: []
|
|
14881
|
+
};
|
|
14882
|
+
if (prompt?.arguments) {
|
|
14883
|
+
const props = {};
|
|
14884
|
+
const req = [];
|
|
14885
|
+
for (const a of prompt.arguments) {
|
|
14886
|
+
props[a.name] = { type: "string", description: a.description };
|
|
14887
|
+
if (a.required) req.push(a.name);
|
|
14888
|
+
}
|
|
14889
|
+
schema.properties = props;
|
|
14890
|
+
schema.required = req;
|
|
14891
|
+
}
|
|
14892
|
+
if (toolArgs.length > 0) {
|
|
14893
|
+
const skipRequired = !!dataArg;
|
|
14894
|
+
const c = coerceArgsBySchema(toolArgs, schema, { noValidate: skipRequired });
|
|
14895
|
+
if (!c.ok) {
|
|
14896
|
+
await writeErr(platform2, json, "usage", c.error);
|
|
14897
|
+
return EXIT_USAGE;
|
|
14898
|
+
}
|
|
14899
|
+
args2 = mergeArgs(args2, c.value);
|
|
14900
|
+
} else if (!dataArg) {
|
|
14901
|
+
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
14902
|
+
for (const r of required) {
|
|
14903
|
+
if (!Object.hasOwn(args2, r)) {
|
|
14904
|
+
await writeErr(platform2, json, "usage", `missing required flag --${r}`);
|
|
14905
|
+
return EXIT_USAGE;
|
|
14906
|
+
}
|
|
14907
|
+
}
|
|
14908
|
+
}
|
|
14909
|
+
const result = await conn.getPrompt(name, args2);
|
|
14910
|
+
if (json) {
|
|
14911
|
+
await renderPromptResultJson(platform2, result);
|
|
14912
|
+
return EXIT_OK;
|
|
14913
|
+
}
|
|
14914
|
+
if (result.description) {
|
|
14915
|
+
await platform2.write(1, `${result.description}
|
|
14916
|
+
|
|
14917
|
+
`);
|
|
14918
|
+
}
|
|
14919
|
+
for (const msg of result.messages) {
|
|
14920
|
+
const text = msg.content?.text ?? (msg.content?.data ? `[${msg.content.mimeType ?? "binary"}]` : "");
|
|
14921
|
+
await platform2.write(1, `[${msg.role}] ${text}
|
|
14922
|
+
`);
|
|
14923
|
+
}
|
|
14924
|
+
return EXIT_OK;
|
|
14925
|
+
}
|
|
14926
|
+
async function runVerb(platform2, parsed, conn, onProgress, respondData) {
|
|
13685
14927
|
switch (parsed.verb) {
|
|
13686
14928
|
case "info":
|
|
13687
14929
|
return runInfo(platform2, conn, parsed.jsonOutput);
|
|
13688
14930
|
case "tools":
|
|
13689
14931
|
return runTools(platform2, conn, parsed.jsonOutput);
|
|
13690
14932
|
case "call":
|
|
13691
|
-
return runCall(platform2, conn, parsed.toolName, parsed.dataArg,
|
|
14933
|
+
return runCall(platform2, conn, parsed.toolName, parsed.dataArg, parsed.toolArgs, {
|
|
13692
14934
|
json: parsed.jsonOutput,
|
|
13693
|
-
|
|
14935
|
+
noValidate: parsed.noValidate,
|
|
14936
|
+
outputPath: parsed.outputPath,
|
|
14937
|
+
onProgress,
|
|
14938
|
+
inputResponses: respondData
|
|
13694
14939
|
});
|
|
13695
14940
|
case "describe":
|
|
13696
14941
|
return runDescribe(platform2, conn, parsed.toolName, parsed.showSchema, parsed.jsonOutput);
|
|
14942
|
+
case "resources":
|
|
14943
|
+
return runResources(platform2, conn, parsed.jsonOutput);
|
|
14944
|
+
case "read":
|
|
14945
|
+
return runRead(platform2, conn, parsed.toolName, parsed.jsonOutput, parsed.outputPath);
|
|
14946
|
+
case "prompts":
|
|
14947
|
+
return runPrompts(platform2, conn, parsed.jsonOutput);
|
|
14948
|
+
case "prompt":
|
|
14949
|
+
return runPrompt(
|
|
14950
|
+
platform2,
|
|
14951
|
+
conn,
|
|
14952
|
+
parsed.toolName,
|
|
14953
|
+
parsed.dataArg,
|
|
14954
|
+
parsed.toolArgs,
|
|
14955
|
+
parsed.jsonOutput
|
|
14956
|
+
);
|
|
13697
14957
|
default:
|
|
13698
14958
|
await platform2.write(2, `mcpcat: unknown command '${parsed.verb}'
|
|
13699
14959
|
`);
|
|
@@ -13831,6 +15091,18 @@ async function resolveTargetNames(platform2, parsed) {
|
|
|
13831
15091
|
return config.list().filter((e) => e.config.enabled !== false).map((e) => e.name);
|
|
13832
15092
|
}
|
|
13833
15093
|
async function dispatchMultiTarget(platform2, parsed, version) {
|
|
15094
|
+
let activeCleanup;
|
|
15095
|
+
async function killTreeAndExit(exitCode) {
|
|
15096
|
+
if (activeCleanup) {
|
|
15097
|
+
try {
|
|
15098
|
+
await Promise.race([activeCleanup(), new Promise((r) => setTimeout(r, 3e3))]);
|
|
15099
|
+
} catch {
|
|
15100
|
+
}
|
|
15101
|
+
}
|
|
15102
|
+
platform2.exit(exitCode);
|
|
15103
|
+
}
|
|
15104
|
+
platform2.onSignal?.("SIGTERM", () => void killTreeAndExit(143));
|
|
15105
|
+
platform2.onSignal?.("SIGHUP", () => void killTreeAndExit(129));
|
|
13834
15106
|
const config = await MergedConfigSource.load(platform2);
|
|
13835
15107
|
if (parsed.verb === "code") {
|
|
13836
15108
|
let source;
|
|
@@ -13862,10 +15134,12 @@ async function dispatchMultiTarget(platform2, parsed, version) {
|
|
|
13862
15134
|
parsed,
|
|
13863
15135
|
version
|
|
13864
15136
|
);
|
|
15137
|
+
activeCleanup = cleanup;
|
|
13865
15138
|
try {
|
|
13866
15139
|
return await runCode(targets, source, {
|
|
13867
15140
|
json: parsed.jsonOutput,
|
|
13868
15141
|
timeoutMs: parsed.timeoutMs,
|
|
15142
|
+
memoryLimitMiB: parsed.memoryLimitMiB,
|
|
13869
15143
|
allowShell: parsed.allowShell,
|
|
13870
15144
|
platform: platform2
|
|
13871
15145
|
});
|
|
@@ -13891,6 +15165,7 @@ async function dispatchMultiTarget(platform2, parsed, version) {
|
|
|
13891
15165
|
parsed,
|
|
13892
15166
|
version
|
|
13893
15167
|
);
|
|
15168
|
+
activeCleanup = cleanup;
|
|
13894
15169
|
try {
|
|
13895
15170
|
return await runGrep(platform2, pattern, targets, {
|
|
13896
15171
|
rank: parsed.rank,
|
|
@@ -13909,6 +15184,7 @@ async function dispatchMultiTarget(platform2, parsed, version) {
|
|
|
13909
15184
|
parsed,
|
|
13910
15185
|
version
|
|
13911
15186
|
);
|
|
15187
|
+
activeCleanup = cleanup;
|
|
13912
15188
|
try {
|
|
13913
15189
|
return await runDescribeTs(platform2, targets, parsed.tsBudget);
|
|
13914
15190
|
} finally {
|
|
@@ -13918,6 +15194,18 @@ async function dispatchMultiTarget(platform2, parsed, version) {
|
|
|
13918
15194
|
return 2;
|
|
13919
15195
|
}
|
|
13920
15196
|
async function run(platform2, version, args2) {
|
|
15197
|
+
try {
|
|
15198
|
+
return await runInner(platform2, version, args2);
|
|
15199
|
+
} catch (err) {
|
|
15200
|
+
if (err.code === "EACCES") {
|
|
15201
|
+
await platform2.write(2, `mcpcat: ${err.message}
|
|
15202
|
+
`);
|
|
15203
|
+
return EXIT_USAGE;
|
|
15204
|
+
}
|
|
15205
|
+
throw err;
|
|
15206
|
+
}
|
|
15207
|
+
}
|
|
15208
|
+
async function runInner(platform2, version, args2) {
|
|
13921
15209
|
if (args2.includes("--version") || args2.includes("-V")) {
|
|
13922
15210
|
await platform2.write(1, `${version}
|
|
13923
15211
|
`);
|
|
@@ -13944,11 +15232,11 @@ async function run(platform2, version, args2) {
|
|
|
13944
15232
|
const discResult = await dispatchDiscoveryVerb(platform2, parsed, args2);
|
|
13945
15233
|
if (discResult !== void 0) return discResult;
|
|
13946
15234
|
if (parsed.verb === "login") {
|
|
13947
|
-
const source = createTokenSource(platform2);
|
|
15235
|
+
const source = await createTokenSource(platform2);
|
|
13948
15236
|
return runLogin(platform2, args2.slice(1), source);
|
|
13949
15237
|
}
|
|
13950
15238
|
if (parsed.verb === "logout") {
|
|
13951
|
-
const source = createTokenSource(platform2);
|
|
15239
|
+
const source = await createTokenSource(platform2);
|
|
13952
15240
|
return runLogout(platform2, args2.slice(1), source);
|
|
13953
15241
|
}
|
|
13954
15242
|
if (UNIMPLEMENTED_VERBS.has(parsed.verb)) {
|
|
@@ -13971,6 +15259,14 @@ async function run(platform2, version, args2) {
|
|
|
13971
15259
|
await platform2.write(2, "mcpcat: call requires a tool name\n");
|
|
13972
15260
|
return 2;
|
|
13973
15261
|
}
|
|
15262
|
+
if (parsed.verb === "read" && !parsed.toolName) {
|
|
15263
|
+
await platform2.write(2, "mcpcat: read requires a URI\n");
|
|
15264
|
+
return 2;
|
|
15265
|
+
}
|
|
15266
|
+
if (parsed.verb === "prompt" && !parsed.toolName) {
|
|
15267
|
+
await platform2.write(2, "mcpcat: prompt requires a name\n");
|
|
15268
|
+
return 2;
|
|
15269
|
+
}
|
|
13974
15270
|
if (!parsed.target) {
|
|
13975
15271
|
await platform2.write(2, `mcpcat: ${parsed.verb} requires a target
|
|
13976
15272
|
`);
|
|
@@ -13981,13 +15277,19 @@ async function run(platform2, version, args2) {
|
|
|
13981
15277
|
function expandAndCollect(headers, env) {
|
|
13982
15278
|
const secrets = [];
|
|
13983
15279
|
const result = {};
|
|
15280
|
+
const unset = [];
|
|
13984
15281
|
for (const [k, v] of Object.entries(headers)) {
|
|
15282
|
+
const missing = missingPlaceholders(v, env);
|
|
15283
|
+
if (missing.length > 0) {
|
|
15284
|
+
unset.push(...missing);
|
|
15285
|
+
continue;
|
|
15286
|
+
}
|
|
13985
15287
|
const e = expandPlaceholders(v, env);
|
|
13986
15288
|
result[k] = e;
|
|
13987
15289
|
if (e !== v) secrets.push(e);
|
|
13988
15290
|
if (v.length > 0) secrets.push(v);
|
|
13989
15291
|
}
|
|
13990
|
-
return { expanded: result, secrets };
|
|
15292
|
+
return { expanded: result, secrets, unset };
|
|
13991
15293
|
}
|
|
13992
15294
|
function classifyError(err) {
|
|
13993
15295
|
if (err instanceof InputRequiredError) {
|
|
@@ -13999,6 +15301,9 @@ function classifyError(err) {
|
|
|
13999
15301
|
if (err instanceof McpAuthError) {
|
|
14000
15302
|
return { code: EXIT_AUTH, kind: "auth", msg: err.message };
|
|
14001
15303
|
}
|
|
15304
|
+
if (err instanceof JerkSignInRequiredError) {
|
|
15305
|
+
return { code: EXIT_AUTH, kind: "auth", msg: err.message };
|
|
15306
|
+
}
|
|
14002
15307
|
if (err instanceof AuthStepUpError) {
|
|
14003
15308
|
const scopeHint = err.scope ? ` --scope ${err.scope}` : "";
|
|
14004
15309
|
return {
|
|
@@ -14020,25 +15325,112 @@ function classifyError(err) {
|
|
|
14020
15325
|
}
|
|
14021
15326
|
return { code: EXIT_RPC_ERROR, kind: "rpc_error", msg };
|
|
14022
15327
|
}
|
|
14023
|
-
function resolveAuthProvider(platform2, hasAuthHeader, url, target, config) {
|
|
15328
|
+
async function resolveAuthProvider(platform2, hasAuthHeader, url, target, config) {
|
|
14024
15329
|
if (hasAuthHeader || !url) return void 0;
|
|
14025
|
-
const source = createTokenSource(platform2);
|
|
15330
|
+
const source = await createTokenSource(platform2);
|
|
14026
15331
|
const entry = config.entry(target);
|
|
14027
15332
|
const oauth = entry && "url" in entry.config ? entry.config.oauth : void 0;
|
|
14028
15333
|
const name = entry ? target : `url:${new URL(url).origin}${new URL(url).pathname}`;
|
|
14029
15334
|
return createMcpAuthProvider({ name, serverUrl: url, source, oauth, platform: platform2 });
|
|
14030
15335
|
}
|
|
15336
|
+
function classifyConnectionError(err, target, interrupted) {
|
|
15337
|
+
if (err instanceof McpAuthError || err instanceof AuthStepUpError || err instanceof JerkSignInRequiredError) {
|
|
15338
|
+
return classifyError(err);
|
|
15339
|
+
}
|
|
15340
|
+
if (err.name === "McpOAuthAuthorizationRequiredError" || err.name === "McpAuthRequiredError") {
|
|
15341
|
+
return { code: EXIT_AUTH, kind: "auth", msg: `unauthorized. Run: mcpcat login ${target}` };
|
|
15342
|
+
}
|
|
15343
|
+
if (interrupted) {
|
|
15344
|
+
return { code: EXIT_INTERRUPTED, kind: "interrupted", msg: "interrupted" };
|
|
15345
|
+
}
|
|
15346
|
+
const msg = transportHint(err) ?? `connection failed: ${err.message}`;
|
|
15347
|
+
return { code: EXIT_CONNECTION, kind: "connection", msg };
|
|
15348
|
+
}
|
|
15349
|
+
function buildRoots(rootArgs, isStdio, cwd) {
|
|
15350
|
+
const roots = rootArgs.map((r) => ({
|
|
15351
|
+
uri: r.startsWith("file://") ? r : `file://${r}`
|
|
15352
|
+
}));
|
|
15353
|
+
if (roots.length === 0 && isStdio) {
|
|
15354
|
+
roots.push({ uri: `file://${cwd}` });
|
|
15355
|
+
}
|
|
15356
|
+
return roots.length > 0 ? roots : void 0;
|
|
15357
|
+
}
|
|
15358
|
+
async function loadRespondData(platform2, respondArg, json) {
|
|
15359
|
+
if (!respondArg) return { data: void 0 };
|
|
15360
|
+
try {
|
|
15361
|
+
return { data: await readDataArg(platform2, respondArg) };
|
|
15362
|
+
} catch (err) {
|
|
15363
|
+
await writeErr(
|
|
15364
|
+
platform2,
|
|
15365
|
+
json,
|
|
15366
|
+
"usage",
|
|
15367
|
+
`invalid --respond argument: ${err.message}`
|
|
15368
|
+
);
|
|
15369
|
+
return { exitCode: EXIT_USAGE };
|
|
15370
|
+
}
|
|
15371
|
+
}
|
|
15372
|
+
async function handleInputRequired(platform2, conn, err, respondData, json) {
|
|
15373
|
+
const parsed2 = JSON.parse(err.requestJson);
|
|
15374
|
+
const inputReq = parseInputRequest(parsed2.result ?? parsed2);
|
|
15375
|
+
if (!inputReq) {
|
|
15376
|
+
await platform2.write(2, `${err.requestJson}
|
|
15377
|
+
`);
|
|
15378
|
+
return EXIT_INPUT_REQUIRED;
|
|
15379
|
+
}
|
|
15380
|
+
const isTty = platform2.isatty(0);
|
|
15381
|
+
const elicitResult = await handleElicitation(platform2, inputReq, isTty, respondData);
|
|
15382
|
+
if ("exitCode" in elicitResult) {
|
|
15383
|
+
await platform2.write(2, `${err.requestJson}
|
|
15384
|
+
`);
|
|
15385
|
+
return EXIT_INPUT_REQUIRED;
|
|
15386
|
+
}
|
|
15387
|
+
try {
|
|
15388
|
+
const retryResult = await conn.callTool(parsed2.name, {
|
|
15389
|
+
...parsed2.arguments,
|
|
15390
|
+
_meta: { inputResponses: elicitResult.answers }
|
|
15391
|
+
});
|
|
15392
|
+
return renderCallResult(platform2, retryResult, { json });
|
|
15393
|
+
} catch (retryErr) {
|
|
15394
|
+
const e2 = classifyError(retryErr);
|
|
15395
|
+
await writeErr(platform2, json, e2.kind, e2.msg);
|
|
15396
|
+
return e2.code;
|
|
15397
|
+
}
|
|
15398
|
+
}
|
|
14031
15399
|
async function runClientVerb(platform2, version, parsed) {
|
|
14032
|
-
const { expanded, secrets } = expandAndCollect(parsed.headers, platform2.env);
|
|
15400
|
+
const { expanded, secrets, unset } = expandAndCollect(parsed.headers, platform2.env);
|
|
15401
|
+
if (unset.length > 0) {
|
|
15402
|
+
await platform2.write(
|
|
15403
|
+
2,
|
|
15404
|
+
`mcpcat: unset variable${unset.length > 1 ? "s" : ""} in -H: ${unset.join(", ")}
|
|
15405
|
+
`
|
|
15406
|
+
);
|
|
15407
|
+
return EXIT_USAGE;
|
|
15408
|
+
}
|
|
14033
15409
|
const verbose = createVerboseLogger(platform2, parsed.verbose);
|
|
14034
15410
|
for (const s of secrets) verbose.secrets.add(s);
|
|
14035
15411
|
const timeoutMs = parsed.timeoutMs;
|
|
14036
15412
|
const sigintController = new AbortController();
|
|
14037
15413
|
let interrupted = false;
|
|
15414
|
+
let termExitCode = EXIT_INTERRUPTED;
|
|
15415
|
+
let activeConn;
|
|
15416
|
+
async function killTreeAndExit(exitCode) {
|
|
15417
|
+
interrupted = true;
|
|
15418
|
+
termExitCode = exitCode;
|
|
15419
|
+
sigintController.abort();
|
|
15420
|
+
if (activeConn) {
|
|
15421
|
+
try {
|
|
15422
|
+
await Promise.race([activeConn.close(), new Promise((r) => setTimeout(r, 3e3))]);
|
|
15423
|
+
} catch {
|
|
15424
|
+
}
|
|
15425
|
+
}
|
|
15426
|
+
platform2.exit(exitCode);
|
|
15427
|
+
}
|
|
14038
15428
|
platform2.onSignal?.("SIGINT", () => {
|
|
14039
15429
|
interrupted = true;
|
|
14040
15430
|
sigintController.abort();
|
|
14041
15431
|
});
|
|
15432
|
+
platform2.onSignal?.("SIGTERM", () => void killTreeAndExit(143));
|
|
15433
|
+
platform2.onSignal?.("SIGHUP", () => void killTreeAndExit(129));
|
|
14042
15434
|
const showProgress = !parsed.silent && (parsed.verbose || platform2.isatty(2));
|
|
14043
15435
|
const onProgress = (progress, total, message) => {
|
|
14044
15436
|
if (!showProgress) return;
|
|
@@ -14057,50 +15449,91 @@ async function runClientVerb(platform2, version, parsed) {
|
|
|
14057
15449
|
await writeErr(platform2, parsed.jsonOutput, "usage", err.message);
|
|
14058
15450
|
return EXIT_USAGE;
|
|
14059
15451
|
}
|
|
14060
|
-
const
|
|
14061
|
-
|
|
15452
|
+
const configHeaders = expandHeaders(resolved.headers, platform2.env, (msg) => {
|
|
15453
|
+
platform2.write(2, `mcpcat: warning: ${msg}
|
|
15454
|
+
`).catch(() => {
|
|
15455
|
+
});
|
|
15456
|
+
});
|
|
15457
|
+
const configSecrets = collectConfigSecrets(resolved.headers, platform2.env);
|
|
15458
|
+
for (const s of configSecrets) verbose.secrets.add(s);
|
|
15459
|
+
const allSecrets = /* @__PURE__ */ new Set([...secrets, ...configSecrets]);
|
|
15460
|
+
const redact2 = (text) => {
|
|
15461
|
+
let result = text;
|
|
15462
|
+
for (const secret of allSecrets) {
|
|
15463
|
+
if (secret.length > 0) {
|
|
15464
|
+
while (result.includes(secret)) {
|
|
15465
|
+
result = result.replace(secret, "[REDACTED]");
|
|
15466
|
+
}
|
|
15467
|
+
}
|
|
15468
|
+
}
|
|
15469
|
+
return result;
|
|
15470
|
+
};
|
|
15471
|
+
const mergedHeaders = { ...configHeaders, ...expanded };
|
|
15472
|
+
const hasAuthHeader = "Authorization" in mergedHeaders;
|
|
15473
|
+
const authProvider = await resolveAuthProvider(
|
|
15474
|
+
platform2,
|
|
15475
|
+
hasAuthHeader,
|
|
15476
|
+
resolved.url,
|
|
15477
|
+
target,
|
|
15478
|
+
config
|
|
15479
|
+
);
|
|
15480
|
+
const roots = buildRoots(parsed.roots, !!resolved.command, platform2.cwd);
|
|
15481
|
+
let respondData;
|
|
15482
|
+
if (parsed.respondArg) {
|
|
15483
|
+
const loaded = await loadRespondData(platform2, parsed.respondArg, parsed.jsonOutput);
|
|
15484
|
+
if ("exitCode" in loaded) return loaded.exitCode;
|
|
15485
|
+
respondData = loaded.data;
|
|
15486
|
+
}
|
|
14062
15487
|
let conn;
|
|
14063
15488
|
try {
|
|
14064
15489
|
conn = await connect(platform2, {
|
|
14065
15490
|
...resolved,
|
|
14066
|
-
headers:
|
|
15491
|
+
headers: mergedHeaders,
|
|
14067
15492
|
protocolVersion: parsed.protocolVersion,
|
|
14068
15493
|
version,
|
|
14069
15494
|
timeoutMs,
|
|
14070
15495
|
signal: sigintController.signal,
|
|
14071
15496
|
verbose,
|
|
14072
|
-
authProvider
|
|
15497
|
+
authProvider,
|
|
15498
|
+
roots
|
|
14073
15499
|
});
|
|
15500
|
+
activeConn = conn;
|
|
14074
15501
|
} catch (err) {
|
|
14075
|
-
if (err
|
|
14076
|
-
|
|
14077
|
-
await writeErr(platform2, parsed.jsonOutput, e.kind, e.msg);
|
|
14078
|
-
return EXIT_AUTH;
|
|
14079
|
-
}
|
|
14080
|
-
if (err.name === "McpOAuthAuthorizationRequiredError" || err.name === "McpAuthRequiredError") {
|
|
14081
|
-
await writeErr(
|
|
15502
|
+
if (isSpawnNotFound(err)) {
|
|
15503
|
+
await writeErrRedacted(
|
|
14082
15504
|
platform2,
|
|
14083
15505
|
parsed.jsonOutput,
|
|
14084
|
-
"
|
|
14085
|
-
`
|
|
15506
|
+
"connection",
|
|
15507
|
+
`command not found: ${resolved.command}`,
|
|
15508
|
+
redact2
|
|
14086
15509
|
);
|
|
14087
|
-
return
|
|
15510
|
+
return EXIT_CONNECTION;
|
|
15511
|
+
}
|
|
15512
|
+
const e = classifyConnectionError(err, target, interrupted);
|
|
15513
|
+
if (e.code === EXIT_INTERRUPTED) {
|
|
15514
|
+
await platform2.write(2, "mcpcat: interrupted\n");
|
|
15515
|
+
return termExitCode;
|
|
14088
15516
|
}
|
|
14089
|
-
|
|
14090
|
-
|
|
14091
|
-
return EXIT_CONNECTION;
|
|
15517
|
+
await writeErrRedacted(platform2, parsed.jsonOutput, e.kind, e.msg, redact2);
|
|
15518
|
+
return e.code;
|
|
14092
15519
|
}
|
|
14093
15520
|
try {
|
|
14094
|
-
|
|
15521
|
+
const code2 = await runVerb(platform2, parsed, conn, onProgress, respondData);
|
|
15522
|
+
if (interrupted) {
|
|
15523
|
+
await platform2.write(2, "mcpcat: interrupted\n");
|
|
15524
|
+
return termExitCode;
|
|
15525
|
+
}
|
|
15526
|
+
return code2;
|
|
14095
15527
|
} catch (err) {
|
|
14096
15528
|
if (interrupted) {
|
|
14097
15529
|
await platform2.write(2, "mcpcat: interrupted\n");
|
|
14098
|
-
return
|
|
15530
|
+
return termExitCode;
|
|
15531
|
+
}
|
|
15532
|
+
if (err instanceof InputRequiredError) {
|
|
15533
|
+
return handleInputRequired(platform2, conn, err, respondData, parsed.jsonOutput);
|
|
14099
15534
|
}
|
|
14100
15535
|
const e = classifyError(err);
|
|
14101
|
-
|
|
14102
|
-
`);
|
|
14103
|
-
else await writeErr(platform2, parsed.jsonOutput, e.kind, e.msg);
|
|
15536
|
+
await writeErrRedacted(platform2, parsed.jsonOutput, e.kind, e.msg, redact2);
|
|
14104
15537
|
return e.code;
|
|
14105
15538
|
} finally {
|
|
14106
15539
|
try {
|
|
@@ -14114,53 +15547,85 @@ async function writeErr(p, json, kind, msg) {
|
|
|
14114
15547
|
else await p.write(2, `mcpcat: ${msg}
|
|
14115
15548
|
`);
|
|
14116
15549
|
}
|
|
15550
|
+
async function writeErrRedacted(p, json, kind, msg, redact2) {
|
|
15551
|
+
if (json) await renderErrorJson(p, kind, msg, void 0, void 0, redact2);
|
|
15552
|
+
else await p.write(2, `mcpcat: ${redact2(msg)}
|
|
15553
|
+
`);
|
|
15554
|
+
}
|
|
15555
|
+
function collectConfigSecrets(headers, env) {
|
|
15556
|
+
if (!headers) return [];
|
|
15557
|
+
const secrets = [];
|
|
15558
|
+
for (const [, v] of Object.entries(headers)) {
|
|
15559
|
+
const expanded = expandPlaceholders(v, env);
|
|
15560
|
+
if (expanded !== v && expanded.length > 0) secrets.push(expanded);
|
|
15561
|
+
if (v.length > 0 && v !== expanded) secrets.push(v);
|
|
15562
|
+
}
|
|
15563
|
+
return secrets;
|
|
15564
|
+
}
|
|
15565
|
+
function isSpawnNotFound(err) {
|
|
15566
|
+
if (!(err instanceof Error)) return false;
|
|
15567
|
+
const code2 = err.code;
|
|
15568
|
+
if (code2 === "ENOENT" || code2 === "EACCES") return true;
|
|
15569
|
+
const msg = err.message;
|
|
15570
|
+
return msg.includes("ENOENT") || msg.includes("EACCES");
|
|
15571
|
+
}
|
|
14117
15572
|
|
|
14118
15573
|
// src/platform/host.ts
|
|
14119
15574
|
import { spawn as cpSpawn } from "node:child_process";
|
|
14120
15575
|
import { constants } from "node:fs";
|
|
14121
|
-
import { access, mkdir, readFile as readFile3, rename, rmdir, stat, writeFile } from "node:fs/promises";
|
|
15576
|
+
import { access, mkdir, readFile as readFile3, rename, rmdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
14122
15577
|
import { createServer as createServer2 } from "node:net";
|
|
14123
15578
|
import { Readable, Writable } from "node:stream";
|
|
14124
15579
|
import * as tty from "node:tty";
|
|
14125
15580
|
var USE_PROCESS_GROUPS2 = process.platform !== "win32";
|
|
14126
15581
|
function nodeSpawn(options) {
|
|
14127
|
-
|
|
14128
|
-
|
|
14129
|
-
|
|
14130
|
-
|
|
14131
|
-
|
|
14132
|
-
|
|
14133
|
-
|
|
14134
|
-
|
|
14135
|
-
|
|
14136
|
-
|
|
14137
|
-
|
|
14138
|
-
|
|
14139
|
-
|
|
14140
|
-
|
|
14141
|
-
|
|
14142
|
-
|
|
14143
|
-
|
|
14144
|
-
|
|
14145
|
-
|
|
14146
|
-
|
|
15582
|
+
return new Promise((resolve, reject) => {
|
|
15583
|
+
let rejected = false;
|
|
15584
|
+
const child = cpSpawn(options.argv[0], options.argv.slice(1), {
|
|
15585
|
+
env: options.env ?? process.env,
|
|
15586
|
+
cwd: options.cwd,
|
|
15587
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
15588
|
+
detached: USE_PROCESS_GROUPS2
|
|
15589
|
+
});
|
|
15590
|
+
child.on("error", (err) => {
|
|
15591
|
+
if (!rejected) {
|
|
15592
|
+
rejected = true;
|
|
15593
|
+
reject(err);
|
|
15594
|
+
}
|
|
15595
|
+
});
|
|
15596
|
+
const stdout = Readable.toWeb(child.stdout);
|
|
15597
|
+
const stderr = Readable.toWeb(child.stderr);
|
|
15598
|
+
const stdin = Writable.toWeb(child.stdin);
|
|
15599
|
+
const closePromise = new Promise((res) => {
|
|
15600
|
+
child.on("close", (code2, signal) => {
|
|
15601
|
+
res({
|
|
15602
|
+
status: signal ? 128 + (signalNumber(signal) ?? 0) : code2 ?? 1,
|
|
15603
|
+
signal: signal ?? void 0
|
|
14147
15604
|
});
|
|
14148
15605
|
});
|
|
14149
|
-
}
|
|
14150
|
-
|
|
14151
|
-
|
|
14152
|
-
|
|
14153
|
-
|
|
14154
|
-
|
|
14155
|
-
|
|
14156
|
-
|
|
14157
|
-
|
|
15606
|
+
});
|
|
15607
|
+
if (child.pid !== void 0) {
|
|
15608
|
+
resolve({
|
|
15609
|
+
stdin,
|
|
15610
|
+
stdout,
|
|
15611
|
+
stderr,
|
|
15612
|
+
wait: () => closePromise,
|
|
15613
|
+
kill(signal) {
|
|
15614
|
+
const sig = signal ?? "SIGTERM";
|
|
15615
|
+
const pid = child.pid;
|
|
15616
|
+
if (USE_PROCESS_GROUPS2 && pid !== void 0) {
|
|
15617
|
+
try {
|
|
15618
|
+
process.kill(-pid, sig);
|
|
15619
|
+
return;
|
|
15620
|
+
} catch {
|
|
15621
|
+
}
|
|
15622
|
+
}
|
|
15623
|
+
try {
|
|
15624
|
+
child.kill(sig);
|
|
15625
|
+
} catch {
|
|
15626
|
+
}
|
|
14158
15627
|
}
|
|
14159
|
-
}
|
|
14160
|
-
try {
|
|
14161
|
-
child.kill(sig);
|
|
14162
|
-
} catch {
|
|
14163
|
-
}
|
|
15628
|
+
});
|
|
14164
15629
|
}
|
|
14165
15630
|
});
|
|
14166
15631
|
}
|
|
@@ -14181,8 +15646,11 @@ var hostFs = {
|
|
|
14181
15646
|
async writeFile(path, data, options) {
|
|
14182
15647
|
await writeFile(path, data, options?.mode !== void 0 ? { mode: options.mode } : void 0);
|
|
14183
15648
|
},
|
|
14184
|
-
async mkdir(path) {
|
|
14185
|
-
await mkdir(path, {
|
|
15649
|
+
async mkdir(path, options) {
|
|
15650
|
+
await mkdir(path, {
|
|
15651
|
+
recursive: true,
|
|
15652
|
+
...options?.mode !== void 0 ? { mode: options.mode } : {}
|
|
15653
|
+
});
|
|
14186
15654
|
},
|
|
14187
15655
|
async rename(from, to) {
|
|
14188
15656
|
await rename(from, to);
|
|
@@ -14204,6 +15672,9 @@ var hostFs = {
|
|
|
14204
15672
|
},
|
|
14205
15673
|
async rmdir(path) {
|
|
14206
15674
|
await rmdir(path);
|
|
15675
|
+
},
|
|
15676
|
+
async unlink(path) {
|
|
15677
|
+
await unlink(path);
|
|
14207
15678
|
}
|
|
14208
15679
|
};
|
|
14209
15680
|
function createStdinStream() {
|
|
@@ -14315,5 +15786,14 @@ function createHostPlatform() {
|
|
|
14315
15786
|
// src/host.ts
|
|
14316
15787
|
var platform = createHostPlatform();
|
|
14317
15788
|
var args = process.argv.slice(2);
|
|
14318
|
-
|
|
15789
|
+
try {
|
|
15790
|
+
process.stdout.on("error", () => {
|
|
15791
|
+
});
|
|
15792
|
+
process.stderr.on("error", () => {
|
|
15793
|
+
});
|
|
15794
|
+
process.on("SIGPIPE", () => {
|
|
15795
|
+
});
|
|
15796
|
+
} catch {
|
|
15797
|
+
}
|
|
15798
|
+
var code = await run(platform, "0.13.0", args);
|
|
14319
15799
|
process.exit(code);
|