@ai-ecoverse/mcpcat 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/THIRD_PARTY_NOTICES +173 -0
- package/dist/host/mcpcat.mjs +1695 -230
- package/dist/kernel/mcpcat.mjs +1824 -224
- package/package.json +2 -2
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,24 +9175,22 @@ 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
|
|
8647
|
-
const seaDir = getSeaDir?.();
|
|
8648
|
-
const wasmPath = seaDir ? `${seaDir}/quickjs.wasm` : await resolveWasmPath(platform2);
|
|
8649
|
-
const workerUrl = seaDir ? new URL(`file://${seaDir}/codemode-worker.mjs`) : new URL("./codemode-worker.mjs", import.meta.url);
|
|
9186
|
+
const wasmPath = await resolveWasmPath(platform2);
|
|
8650
9187
|
return new CodemodeSandbox2({
|
|
8651
9188
|
tools,
|
|
8652
9189
|
globals,
|
|
8653
9190
|
timeoutMs,
|
|
9191
|
+
memoryLimitBytes,
|
|
8654
9192
|
wasm: loadQuickJSWasm2(wasmPath),
|
|
8655
|
-
workerUrl
|
|
9193
|
+
workerUrl: new URL("./codemode-worker.mjs", import.meta.url)
|
|
8656
9194
|
});
|
|
8657
9195
|
}
|
|
8658
9196
|
async function resolveWasmPath(platform2) {
|
|
@@ -8726,6 +9264,14 @@ function containsPlaceholders(value) {
|
|
|
8726
9264
|
function expandPlaceholders(value, env) {
|
|
8727
9265
|
return value.replace(PLACEHOLDER, (_, name) => env[name] ?? "");
|
|
8728
9266
|
}
|
|
9267
|
+
function missingPlaceholders(value, env) {
|
|
9268
|
+
const missing = [];
|
|
9269
|
+
for (const m of value.matchAll(PLACEHOLDER)) {
|
|
9270
|
+
const name = m[1];
|
|
9271
|
+
if (env[name] === void 0 && !missing.includes(name)) missing.push(name);
|
|
9272
|
+
}
|
|
9273
|
+
return missing;
|
|
9274
|
+
}
|
|
8729
9275
|
function expandPlaceholdersStrict(value, env) {
|
|
8730
9276
|
let hasUndefined = false;
|
|
8731
9277
|
const result = value.replace(PLACEHOLDER, (_, name) => {
|
|
@@ -8738,6 +9284,20 @@ function expandPlaceholdersStrict(value, env) {
|
|
|
8738
9284
|
if (hasUndefined) return void 0;
|
|
8739
9285
|
return result;
|
|
8740
9286
|
}
|
|
9287
|
+
function expandHeaders(headers, env, warn) {
|
|
9288
|
+
if (!headers) return void 0;
|
|
9289
|
+
const result = {};
|
|
9290
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
9291
|
+
const missing = missingPlaceholders(v, env);
|
|
9292
|
+
if (missing.length > 0) {
|
|
9293
|
+
if (warn)
|
|
9294
|
+
warn(`header "${k}": unset variable${missing.length > 1 ? "s" : ""} ${missing.join(", ")}`);
|
|
9295
|
+
continue;
|
|
9296
|
+
}
|
|
9297
|
+
result[k] = expandPlaceholders(v, env);
|
|
9298
|
+
}
|
|
9299
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
9300
|
+
}
|
|
8741
9301
|
|
|
8742
9302
|
// src/config/parse.ts
|
|
8743
9303
|
var SERVER_NAME = /^[A-Za-z0-9_-]+$/;
|
|
@@ -8928,7 +9488,12 @@ async function readFile2(fs, path) {
|
|
|
8928
9488
|
try {
|
|
8929
9489
|
const data = await fs.readFile(path);
|
|
8930
9490
|
return new TextDecoder().decode(data);
|
|
8931
|
-
} catch {
|
|
9491
|
+
} catch (err) {
|
|
9492
|
+
if (err.code === "EACCES") {
|
|
9493
|
+
const e = new Error(`permission denied: ${path}`);
|
|
9494
|
+
e.code = "EACCES";
|
|
9495
|
+
throw e;
|
|
9496
|
+
}
|
|
8932
9497
|
return void 0;
|
|
8933
9498
|
}
|
|
8934
9499
|
}
|
|
@@ -9093,6 +9658,13 @@ async function removeStaleLock(fs, lockPath) {
|
|
|
9093
9658
|
async function acquireLock(fs, configPath) {
|
|
9094
9659
|
const lockPath = `${configPath}.lock`;
|
|
9095
9660
|
const start = Date.now();
|
|
9661
|
+
const parentDir = lockPath.slice(0, lockPath.lastIndexOf("/"));
|
|
9662
|
+
if (parentDir) {
|
|
9663
|
+
try {
|
|
9664
|
+
await fs.mkdir(parentDir);
|
|
9665
|
+
} catch {
|
|
9666
|
+
}
|
|
9667
|
+
}
|
|
9096
9668
|
while (true) {
|
|
9097
9669
|
try {
|
|
9098
9670
|
await fs.mkdirExclusive(lockPath);
|
|
@@ -9154,7 +9726,15 @@ async function editMcpServers(fs, path, edit) {
|
|
|
9154
9726
|
}
|
|
9155
9727
|
const tmpPath = `${path}.tmp.${Date.now()}`;
|
|
9156
9728
|
await fs.writeFile(tmpPath, output);
|
|
9157
|
-
|
|
9729
|
+
try {
|
|
9730
|
+
await fs.rename(tmpPath, path);
|
|
9731
|
+
} catch (renameErr) {
|
|
9732
|
+
try {
|
|
9733
|
+
if (fs.unlink) await fs.unlink(tmpPath);
|
|
9734
|
+
} catch {
|
|
9735
|
+
}
|
|
9736
|
+
throw renameErr;
|
|
9737
|
+
}
|
|
9158
9738
|
return { changed: true };
|
|
9159
9739
|
} finally {
|
|
9160
9740
|
await release();
|
|
@@ -9531,6 +10111,31 @@ async function getCandidate(fs, stateDir2, id) {
|
|
|
9531
10111
|
}
|
|
9532
10112
|
|
|
9533
10113
|
// src/discovery/probe.ts
|
|
10114
|
+
var LOOPBACK_RE = /^(127\.\d+\.\d+\.\d+|\[?::1\]?|localhost)$/i;
|
|
10115
|
+
var LINK_LOCAL_RE = /^(169\.254\.|fe80:)/i;
|
|
10116
|
+
var PRIVATE_RE = /^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.|fc|fd)/i;
|
|
10117
|
+
var LOCALHOST_SUFFIX_RE = /\.localhost$/i;
|
|
10118
|
+
var SLICC_INTERNAL_RE = /^host\.slicc\.internal$/i;
|
|
10119
|
+
function isPrivateOrLoopback(hostname) {
|
|
10120
|
+
const bare = hostname.replace(/^\[|\]$/g, "");
|
|
10121
|
+
return LOOPBACK_RE.test(bare) || LINK_LOCAL_RE.test(bare) || PRIVATE_RE.test(bare) || LOCALHOST_SUFFIX_RE.test(bare) || SLICC_INTERNAL_RE.test(bare);
|
|
10122
|
+
}
|
|
10123
|
+
function isSameOrigin(base, candidate) {
|
|
10124
|
+
try {
|
|
10125
|
+
const a = new URL(base);
|
|
10126
|
+
const b = new URL(candidate);
|
|
10127
|
+
return a.origin === b.origin;
|
|
10128
|
+
} catch {
|
|
10129
|
+
return false;
|
|
10130
|
+
}
|
|
10131
|
+
}
|
|
10132
|
+
function isUserUrlPrivate(userUrl) {
|
|
10133
|
+
try {
|
|
10134
|
+
return isPrivateOrLoopback(new URL(userUrl).hostname);
|
|
10135
|
+
} catch {
|
|
10136
|
+
return false;
|
|
10137
|
+
}
|
|
10138
|
+
}
|
|
9534
10139
|
function isJsonContentType(ct) {
|
|
9535
10140
|
if (!ct) return false;
|
|
9536
10141
|
const lower = ct.toLowerCase();
|
|
@@ -9665,13 +10270,44 @@ async function probeWellKnown(fetcher, origin, page) {
|
|
|
9665
10270
|
}
|
|
9666
10271
|
return events;
|
|
9667
10272
|
}
|
|
9668
|
-
async function probeLinkHeaders(fetcher, targetUrl, origin, page) {
|
|
10273
|
+
async function probeLinkHeaders(fetcher, targetUrl, origin, page, userUrlIsPrivate) {
|
|
9669
10274
|
const events = [];
|
|
9670
10275
|
const targetResult = await probeUrl(fetcher, targetUrl);
|
|
9671
10276
|
if (!targetResult) return events;
|
|
9672
10277
|
const links = parseLinkHeaders(targetResult.headers);
|
|
9673
10278
|
for (const link of links) {
|
|
9674
10279
|
const resolved = new URL(link.url, targetUrl).href;
|
|
10280
|
+
if (!isSameOrigin(targetUrl, resolved)) {
|
|
10281
|
+
events.push(
|
|
10282
|
+
makeEvent(
|
|
10283
|
+
link.rel,
|
|
10284
|
+
origin,
|
|
10285
|
+
resolved,
|
|
10286
|
+
"link-header",
|
|
10287
|
+
page,
|
|
10288
|
+
"invalid",
|
|
10289
|
+
null,
|
|
10290
|
+
`skipped: cross-origin Link target (${new URL(resolved).origin})`
|
|
10291
|
+
)
|
|
10292
|
+
);
|
|
10293
|
+
continue;
|
|
10294
|
+
}
|
|
10295
|
+
const resolvedHost = new URL(resolved).hostname;
|
|
10296
|
+
if (!userUrlIsPrivate && isPrivateOrLoopback(resolvedHost)) {
|
|
10297
|
+
events.push(
|
|
10298
|
+
makeEvent(
|
|
10299
|
+
link.rel,
|
|
10300
|
+
origin,
|
|
10301
|
+
resolved,
|
|
10302
|
+
"link-header",
|
|
10303
|
+
page,
|
|
10304
|
+
"invalid",
|
|
10305
|
+
null,
|
|
10306
|
+
`skipped: private/loopback target (${resolvedHost})`
|
|
10307
|
+
)
|
|
10308
|
+
);
|
|
10309
|
+
continue;
|
|
10310
|
+
}
|
|
9675
10311
|
const classifier = link.rel === "ard" ? classifyArd : link.rel === "ai-catalog" ? classifyAiCatalog : null;
|
|
9676
10312
|
if (!classifier) continue;
|
|
9677
10313
|
const linkResult = await probeUrl(fetcher, resolved, "application/json");
|
|
@@ -9768,9 +10404,10 @@ async function probeDiscover(platform2, targetUrl) {
|
|
|
9768
10404
|
}
|
|
9769
10405
|
const page = targetUrl;
|
|
9770
10406
|
const fetcher = platform2.fetch;
|
|
10407
|
+
const userPrivate = isUserUrlPrivate(targetUrl);
|
|
9771
10408
|
const events = [
|
|
9772
10409
|
...await probeWellKnown(fetcher, origin, page),
|
|
9773
|
-
...await probeLinkHeaders(fetcher, targetUrl, origin, page),
|
|
10410
|
+
...await probeLinkHeaders(fetcher, targetUrl, origin, page, userPrivate),
|
|
9774
10411
|
...await probeMcp(fetcher, origin, page, targetUrl)
|
|
9775
10412
|
];
|
|
9776
10413
|
return { events };
|
|
@@ -10027,13 +10664,27 @@ function renderCallResultJson(result) {
|
|
|
10027
10664
|
return JSON.stringify(result, null, 2);
|
|
10028
10665
|
}
|
|
10029
10666
|
async function renderCallResult(platform2, result, options) {
|
|
10667
|
+
const r = options?.redact ?? ((s) => s);
|
|
10668
|
+
if (options?.outputPath) {
|
|
10669
|
+
const content = options.json ? renderCallResultJson(result) : renderCallResultText(result, options);
|
|
10670
|
+
await platform2.fs.writeFile(options.outputPath, content);
|
|
10671
|
+
if (result.isError) {
|
|
10672
|
+
await platform2.write(
|
|
10673
|
+
2,
|
|
10674
|
+
r(`${result.isError ? "(tool reported error)" : ""}
|
|
10675
|
+
`)
|
|
10676
|
+
);
|
|
10677
|
+
return 1;
|
|
10678
|
+
}
|
|
10679
|
+
return 0;
|
|
10680
|
+
}
|
|
10030
10681
|
if (options?.json) {
|
|
10031
10682
|
const json = renderCallResultJson(result);
|
|
10032
10683
|
if (result.isError) {
|
|
10033
10684
|
await platform2.write(
|
|
10034
10685
|
2,
|
|
10035
|
-
`${JSON.stringify({ error: { kind: "tool_error", message: json } }, null, 2)}
|
|
10036
|
-
`
|
|
10686
|
+
r(`${JSON.stringify({ error: { kind: "tool_error", message: json } }, null, 2)}
|
|
10687
|
+
`)
|
|
10037
10688
|
);
|
|
10038
10689
|
return 1;
|
|
10039
10690
|
}
|
|
@@ -10043,8 +10694,8 @@ async function renderCallResult(platform2, result, options) {
|
|
|
10043
10694
|
}
|
|
10044
10695
|
const text = renderCallResultText(result, options);
|
|
10045
10696
|
if (result.isError) {
|
|
10046
|
-
await platform2.write(2, `${text || "(tool reported error)"}
|
|
10047
|
-
`);
|
|
10697
|
+
await platform2.write(2, r(`${text || "(tool reported error)"}
|
|
10698
|
+
`));
|
|
10048
10699
|
return 1;
|
|
10049
10700
|
}
|
|
10050
10701
|
if (text) {
|
|
@@ -10061,13 +10712,33 @@ async function renderInfoJson(platform2, info) {
|
|
|
10061
10712
|
await platform2.write(1, `${JSON.stringify(info, null, 2)}
|
|
10062
10713
|
`);
|
|
10063
10714
|
}
|
|
10064
|
-
async function renderErrorJson(platform2, kind, message, code2, data) {
|
|
10065
|
-
const
|
|
10715
|
+
async function renderErrorJson(platform2, kind, message, code2, data, redact2) {
|
|
10716
|
+
const r = redact2 ?? ((s) => s);
|
|
10717
|
+
const errorObj = { kind, message: r(message) };
|
|
10066
10718
|
if (code2 !== void 0) errorObj.code = code2;
|
|
10067
10719
|
if (data !== void 0) errorObj.data = data;
|
|
10068
10720
|
await platform2.write(2, `${JSON.stringify({ error: errorObj }, null, 2)}
|
|
10069
10721
|
`);
|
|
10070
10722
|
}
|
|
10723
|
+
async function renderResourcesJson(platform2, resources, templates) {
|
|
10724
|
+
await platform2.write(
|
|
10725
|
+
1,
|
|
10726
|
+
`${JSON.stringify({ resources, resourceTemplates: templates }, null, 2)}
|
|
10727
|
+
`
|
|
10728
|
+
);
|
|
10729
|
+
}
|
|
10730
|
+
async function renderPromptsJson(platform2, prompts) {
|
|
10731
|
+
await platform2.write(1, `${JSON.stringify({ prompts }, null, 2)}
|
|
10732
|
+
`);
|
|
10733
|
+
}
|
|
10734
|
+
async function renderPromptResultJson(platform2, result) {
|
|
10735
|
+
await platform2.write(1, `${JSON.stringify(result, null, 2)}
|
|
10736
|
+
`);
|
|
10737
|
+
}
|
|
10738
|
+
async function renderReadResourceJson(platform2, result) {
|
|
10739
|
+
await platform2.write(1, `${JSON.stringify(result, null, 2)}
|
|
10740
|
+
`);
|
|
10741
|
+
}
|
|
10071
10742
|
|
|
10072
10743
|
// src/serve/catalog.ts
|
|
10073
10744
|
var NAME_RE = /^[A-Za-z][\w-]*$/;
|
|
@@ -10835,38 +11506,46 @@ function hasOwn(value, key) {
|
|
|
10835
11506
|
}
|
|
10836
11507
|
|
|
10837
11508
|
// src/serve/origin.ts
|
|
10838
|
-
|
|
10839
|
-
|
|
10840
|
-
try {
|
|
10841
|
-
const url = new URL(origin);
|
|
10842
|
-
const hostname = url.hostname;
|
|
10843
|
-
if (LOOPBACK_HOSTS.has(hostname)) return true;
|
|
10844
|
-
if (hostname === "::1") return true;
|
|
10845
|
-
if (hostname.endsWith(".localhost")) return true;
|
|
10846
|
-
return false;
|
|
10847
|
-
} catch {
|
|
10848
|
-
return false;
|
|
10849
|
-
}
|
|
11509
|
+
function isLoopbackBind(host) {
|
|
11510
|
+
return host === "127.0.0.1" || host === "localhost" || host === "::1" || host === "[::1]";
|
|
10850
11511
|
}
|
|
10851
|
-
function
|
|
10852
|
-
const
|
|
10853
|
-
|
|
10854
|
-
|
|
10855
|
-
if (
|
|
10856
|
-
|
|
11512
|
+
function buildServerOrigins(host, port) {
|
|
11513
|
+
const origins = /* @__PURE__ */ new Set();
|
|
11514
|
+
origins.add(`http://${host}:${port}`);
|
|
11515
|
+
origins.add(`http://${port}.kernel.localhost`);
|
|
11516
|
+
if (isLoopbackBind(host)) {
|
|
11517
|
+
origins.add(`http://localhost:${port}`);
|
|
11518
|
+
origins.add(`http://127.0.0.1:${port}`);
|
|
11519
|
+
origins.add(`http://[::1]:${port}`);
|
|
11520
|
+
}
|
|
11521
|
+
return origins;
|
|
10857
11522
|
}
|
|
10858
|
-
function checkOrigin(origin,
|
|
11523
|
+
function checkOrigin(origin, serverOrigins, allowedOrigins) {
|
|
10859
11524
|
if (origin === void 0) return { ok: true };
|
|
11525
|
+
if (serverOrigins.has(origin)) return { ok: true };
|
|
10860
11526
|
if (allowedOrigins.has(origin)) return { ok: true };
|
|
10861
|
-
if (isLoopbackOrigin(origin)) return { ok: true };
|
|
10862
|
-
if (!boundLoopback) return { ok: true };
|
|
10863
11527
|
return { ok: false, reason: `origin ${origin} is not allowed` };
|
|
10864
11528
|
}
|
|
10865
|
-
function
|
|
10866
|
-
|
|
11529
|
+
function buildAllowedHosts(host, port, extraHosts) {
|
|
11530
|
+
const hosts = /* @__PURE__ */ new Set();
|
|
11531
|
+
hosts.add(`${host}:${port}`);
|
|
11532
|
+
hosts.add(host);
|
|
11533
|
+
hosts.add(`${port}.kernel.localhost`);
|
|
11534
|
+
const loopback2 = ["localhost", "127.0.0.1", "[::1]"];
|
|
11535
|
+
for (const h of loopback2) {
|
|
11536
|
+
hosts.add(h);
|
|
11537
|
+
hosts.add(`${h}:${port}`);
|
|
11538
|
+
}
|
|
11539
|
+
for (const h of extraHosts) hosts.add(h);
|
|
11540
|
+
return hosts;
|
|
11541
|
+
}
|
|
11542
|
+
function checkHost(host, allowedHosts) {
|
|
10867
11543
|
if (!host) return { ok: false, reason: "missing Host header" };
|
|
10868
|
-
if (
|
|
10869
|
-
|
|
11544
|
+
if (allowedHosts.has(host)) return { ok: true };
|
|
11545
|
+
const hostname = host.replace(/:\d+$/, "");
|
|
11546
|
+
if (allowedHosts.has(hostname)) return { ok: true };
|
|
11547
|
+
if (hostname.endsWith(".localhost")) return { ok: true };
|
|
11548
|
+
return { ok: false, reason: `host ${host} is not allowed` };
|
|
10870
11549
|
}
|
|
10871
11550
|
|
|
10872
11551
|
// src/serve/proxy.ts
|
|
@@ -11103,6 +11782,8 @@ function parseSingleFlag(args2, i, opts) {
|
|
|
11103
11782
|
return parsed.next;
|
|
11104
11783
|
} else if (arg === "--json") {
|
|
11105
11784
|
opts.json = true;
|
|
11785
|
+
} else if (arg === "--no-auth") {
|
|
11786
|
+
opts.noAuth = true;
|
|
11106
11787
|
} else if (!arg.startsWith("-")) {
|
|
11107
11788
|
opts.commands.push(parseCommandTarget(arg));
|
|
11108
11789
|
} else {
|
|
@@ -11123,6 +11804,9 @@ function parseValueFlag(args2, i, opts) {
|
|
|
11123
11804
|
case "--allow-origin":
|
|
11124
11805
|
opts.allowOrigins.push(args2[++i]);
|
|
11125
11806
|
break;
|
|
11807
|
+
case "--allow-host":
|
|
11808
|
+
opts.allowHosts.push(args2[++i]);
|
|
11809
|
+
break;
|
|
11126
11810
|
case "--env":
|
|
11127
11811
|
opts.envAllow.push(args2[++i]);
|
|
11128
11812
|
break;
|
|
@@ -11161,7 +11845,9 @@ function parseServeFlags(args2) {
|
|
|
11161
11845
|
host: "127.0.0.1",
|
|
11162
11846
|
port: 0,
|
|
11163
11847
|
bearer: void 0,
|
|
11848
|
+
noAuth: false,
|
|
11164
11849
|
allowOrigins: [],
|
|
11850
|
+
allowHosts: [],
|
|
11165
11851
|
envAllow: [],
|
|
11166
11852
|
toolTemplates: [],
|
|
11167
11853
|
callTimeout: CALL_TIMEOUT_MS,
|
|
@@ -11190,6 +11876,20 @@ function constantTimeEqual(a, b) {
|
|
|
11190
11876
|
}
|
|
11191
11877
|
return mismatch === 0;
|
|
11192
11878
|
}
|
|
11879
|
+
function generateToken() {
|
|
11880
|
+
const bytes = new Uint8Array(32);
|
|
11881
|
+
crypto.getRandomValues(bytes);
|
|
11882
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
11883
|
+
}
|
|
11884
|
+
function checkContentType(ct) {
|
|
11885
|
+
if (!ct) return false;
|
|
11886
|
+
return ct.toLowerCase().startsWith("application/json");
|
|
11887
|
+
}
|
|
11888
|
+
function checkAcceptHeader(accept) {
|
|
11889
|
+
if (!accept) return false;
|
|
11890
|
+
const lower = accept.toLowerCase();
|
|
11891
|
+
return lower.includes("application/json") || lower.includes("text/event-stream") || lower.includes("*/*");
|
|
11892
|
+
}
|
|
11193
11893
|
function buildChildEnv(platform2, envAllow) {
|
|
11194
11894
|
const env = {};
|
|
11195
11895
|
for (const key of ALLOWED_ENV_KEYS) {
|
|
@@ -11238,7 +11938,7 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
|
|
|
11238
11938
|
}
|
|
11239
11939
|
const stdoutChunks = [];
|
|
11240
11940
|
const stderrChunks = [];
|
|
11241
|
-
const
|
|
11941
|
+
const readStream3 = async (stream, into) => {
|
|
11242
11942
|
const r = stream.getReader();
|
|
11243
11943
|
try {
|
|
11244
11944
|
for (; ; ) {
|
|
@@ -11254,8 +11954,8 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
|
|
|
11254
11954
|
(resolve) => setTimeout(() => resolve("timeout"), timeoutMs)
|
|
11255
11955
|
);
|
|
11256
11956
|
const work = Promise.all([
|
|
11257
|
-
|
|
11258
|
-
|
|
11957
|
+
readStream3(proc.stdout, stdoutChunks),
|
|
11958
|
+
readStream3(proc.stderr, stderrChunks),
|
|
11259
11959
|
proc.wait()
|
|
11260
11960
|
]);
|
|
11261
11961
|
const result = await Promise.race([work, timer]);
|
|
@@ -11487,7 +12187,10 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
|
|
|
11487
12187
|
}
|
|
11488
12188
|
async function runServe(platform2, options, version) {
|
|
11489
12189
|
let resolvedBearer;
|
|
11490
|
-
|
|
12190
|
+
let generatedBearer = false;
|
|
12191
|
+
if (options._resolvedBearer) {
|
|
12192
|
+
resolvedBearer = options._resolvedBearer;
|
|
12193
|
+
} else if (options.bearer) {
|
|
11491
12194
|
const expanded = expandBearerToken(options.bearer, platform2.env);
|
|
11492
12195
|
if (!expanded) {
|
|
11493
12196
|
await platform2.write(
|
|
@@ -11497,9 +12200,31 @@ async function runServe(platform2, options, version) {
|
|
|
11497
12200
|
return 2;
|
|
11498
12201
|
}
|
|
11499
12202
|
resolvedBearer = expanded;
|
|
12203
|
+
} else if (options.noAuth) {
|
|
12204
|
+
if (options.transport === "http" && !isLoopbackBind(options.host)) {
|
|
12205
|
+
await platform2.write(2, "mcpcat serve: --no-auth is only allowed on loopback\n");
|
|
12206
|
+
return 2;
|
|
12207
|
+
}
|
|
12208
|
+
if (options.transport === "http") {
|
|
12209
|
+
await platform2.write(
|
|
12210
|
+
2,
|
|
12211
|
+
"mcpcat serve: warning: running without authentication on loopback\n"
|
|
12212
|
+
);
|
|
12213
|
+
}
|
|
12214
|
+
resolvedBearer = void 0;
|
|
12215
|
+
} else if (options.transport === "http") {
|
|
12216
|
+
if (platform2.kind === "kernel") {
|
|
12217
|
+
await platform2.write(
|
|
12218
|
+
2,
|
|
12219
|
+
"mcpcat serve: kernel HTTP serve requires --bearer '${VAR}' or --no-auth\n"
|
|
12220
|
+
);
|
|
12221
|
+
return 2;
|
|
12222
|
+
}
|
|
12223
|
+
resolvedBearer = generateToken();
|
|
12224
|
+
generatedBearer = true;
|
|
11500
12225
|
}
|
|
11501
12226
|
if (options.proxy) {
|
|
11502
|
-
return runProxyServe(platform2, options, version, resolvedBearer);
|
|
12227
|
+
return runProxyServe(platform2, options, version, resolvedBearer, generatedBearer);
|
|
11503
12228
|
}
|
|
11504
12229
|
const serveCwd = options.cwd ?? platform2.cwd;
|
|
11505
12230
|
const childEnv = buildChildEnv(platform2, options.envAllow);
|
|
@@ -11536,12 +12261,35 @@ async function runServe(platform2, options, version) {
|
|
|
11536
12261
|
executor.invoke,
|
|
11537
12262
|
serverName,
|
|
11538
12263
|
version,
|
|
11539
|
-
resolvedBearer
|
|
12264
|
+
resolvedBearer,
|
|
12265
|
+
generatedBearer
|
|
11540
12266
|
);
|
|
11541
12267
|
}
|
|
12268
|
+
async function publishBearer(platform2, token, port, listenUrl, json) {
|
|
12269
|
+
if (!json && platform2.isatty(2)) {
|
|
12270
|
+
await platform2.write(2, `mcpcat serve: bearer ${token}
|
|
12271
|
+
`);
|
|
12272
|
+
return void 0;
|
|
12273
|
+
}
|
|
12274
|
+
const dir = stateDir(platform2);
|
|
12275
|
+
await platform2.fs.mkdir(dir, { mode: 448 });
|
|
12276
|
+
const tokenPath = `${dir}/serve-${port}.token`;
|
|
12277
|
+
try {
|
|
12278
|
+
await platform2.fs.unlink?.(tokenPath);
|
|
12279
|
+
} catch {
|
|
12280
|
+
}
|
|
12281
|
+
await platform2.fs.writeFile(tokenPath, token, { mode: 384 });
|
|
12282
|
+
await platform2.write(2, `mcpcat serve: bearer saved to ${tokenPath}
|
|
12283
|
+
`);
|
|
12284
|
+
if (json) {
|
|
12285
|
+
await platform2.write(1, `${JSON.stringify({ url: listenUrl, bearerFile: tokenPath })}
|
|
12286
|
+
`);
|
|
12287
|
+
}
|
|
12288
|
+
return tokenPath;
|
|
12289
|
+
}
|
|
11542
12290
|
function resolveProxyStdio(target, version) {
|
|
11543
12291
|
const rest = target.slice(6);
|
|
11544
|
-
const parts = rest
|
|
12292
|
+
const parts = splitShellStyle(rest);
|
|
11545
12293
|
return { command: parts[0], args: parts.slice(1), version };
|
|
11546
12294
|
}
|
|
11547
12295
|
function resolveProxyUrl(target, headers, version) {
|
|
@@ -11611,7 +12359,7 @@ async function buildProxyConnectOpts(platform2, options, version) {
|
|
|
11611
12359
|
}
|
|
11612
12360
|
return { connectOpts, hiddenValues };
|
|
11613
12361
|
}
|
|
11614
|
-
async function runProxyServe(platform2, options, version, resolvedBearer) {
|
|
12362
|
+
async function runProxyServe(platform2, options, version, resolvedBearer, generatedBearer) {
|
|
11615
12363
|
const built = await buildProxyConnectOpts(platform2, options, version);
|
|
11616
12364
|
if ("error" in built) {
|
|
11617
12365
|
await platform2.write(2, `mcpcat serve --proxy: ${built.error}
|
|
@@ -11629,7 +12377,7 @@ async function runProxyServe(platform2, options, version, resolvedBearer) {
|
|
|
11629
12377
|
if (options.transport === "stdio") {
|
|
11630
12378
|
return runProxyStdio(platform2, proxy);
|
|
11631
12379
|
}
|
|
11632
|
-
return runProxyHttp(platform2, options, proxy, resolvedBearer);
|
|
12380
|
+
return runProxyHttp(platform2, options, proxy, resolvedBearer, generatedBearer);
|
|
11633
12381
|
}
|
|
11634
12382
|
async function runProxyStdio(platform2, proxy) {
|
|
11635
12383
|
const reader = platform2.stdin.getReader();
|
|
@@ -11660,12 +12408,12 @@ async function runProxyStdio(platform2, proxy) {
|
|
|
11660
12408
|
}
|
|
11661
12409
|
return 0;
|
|
11662
12410
|
}
|
|
11663
|
-
async function runProxyHttp(platform2, options, proxy, resolvedBearer) {
|
|
12411
|
+
async function runProxyHttp(platform2, options, proxy, resolvedBearer, generatedBearer) {
|
|
11664
12412
|
if (!platform2.listen) {
|
|
11665
12413
|
await platform2.write(2, "mcpcat serve --proxy: HTTP transport requires platform.listen\n");
|
|
11666
12414
|
return 3;
|
|
11667
12415
|
}
|
|
11668
|
-
const boundLoopback = options.host
|
|
12416
|
+
const boundLoopback = isLoopbackBind(options.host);
|
|
11669
12417
|
if (!boundLoopback && !resolvedBearer) {
|
|
11670
12418
|
await platform2.write(
|
|
11671
12419
|
2,
|
|
@@ -11679,10 +12427,23 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer) {
|
|
|
11679
12427
|
port: options.port
|
|
11680
12428
|
});
|
|
11681
12429
|
const actualPort = listener.port;
|
|
12430
|
+
const serverOrigins = buildServerOrigins(options.host, actualPort);
|
|
12431
|
+
const allowedHosts = buildAllowedHosts(options.host, actualPort, options.allowHosts);
|
|
11682
12432
|
const listenUrl = `http://${options.host}:${actualPort}/mcp`;
|
|
11683
|
-
|
|
11684
|
-
|
|
12433
|
+
let tokenFilePath;
|
|
12434
|
+
if (generatedBearer && resolvedBearer) {
|
|
12435
|
+
tokenFilePath = await publishBearer(
|
|
12436
|
+
platform2,
|
|
12437
|
+
resolvedBearer,
|
|
12438
|
+
actualPort,
|
|
12439
|
+
listenUrl,
|
|
12440
|
+
options.json
|
|
12441
|
+
);
|
|
12442
|
+
} else {
|
|
12443
|
+
if (options.json) {
|
|
12444
|
+
await platform2.write(1, `${JSON.stringify({ url: listenUrl })}
|
|
11685
12445
|
`);
|
|
12446
|
+
}
|
|
11686
12447
|
}
|
|
11687
12448
|
await platform2.write(2, `mcpcat serve --proxy: listening on ${listenUrl}
|
|
11688
12449
|
`);
|
|
@@ -11695,12 +12456,23 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer) {
|
|
|
11695
12456
|
shutdownRequested = true;
|
|
11696
12457
|
listener.close();
|
|
11697
12458
|
void proxy.shutdown();
|
|
12459
|
+
if (tokenFilePath) {
|
|
12460
|
+
platform2.fs.unlink?.(tokenFilePath).catch(() => {
|
|
12461
|
+
});
|
|
12462
|
+
}
|
|
11698
12463
|
};
|
|
11699
12464
|
if (platform2.onSignal) {
|
|
11700
12465
|
platform2.onSignal("SIGINT", shutdown);
|
|
11701
12466
|
platform2.onSignal("SIGTERM", shutdown);
|
|
11702
12467
|
}
|
|
11703
|
-
const handler = (request) => handleProxyMcpRequest(
|
|
12468
|
+
const handler = (request) => handleProxyMcpRequest(
|
|
12469
|
+
request,
|
|
12470
|
+
proxy,
|
|
12471
|
+
serverOrigins,
|
|
12472
|
+
allowedOrigins,
|
|
12473
|
+
allowedHosts,
|
|
12474
|
+
resolvedBearer
|
|
12475
|
+
);
|
|
11704
12476
|
try {
|
|
11705
12477
|
for await (const connection of listener) {
|
|
11706
12478
|
if (shutdownRequested) break;
|
|
@@ -11718,20 +12490,40 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer) {
|
|
|
11718
12490
|
}
|
|
11719
12491
|
return 0;
|
|
11720
12492
|
}
|
|
11721
|
-
async function handleProxyMcpRequest(request, proxy, allowedOrigins,
|
|
11722
|
-
if (
|
|
11723
|
-
|
|
11724
|
-
|
|
11725
|
-
|
|
11726
|
-
|
|
11727
|
-
|
|
11728
|
-
|
|
11729
|
-
|
|
11730
|
-
|
|
11731
|
-
|
|
11732
|
-
|
|
12493
|
+
async function handleProxyMcpRequest(request, proxy, serverOrigins, allowedOrigins, allowedHosts, resolvedBearer) {
|
|
12494
|
+
if (request.target !== "/mcp") {
|
|
12495
|
+
return {
|
|
12496
|
+
status: 404,
|
|
12497
|
+
reason: "Not Found",
|
|
12498
|
+
headers: { "content-type": "text/plain" },
|
|
12499
|
+
body: "Not Found"
|
|
12500
|
+
};
|
|
12501
|
+
}
|
|
12502
|
+
if (request.method !== "POST") {
|
|
12503
|
+
return {
|
|
12504
|
+
status: 405,
|
|
12505
|
+
reason: "Method Not Allowed",
|
|
12506
|
+
headers: { "content-type": "text/plain", allow: "POST" },
|
|
12507
|
+
body: "Method Not Allowed"
|
|
12508
|
+
};
|
|
12509
|
+
}
|
|
12510
|
+
if (!checkContentType(request.headers["content-type"])) {
|
|
12511
|
+
return {
|
|
12512
|
+
status: 415,
|
|
12513
|
+
reason: "Unsupported Media Type",
|
|
12514
|
+
headers: { "content-type": "text/plain" },
|
|
12515
|
+
body: "Content-Type must be application/json"
|
|
12516
|
+
};
|
|
12517
|
+
}
|
|
12518
|
+
if (!checkAcceptHeader(request.headers.accept)) {
|
|
12519
|
+
return {
|
|
12520
|
+
status: 406,
|
|
12521
|
+
reason: "Not Acceptable",
|
|
12522
|
+
headers: { "content-type": "text/plain" },
|
|
12523
|
+
body: "Accept must include application/json or text/event-stream"
|
|
12524
|
+
};
|
|
11733
12525
|
}
|
|
11734
|
-
const originCheck = checkOrigin(request.headers.origin,
|
|
12526
|
+
const originCheck = checkOrigin(request.headers.origin, serverOrigins, allowedOrigins);
|
|
11735
12527
|
if (!originCheck.ok) {
|
|
11736
12528
|
return {
|
|
11737
12529
|
status: 403,
|
|
@@ -11740,7 +12532,7 @@ async function handleProxyMcpRequest(request, proxy, allowedOrigins, boundLoopba
|
|
|
11740
12532
|
body: originCheck.reason ?? "Forbidden"
|
|
11741
12533
|
};
|
|
11742
12534
|
}
|
|
11743
|
-
const hostCheck = checkHost(request.headers.host,
|
|
12535
|
+
const hostCheck = checkHost(request.headers.host, allowedHosts);
|
|
11744
12536
|
if (!hostCheck.ok) {
|
|
11745
12537
|
return {
|
|
11746
12538
|
status: 403,
|
|
@@ -11749,21 +12541,17 @@ async function handleProxyMcpRequest(request, proxy, allowedOrigins, boundLoopba
|
|
|
11749
12541
|
body: hostCheck.reason ?? "Forbidden"
|
|
11750
12542
|
};
|
|
11751
12543
|
}
|
|
11752
|
-
if (
|
|
11753
|
-
|
|
11754
|
-
|
|
11755
|
-
|
|
11756
|
-
|
|
11757
|
-
|
|
11758
|
-
|
|
11759
|
-
|
|
11760
|
-
|
|
11761
|
-
|
|
11762
|
-
|
|
11763
|
-
reason: "Method Not Allowed",
|
|
11764
|
-
headers: { "content-type": "text/plain", allow: "POST" },
|
|
11765
|
-
body: "Method Not Allowed"
|
|
11766
|
-
};
|
|
12544
|
+
if (resolvedBearer) {
|
|
12545
|
+
const authHeader = request.headers.authorization;
|
|
12546
|
+
const provided = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : "";
|
|
12547
|
+
if (!provided || !constantTimeEqual(provided, resolvedBearer)) {
|
|
12548
|
+
return {
|
|
12549
|
+
status: 401,
|
|
12550
|
+
reason: "Unauthorized",
|
|
12551
|
+
headers: { "content-type": "text/plain" },
|
|
12552
|
+
body: "Unauthorized"
|
|
12553
|
+
};
|
|
12554
|
+
}
|
|
11767
12555
|
}
|
|
11768
12556
|
const outcome = await proxy.handle(request.body);
|
|
11769
12557
|
return {
|
|
@@ -11801,12 +12589,12 @@ async function runStdioServer(platform2, tools, call, serverName, version) {
|
|
|
11801
12589
|
}
|
|
11802
12590
|
return 0;
|
|
11803
12591
|
}
|
|
11804
|
-
async function runHttpServer(platform2, options, tools, call, serverName, version, resolvedBearer) {
|
|
12592
|
+
async function runHttpServer(platform2, options, tools, call, serverName, version, resolvedBearer, generatedBearer) {
|
|
11805
12593
|
if (!platform2.listen) {
|
|
11806
12594
|
await platform2.write(2, "mcpcat serve: HTTP transport requires platform.listen\n");
|
|
11807
12595
|
return 3;
|
|
11808
12596
|
}
|
|
11809
|
-
const boundLoopback = options.host
|
|
12597
|
+
const boundLoopback = isLoopbackBind(options.host);
|
|
11810
12598
|
if (!boundLoopback && !resolvedBearer) {
|
|
11811
12599
|
await platform2.write(2, "mcpcat serve: --bearer is required when binding off loopback\n");
|
|
11812
12600
|
return 2;
|
|
@@ -11814,10 +12602,23 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
11814
12602
|
const allowedOrigins = new Set(options.allowOrigins);
|
|
11815
12603
|
const listener = await platform2.listen({ host: options.host, port: options.port });
|
|
11816
12604
|
const actualPort = listener.port;
|
|
12605
|
+
const serverOrigins = buildServerOrigins(options.host, actualPort);
|
|
12606
|
+
const allowedHosts = buildAllowedHosts(options.host, actualPort, options.allowHosts);
|
|
11817
12607
|
const listenUrl = `http://${options.host}:${actualPort}/mcp`;
|
|
11818
|
-
|
|
11819
|
-
|
|
12608
|
+
let tokenFilePath;
|
|
12609
|
+
if (generatedBearer && resolvedBearer) {
|
|
12610
|
+
tokenFilePath = await publishBearer(
|
|
12611
|
+
platform2,
|
|
12612
|
+
resolvedBearer,
|
|
12613
|
+
actualPort,
|
|
12614
|
+
listenUrl,
|
|
12615
|
+
options.json
|
|
12616
|
+
);
|
|
12617
|
+
} else {
|
|
12618
|
+
if (options.json) {
|
|
12619
|
+
await platform2.write(1, `${JSON.stringify({ url: listenUrl })}
|
|
11820
12620
|
`);
|
|
12621
|
+
}
|
|
11821
12622
|
}
|
|
11822
12623
|
await platform2.write(2, `mcpcat serve: listening on ${listenUrl}
|
|
11823
12624
|
`);
|
|
@@ -11827,6 +12628,10 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
11827
12628
|
shutdownRequested = true;
|
|
11828
12629
|
listener.close();
|
|
11829
12630
|
void killInflight();
|
|
12631
|
+
if (tokenFilePath) {
|
|
12632
|
+
platform2.fs.unlink?.(tokenFilePath).catch(() => {
|
|
12633
|
+
});
|
|
12634
|
+
}
|
|
11830
12635
|
};
|
|
11831
12636
|
if (platform2.onSignal) {
|
|
11832
12637
|
platform2.onSignal("SIGINT", shutdown);
|
|
@@ -11838,8 +12643,9 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
11838
12643
|
call,
|
|
11839
12644
|
serverName,
|
|
11840
12645
|
version,
|
|
12646
|
+
serverOrigins,
|
|
11841
12647
|
allowedOrigins,
|
|
11842
|
-
|
|
12648
|
+
allowedHosts,
|
|
11843
12649
|
resolvedBearer
|
|
11844
12650
|
);
|
|
11845
12651
|
try {
|
|
@@ -11859,20 +12665,40 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
11859
12665
|
}
|
|
11860
12666
|
return 0;
|
|
11861
12667
|
}
|
|
11862
|
-
async function handleMcpRequest(request, tools, call, serverName, version, allowedOrigins,
|
|
11863
|
-
if (
|
|
11864
|
-
|
|
11865
|
-
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
|
|
11871
|
-
|
|
11872
|
-
|
|
11873
|
-
|
|
12668
|
+
async function handleMcpRequest(request, tools, call, serverName, version, serverOrigins, allowedOrigins, allowedHosts, resolvedBearer) {
|
|
12669
|
+
if (request.target !== "/mcp") {
|
|
12670
|
+
return {
|
|
12671
|
+
status: 404,
|
|
12672
|
+
reason: "Not Found",
|
|
12673
|
+
headers: { "content-type": "text/plain" },
|
|
12674
|
+
body: "Not Found"
|
|
12675
|
+
};
|
|
12676
|
+
}
|
|
12677
|
+
if (request.method !== "POST") {
|
|
12678
|
+
return {
|
|
12679
|
+
status: 405,
|
|
12680
|
+
reason: "Method Not Allowed",
|
|
12681
|
+
headers: { "content-type": "text/plain", allow: "POST" },
|
|
12682
|
+
body: "Method Not Allowed"
|
|
12683
|
+
};
|
|
12684
|
+
}
|
|
12685
|
+
if (!checkContentType(request.headers["content-type"])) {
|
|
12686
|
+
return {
|
|
12687
|
+
status: 415,
|
|
12688
|
+
reason: "Unsupported Media Type",
|
|
12689
|
+
headers: { "content-type": "text/plain" },
|
|
12690
|
+
body: "Content-Type must be application/json"
|
|
12691
|
+
};
|
|
12692
|
+
}
|
|
12693
|
+
if (!checkAcceptHeader(request.headers.accept)) {
|
|
12694
|
+
return {
|
|
12695
|
+
status: 406,
|
|
12696
|
+
reason: "Not Acceptable",
|
|
12697
|
+
headers: { "content-type": "text/plain" },
|
|
12698
|
+
body: "Accept must include application/json or text/event-stream"
|
|
12699
|
+
};
|
|
11874
12700
|
}
|
|
11875
|
-
const originCheck = checkOrigin(request.headers.origin,
|
|
12701
|
+
const originCheck = checkOrigin(request.headers.origin, serverOrigins, allowedOrigins);
|
|
11876
12702
|
if (!originCheck.ok) {
|
|
11877
12703
|
return {
|
|
11878
12704
|
status: 403,
|
|
@@ -11881,7 +12707,7 @@ async function handleMcpRequest(request, tools, call, serverName, version, allow
|
|
|
11881
12707
|
body: originCheck.reason ?? "Forbidden"
|
|
11882
12708
|
};
|
|
11883
12709
|
}
|
|
11884
|
-
const hostCheck = checkHost(request.headers.host,
|
|
12710
|
+
const hostCheck = checkHost(request.headers.host, allowedHosts);
|
|
11885
12711
|
if (!hostCheck.ok) {
|
|
11886
12712
|
return {
|
|
11887
12713
|
status: 403,
|
|
@@ -11890,21 +12716,17 @@ async function handleMcpRequest(request, tools, call, serverName, version, allow
|
|
|
11890
12716
|
body: hostCheck.reason ?? "Forbidden"
|
|
11891
12717
|
};
|
|
11892
12718
|
}
|
|
11893
|
-
if (
|
|
11894
|
-
|
|
11895
|
-
|
|
11896
|
-
|
|
11897
|
-
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
|
|
11901
|
-
|
|
11902
|
-
|
|
11903
|
-
|
|
11904
|
-
reason: "Method Not Allowed",
|
|
11905
|
-
headers: { "content-type": "text/plain", allow: "POST" },
|
|
11906
|
-
body: "Method Not Allowed"
|
|
11907
|
-
};
|
|
12719
|
+
if (resolvedBearer) {
|
|
12720
|
+
const authHeader = request.headers.authorization;
|
|
12721
|
+
const provided = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : "";
|
|
12722
|
+
if (!provided || !constantTimeEqual(provided, resolvedBearer)) {
|
|
12723
|
+
return {
|
|
12724
|
+
status: 401,
|
|
12725
|
+
reason: "Unauthorized",
|
|
12726
|
+
headers: { "content-type": "text/plain" },
|
|
12727
|
+
body: "Unauthorized"
|
|
12728
|
+
};
|
|
12729
|
+
}
|
|
11908
12730
|
}
|
|
11909
12731
|
const outcome = await handleJsonRpc(request.body, tools, call, serverName, version);
|
|
11910
12732
|
return {
|
|
@@ -12231,8 +13053,16 @@ var PiFileTokenSource = class {
|
|
|
12231
13053
|
};
|
|
12232
13054
|
|
|
12233
13055
|
// src/cli/auth-verbs.ts
|
|
12234
|
-
|
|
12235
|
-
|
|
13056
|
+
var resolvedSource;
|
|
13057
|
+
async function createTokenSource(platform2) {
|
|
13058
|
+
if (isKernel(platform2)) return new PiFileTokenSource(platform2);
|
|
13059
|
+
if (resolvedSource) return resolvedSource;
|
|
13060
|
+
if (await jerkAvailable(platform2)) {
|
|
13061
|
+
resolvedSource = new JerkTokenSource(platform2);
|
|
13062
|
+
} else {
|
|
13063
|
+
resolvedSource = new PiFileTokenSource(platform2);
|
|
13064
|
+
}
|
|
13065
|
+
return resolvedSource;
|
|
12236
13066
|
}
|
|
12237
13067
|
function resolveServerName(target) {
|
|
12238
13068
|
try {
|
|
@@ -12260,7 +13090,80 @@ async function resolveLoginTarget(platform2, target) {
|
|
|
12260
13090
|
const url = target.startsWith("http") ? target : `https://${target}`;
|
|
12261
13091
|
return { name: resolved.name, serverUrl: url, oauth: void 0 };
|
|
12262
13092
|
}
|
|
12263
|
-
throw new Error(`unknown target '${target}': not a URL and not found in mcp.json`);
|
|
13093
|
+
throw new Error(`unknown target '${target}': not a URL and not found in mcp.json`);
|
|
13094
|
+
}
|
|
13095
|
+
function jerkMcpId(name) {
|
|
13096
|
+
if (name.startsWith("mcp:")) return name;
|
|
13097
|
+
if (name.startsWith("url:")) return `mcp:${name}`;
|
|
13098
|
+
return `mcp:${name}`;
|
|
13099
|
+
}
|
|
13100
|
+
async function readStream2(stream) {
|
|
13101
|
+
const reader = stream.getReader();
|
|
13102
|
+
const chunks = [];
|
|
13103
|
+
for (; ; ) {
|
|
13104
|
+
const { done, value } = await reader.read();
|
|
13105
|
+
if (done) break;
|
|
13106
|
+
chunks.push(value);
|
|
13107
|
+
}
|
|
13108
|
+
const total = chunks.reduce((s, c) => s + c.length, 0);
|
|
13109
|
+
const buf = new Uint8Array(total);
|
|
13110
|
+
let off = 0;
|
|
13111
|
+
for (const c of chunks) {
|
|
13112
|
+
buf.set(c, off);
|
|
13113
|
+
off += c.length;
|
|
13114
|
+
}
|
|
13115
|
+
return new TextDecoder().decode(buf);
|
|
13116
|
+
}
|
|
13117
|
+
async function jerkLogin(platform2, target, args2) {
|
|
13118
|
+
const resolved = await resolveLoginTarget(platform2, target);
|
|
13119
|
+
const id = jerkMcpId(resolved.name);
|
|
13120
|
+
const argv = ["jerk", id, "create"];
|
|
13121
|
+
const scopeIdx = args2.indexOf("--scope");
|
|
13122
|
+
if (scopeIdx !== -1 && args2[scopeIdx + 1]) {
|
|
13123
|
+
argv.push("--scope", args2[scopeIdx + 1]);
|
|
13124
|
+
}
|
|
13125
|
+
if (args2.includes("--no-browser")) {
|
|
13126
|
+
argv.push("--no-browser");
|
|
13127
|
+
}
|
|
13128
|
+
const proc = await platform2.spawn({ argv });
|
|
13129
|
+
proc.stdin.close().catch(() => {
|
|
13130
|
+
});
|
|
13131
|
+
const [, errText, result] = await Promise.all([
|
|
13132
|
+
readStream2(proc.stdout),
|
|
13133
|
+
readStream2(proc.stderr),
|
|
13134
|
+
proc.wait()
|
|
13135
|
+
]);
|
|
13136
|
+
if (result.status === 0) {
|
|
13137
|
+
await platform2.write(1, `Signed in to ${target}
|
|
13138
|
+
`);
|
|
13139
|
+
return 0;
|
|
13140
|
+
}
|
|
13141
|
+
if (errText.trim()) {
|
|
13142
|
+
await platform2.write(2, errText);
|
|
13143
|
+
}
|
|
13144
|
+
if (result.status === JERK_EXIT_SIGN_IN) return 4;
|
|
13145
|
+
return result.status;
|
|
13146
|
+
}
|
|
13147
|
+
async function jerkLogout(platform2, target) {
|
|
13148
|
+
const resolved = await resolveLoginTarget(platform2, target);
|
|
13149
|
+
const id = jerkMcpId(resolved.name);
|
|
13150
|
+
const argv = ["jerk", id, "drop", "--yes"];
|
|
13151
|
+
const proc = await platform2.spawn({ argv });
|
|
13152
|
+
proc.stdin.close().catch(() => {
|
|
13153
|
+
});
|
|
13154
|
+
const [, , result] = await Promise.all([
|
|
13155
|
+
readStream2(proc.stdout),
|
|
13156
|
+
readStream2(proc.stderr),
|
|
13157
|
+
proc.wait()
|
|
13158
|
+
]);
|
|
13159
|
+
if (result.status === 0) {
|
|
13160
|
+
await platform2.write(1, `Signed out of ${target}
|
|
13161
|
+
`);
|
|
13162
|
+
} else {
|
|
13163
|
+
await platform2.write(1, `No credentials stored for ${target}
|
|
13164
|
+
`);
|
|
13165
|
+
}
|
|
13166
|
+
return 0;
|
|
12264
13167
|
}
|
|
12265
13168
|
async function runLogin(platform2, args2, source) {
|
|
12266
13169
|
if (isKernel(platform2)) {
|
|
@@ -12270,13 +13173,22 @@ async function runLogin(platform2, args2, source) {
|
|
|
12270
13173
|
);
|
|
12271
13174
|
return 4;
|
|
12272
13175
|
}
|
|
12273
|
-
const noBrowser = args2.includes("--no-browser");
|
|
12274
13176
|
const positional = args2.filter((a) => !a.startsWith("-"));
|
|
12275
13177
|
if (positional.length === 0) {
|
|
12276
13178
|
await platform2.write(2, "mcpcat: login requires a target\n");
|
|
12277
13179
|
return 2;
|
|
12278
13180
|
}
|
|
12279
13181
|
const target = positional[0];
|
|
13182
|
+
if (source instanceof JerkTokenSource) {
|
|
13183
|
+
try {
|
|
13184
|
+
return await jerkLogin(platform2, target, args2);
|
|
13185
|
+
} catch (err) {
|
|
13186
|
+
await platform2.write(2, `mcpcat: login failed: ${err.message}
|
|
13187
|
+
`);
|
|
13188
|
+
return 4;
|
|
13189
|
+
}
|
|
13190
|
+
}
|
|
13191
|
+
const noBrowser = args2.includes("--no-browser");
|
|
12280
13192
|
let resolved;
|
|
12281
13193
|
try {
|
|
12282
13194
|
resolved = await resolveLoginTarget(platform2, target);
|
|
@@ -12358,6 +13270,15 @@ async function runLogout(platform2, args2, source) {
|
|
|
12358
13270
|
return 2;
|
|
12359
13271
|
}
|
|
12360
13272
|
const target = positional[0];
|
|
13273
|
+
if (source instanceof JerkTokenSource) {
|
|
13274
|
+
try {
|
|
13275
|
+
return await jerkLogout(platform2, target);
|
|
13276
|
+
} catch (err) {
|
|
13277
|
+
await platform2.write(2, `mcpcat: logout failed: ${err.message}
|
|
13278
|
+
`);
|
|
13279
|
+
return 1;
|
|
13280
|
+
}
|
|
13281
|
+
}
|
|
12361
13282
|
let resolved;
|
|
12362
13283
|
try {
|
|
12363
13284
|
resolved = await resolveLoginTarget(platform2, target);
|
|
@@ -12730,6 +13651,49 @@ function checkAgentWarnings(name, config) {
|
|
|
12730
13651
|
return warnings;
|
|
12731
13652
|
}
|
|
12732
13653
|
|
|
13654
|
+
// src/serve/seal.ts
|
|
13655
|
+
async function sealSecret(platform2, name, value, options) {
|
|
13656
|
+
if (!platform2.seal) {
|
|
13657
|
+
throw Object.assign(new Error("vault device not available"), { sealErrno: "ENOTCONN" });
|
|
13658
|
+
}
|
|
13659
|
+
const request = JSON.stringify({
|
|
13660
|
+
v: 1,
|
|
13661
|
+
op: "seal",
|
|
13662
|
+
kind: "mcp",
|
|
13663
|
+
name,
|
|
13664
|
+
value,
|
|
13665
|
+
server: options.server,
|
|
13666
|
+
url: options.url
|
|
13667
|
+
});
|
|
13668
|
+
let responseText;
|
|
13669
|
+
try {
|
|
13670
|
+
responseText = await platform2.seal(request);
|
|
13671
|
+
} catch (err) {
|
|
13672
|
+
const code2 = err.code;
|
|
13673
|
+
if (code2 === "ENOENT" || code2 === "ENOTCONN") {
|
|
13674
|
+
throw Object.assign(new Error("vault device not available"), { sealErrno: "ENOTCONN" });
|
|
13675
|
+
}
|
|
13676
|
+
throw Object.assign(new Error(`vault: ${err.message}`), {
|
|
13677
|
+
sealErrno: code2 ?? "EACCES"
|
|
13678
|
+
});
|
|
13679
|
+
}
|
|
13680
|
+
const response = JSON.parse(responseText);
|
|
13681
|
+
if (response.error) {
|
|
13682
|
+
throw Object.assign(new Error(response.error), { sealErrno: response.errno ?? "EACCES" });
|
|
13683
|
+
}
|
|
13684
|
+
return { placeholder: response.placeholder };
|
|
13685
|
+
}
|
|
13686
|
+
function sealExitCode(errno) {
|
|
13687
|
+
if (errno === "EEXIST") return 2;
|
|
13688
|
+
return 4;
|
|
13689
|
+
}
|
|
13690
|
+
function sealErrorMessage(errno, name) {
|
|
13691
|
+
if (errno === "EEXIST") return `a non-bridge secret named ${name} already exists`;
|
|
13692
|
+
if (errno === "ENOTCONN") return "no vault available; use --bearer '${VAR}' or --no-auth";
|
|
13693
|
+
if (errno === "EACCES") return "vault locked; use --bearer '${VAR}' or --no-auth";
|
|
13694
|
+
return `vault error: ${errno}`;
|
|
13695
|
+
}
|
|
13696
|
+
|
|
12733
13697
|
// src/cli/config-verbs.ts
|
|
12734
13698
|
function parseHeader(hdr) {
|
|
12735
13699
|
const colonIdx = hdr.indexOf(":");
|
|
@@ -12751,6 +13715,10 @@ function parseAddFlagAt(flagArgs, i, flags) {
|
|
|
12751
13715
|
flags.noProbe = true;
|
|
12752
13716
|
return { next: i + 1 };
|
|
12753
13717
|
}
|
|
13718
|
+
if (arg === "--no-auth") {
|
|
13719
|
+
flags.noAuth = true;
|
|
13720
|
+
return { next: i + 1 };
|
|
13721
|
+
}
|
|
12754
13722
|
if (arg === "--bridge") {
|
|
12755
13723
|
flags.bridge = true;
|
|
12756
13724
|
return { next: i + 1 };
|
|
@@ -12796,6 +13764,7 @@ function parseAddFlags(args2) {
|
|
|
12796
13764
|
local: false,
|
|
12797
13765
|
noProbe: false,
|
|
12798
13766
|
bridge: false,
|
|
13767
|
+
noAuth: false,
|
|
12799
13768
|
headers: [],
|
|
12800
13769
|
envPairs: [],
|
|
12801
13770
|
positional: [],
|
|
@@ -13158,6 +14127,11 @@ async function runImport(platform2, args2) {
|
|
|
13158
14127
|
}
|
|
13159
14128
|
return 0;
|
|
13160
14129
|
}
|
|
14130
|
+
function generateBridgeToken() {
|
|
14131
|
+
const bytes = new Uint8Array(32);
|
|
14132
|
+
crypto.getRandomValues(bytes);
|
|
14133
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
14134
|
+
}
|
|
13161
14135
|
async function runBridge(platform2, args2, version) {
|
|
13162
14136
|
if (platform2.kind === "host") {
|
|
13163
14137
|
await platform2.write(
|
|
@@ -13184,16 +14158,56 @@ async function runBridge(platform2, args2, version) {
|
|
|
13184
14158
|
if (flags.bearer) {
|
|
13185
14159
|
await platform2.write(
|
|
13186
14160
|
2,
|
|
13187
|
-
"mcpcat add --bridge: --bearer is not
|
|
14161
|
+
"mcpcat add --bridge: --bearer is not supported for bridge; authentication is automatic via the vault\n"
|
|
13188
14162
|
);
|
|
13189
14163
|
return 2;
|
|
13190
14164
|
}
|
|
13191
14165
|
const serveOptions = parseServeFlags(["--proxy", "__bridge__", "--http", "0", "--json"]);
|
|
13192
14166
|
serveOptions.proxyCommand = flags.cmdArgs;
|
|
13193
14167
|
const path = flags.configFile ?? (flags.local ? projectConfigPath(platform2) : userConfigPath(platform2));
|
|
14168
|
+
if (flags.noAuth) {
|
|
14169
|
+
await platform2.write(
|
|
14170
|
+
2,
|
|
14171
|
+
"mcpcat add --bridge: warning: running without authentication on loopback\n"
|
|
14172
|
+
);
|
|
14173
|
+
serveOptions.noAuth = true;
|
|
14174
|
+
serveOptions.onListening = async (actualPort) => {
|
|
14175
|
+
const bridgeUrl = `http://${actualPort}.kernel.localhost/mcp`;
|
|
14176
|
+
const httpConfig = { url: bridgeUrl };
|
|
14177
|
+
if (flags.description) httpConfig.description = flags.description;
|
|
14178
|
+
if (flags.exposure) httpConfig.exposure = flags.exposure;
|
|
14179
|
+
const { replaced } = await addServer(platform2.fs, path, name, httpConfig);
|
|
14180
|
+
await platform2.write(
|
|
14181
|
+
1,
|
|
14182
|
+
`${replaced ? "replaced" : "added"} ${name} in ${path}
|
|
14183
|
+
bridge does not survive a reload; runs in the foreground (start with &)
|
|
14184
|
+
`
|
|
14185
|
+
);
|
|
14186
|
+
};
|
|
14187
|
+
return runServe(platform2, serveOptions, version);
|
|
14188
|
+
}
|
|
14189
|
+
const token = generateBridgeToken();
|
|
14190
|
+
const secretName = `MCP_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_TOKEN`;
|
|
14191
|
+
serveOptions._resolvedBearer = token;
|
|
13194
14192
|
serveOptions.onListening = async (actualPort) => {
|
|
14193
|
+
if (actualPort === 9222) {
|
|
14194
|
+
await platform2.write(2, "mcpcat add --bridge: port 9222 is reserved; please retry\n");
|
|
14195
|
+
return;
|
|
14196
|
+
}
|
|
13195
14197
|
const bridgeUrl = `http://${actualPort}.kernel.localhost/mcp`;
|
|
13196
|
-
|
|
14198
|
+
try {
|
|
14199
|
+
await sealSecret(platform2, secretName, token, { server: name, url: bridgeUrl });
|
|
14200
|
+
} catch (err) {
|
|
14201
|
+
const errno = err.sealErrno ?? "EACCES";
|
|
14202
|
+
const code2 = sealExitCode(errno);
|
|
14203
|
+
await platform2.write(2, `mcpcat add --bridge: ${sealErrorMessage(errno, secretName)}
|
|
14204
|
+
`);
|
|
14205
|
+
platform2.exit(code2);
|
|
14206
|
+
}
|
|
14207
|
+
const httpConfig = {
|
|
14208
|
+
url: bridgeUrl,
|
|
14209
|
+
headers: { Authorization: "Bearer ${" + secretName + "}" }
|
|
14210
|
+
};
|
|
13197
14211
|
if (flags.description) httpConfig.description = flags.description;
|
|
13198
14212
|
if (flags.exposure) httpConfig.exposure = flags.exposure;
|
|
13199
14213
|
const { replaced } = await addServer(platform2.fs, path, name, httpConfig);
|
|
@@ -13330,8 +14344,18 @@ var CONFIG_VERBS = {
|
|
|
13330
14344
|
exposure: true,
|
|
13331
14345
|
import: true
|
|
13332
14346
|
};
|
|
13333
|
-
var UNIMPLEMENTED_VERBS = /* @__PURE__ */ new Set(["
|
|
13334
|
-
var CLIENT_VERBS = /* @__PURE__ */ new Set([
|
|
14347
|
+
var UNIMPLEMENTED_VERBS = /* @__PURE__ */ new Set(["complete", "task"]);
|
|
14348
|
+
var CLIENT_VERBS = /* @__PURE__ */ new Set([
|
|
14349
|
+
"info",
|
|
14350
|
+
"tools",
|
|
14351
|
+
"call",
|
|
14352
|
+
"describe",
|
|
14353
|
+
"serve",
|
|
14354
|
+
"resources",
|
|
14355
|
+
"read",
|
|
14356
|
+
"prompts",
|
|
14357
|
+
"prompt"
|
|
14358
|
+
]);
|
|
13335
14359
|
function helpText(version) {
|
|
13336
14360
|
return `mcpcat ${version} \u2013 a curl for the Model Context Protocol
|
|
13337
14361
|
|
|
@@ -13370,6 +14394,7 @@ Options:
|
|
|
13370
14394
|
-H, --header 'N: V' Extra header
|
|
13371
14395
|
-d <json> Tool arguments as JSON (or @file, @- for stdin)
|
|
13372
14396
|
-m, --timeout <s> Request timeout (default 60)
|
|
14397
|
+
--memory <MiB> Code mode memory limit (default 256, max 1024)
|
|
13373
14398
|
--protocol-version <v> Pin protocol version
|
|
13374
14399
|
--schema Print raw JSON Schema (describe)
|
|
13375
14400
|
--ts TypeScript declarations (describe)
|
|
@@ -13386,6 +14411,31 @@ Options:
|
|
|
13386
14411
|
-h, --help Show this help
|
|
13387
14412
|
`;
|
|
13388
14413
|
}
|
|
14414
|
+
var VERBS_WITH_TOOL = /* @__PURE__ */ new Set(["call", "prompt", "describe", "read"]);
|
|
14415
|
+
var VERBS_WITH_TARGET = /* @__PURE__ */ new Set([
|
|
14416
|
+
"info",
|
|
14417
|
+
"tools",
|
|
14418
|
+
"call",
|
|
14419
|
+
"describe",
|
|
14420
|
+
"serve",
|
|
14421
|
+
"grep",
|
|
14422
|
+
"code",
|
|
14423
|
+
"discover",
|
|
14424
|
+
"login",
|
|
14425
|
+
"logout",
|
|
14426
|
+
"resources",
|
|
14427
|
+
"read",
|
|
14428
|
+
"prompts",
|
|
14429
|
+
"prompt"
|
|
14430
|
+
]);
|
|
14431
|
+
function resolveMaxPositional(positional) {
|
|
14432
|
+
if (positional.length === 0) return 3;
|
|
14433
|
+
const first = positional[0];
|
|
14434
|
+
if (!VERBS.includes(first)) return 2;
|
|
14435
|
+
if (VERBS_WITH_TOOL.has(first)) return 3;
|
|
14436
|
+
if (VERBS_WITH_TARGET.has(first)) return 2;
|
|
14437
|
+
return 2;
|
|
14438
|
+
}
|
|
13389
14439
|
function parseFlags(args2) {
|
|
13390
14440
|
const headers = {};
|
|
13391
14441
|
let dataArg;
|
|
@@ -13400,10 +14450,16 @@ function parseFlags(args2) {
|
|
|
13400
14450
|
const serverTargets = [];
|
|
13401
14451
|
let allowShell = false;
|
|
13402
14452
|
let timeoutMs;
|
|
14453
|
+
let memoryLimitMiB;
|
|
13403
14454
|
let verbose = false;
|
|
13404
14455
|
let silent = false;
|
|
13405
14456
|
const positional = [];
|
|
13406
14457
|
let trailingCommand;
|
|
14458
|
+
let respondArg;
|
|
14459
|
+
const roots = [];
|
|
14460
|
+
let outputPath;
|
|
14461
|
+
let noValidate = false;
|
|
14462
|
+
const rest = [];
|
|
13407
14463
|
for (let i = 0; i < args2.length; i++) {
|
|
13408
14464
|
const arg = args2[i];
|
|
13409
14465
|
if (arg === "--") {
|
|
@@ -13444,8 +14500,41 @@ function parseFlags(args2) {
|
|
|
13444
14500
|
silent = true;
|
|
13445
14501
|
} else if ((arg === "-m" || arg === "--timeout") && args2[i + 1]) {
|
|
13446
14502
|
timeoutMs = Number(args2[++i]) * 1e3;
|
|
14503
|
+
} else if (arg === "--respond" && args2[i + 1]) {
|
|
14504
|
+
respondArg = args2[++i];
|
|
14505
|
+
} else if (arg === "--root" && args2[i + 1]) {
|
|
14506
|
+
roots.push(args2[++i]);
|
|
14507
|
+
} else if ((arg === "-o" || arg === "--output") && args2[i + 1]) {
|
|
14508
|
+
outputPath = args2[++i];
|
|
14509
|
+
} else if (arg === "--memory" && args2[i + 1]) {
|
|
14510
|
+
memoryLimitMiB = Number(args2[++i]);
|
|
14511
|
+
} else if (arg === "--no-validate") {
|
|
14512
|
+
noValidate = true;
|
|
13447
14513
|
} else if (!arg.startsWith("-")) {
|
|
13448
14514
|
positional.push(arg);
|
|
14515
|
+
const maxPositional = resolveMaxPositional(positional);
|
|
14516
|
+
if (positional.length >= maxPositional) {
|
|
14517
|
+
rest.push(...args2.slice(i + 1));
|
|
14518
|
+
break;
|
|
14519
|
+
}
|
|
14520
|
+
}
|
|
14521
|
+
}
|
|
14522
|
+
const toolArgs = [];
|
|
14523
|
+
for (let j = 0; j < rest.length; j++) {
|
|
14524
|
+
const ra = rest[j];
|
|
14525
|
+
if (ra === "-d" && rest[j + 1] !== void 0) {
|
|
14526
|
+
dataArg = rest[++j];
|
|
14527
|
+
} else if (ra === "--json") {
|
|
14528
|
+
jsonOutput = true;
|
|
14529
|
+
} else if (ra === "--no-validate") {
|
|
14530
|
+
noValidate = true;
|
|
14531
|
+
} else if ((ra === "-o" || ra === "--output") && rest[j + 1] !== void 0) {
|
|
14532
|
+
outputPath = rest[++j];
|
|
14533
|
+
} else if (ra === "--help" || ra === "-h") {
|
|
14534
|
+
toolArgs.length = 0;
|
|
14535
|
+
break;
|
|
14536
|
+
} else {
|
|
14537
|
+
toolArgs.push(ra);
|
|
13449
14538
|
}
|
|
13450
14539
|
}
|
|
13451
14540
|
return {
|
|
@@ -13462,10 +14551,16 @@ function parseFlags(args2) {
|
|
|
13462
14551
|
serverTargets,
|
|
13463
14552
|
allowShell,
|
|
13464
14553
|
timeoutMs,
|
|
14554
|
+
memoryLimitMiB,
|
|
13465
14555
|
verbose,
|
|
13466
14556
|
silent,
|
|
13467
14557
|
positional,
|
|
13468
|
-
trailingCommand
|
|
14558
|
+
trailingCommand,
|
|
14559
|
+
respondArg,
|
|
14560
|
+
roots,
|
|
14561
|
+
outputPath,
|
|
14562
|
+
noValidate,
|
|
14563
|
+
rest: toolArgs
|
|
13469
14564
|
};
|
|
13470
14565
|
}
|
|
13471
14566
|
function resolveVerb(positional, trailingCommand) {
|
|
@@ -13500,6 +14595,7 @@ function parseArgs(args2) {
|
|
|
13500
14595
|
const resolved = resolveVerb(flags.positional, flags.trailingCommand);
|
|
13501
14596
|
return {
|
|
13502
14597
|
...resolved,
|
|
14598
|
+
toolArgs: flags.rest,
|
|
13503
14599
|
headers: flags.headers,
|
|
13504
14600
|
dataArg: flags.dataArg,
|
|
13505
14601
|
protocolVersion: flags.protocolVersion,
|
|
@@ -13513,8 +14609,13 @@ function parseArgs(args2) {
|
|
|
13513
14609
|
serverTargets: flags.serverTargets,
|
|
13514
14610
|
allowShell: flags.allowShell,
|
|
13515
14611
|
timeoutMs: flags.timeoutMs,
|
|
14612
|
+
memoryLimitMiB: flags.memoryLimitMiB,
|
|
13516
14613
|
verbose: flags.verbose,
|
|
13517
|
-
silent: flags.silent
|
|
14614
|
+
silent: flags.silent,
|
|
14615
|
+
noValidate: flags.noValidate,
|
|
14616
|
+
respondArg: flags.respondArg,
|
|
14617
|
+
roots: flags.roots,
|
|
14618
|
+
outputPath: flags.outputPath
|
|
13518
14619
|
};
|
|
13519
14620
|
}
|
|
13520
14621
|
function formatToolLine(tool) {
|
|
@@ -13637,7 +14738,8 @@ async function runCall(platform2, conn, toolName, dataArg, toolArgs, opts) {
|
|
|
13637
14738
|
const tools = await conn.listTools();
|
|
13638
14739
|
const tool = tools.find((t) => t.name === toolName);
|
|
13639
14740
|
const schema = tool?.inputSchema ?? { type: "object", properties: {} };
|
|
13640
|
-
const
|
|
14741
|
+
const skipRequired = opts.noValidate || !!dataArg;
|
|
14742
|
+
const c = coerceArgsBySchema(toolArgs, schema, { noValidate: skipRequired });
|
|
13641
14743
|
if (!c.ok) {
|
|
13642
14744
|
await writeErr(platform2, opts.json ?? false, "usage", c.error);
|
|
13643
14745
|
return EXIT_USAGE;
|
|
@@ -13646,8 +14748,11 @@ async function runCall(platform2, conn, toolName, dataArg, toolArgs, opts) {
|
|
|
13646
14748
|
}
|
|
13647
14749
|
return renderCallResult(
|
|
13648
14750
|
platform2,
|
|
13649
|
-
await conn.callTool(toolName, fin, {
|
|
13650
|
-
|
|
14751
|
+
await conn.callTool(toolName, fin, {
|
|
14752
|
+
onProgress: opts.onProgress,
|
|
14753
|
+
inputResponses: opts.inputResponses
|
|
14754
|
+
}),
|
|
14755
|
+
{ json: opts.json, outputPath: opts.outputPath }
|
|
13651
14756
|
);
|
|
13652
14757
|
}
|
|
13653
14758
|
async function runDescribe(platform2, conn, toolName, showSchema, json) {
|
|
@@ -13684,19 +14789,168 @@ async function runDescribe(platform2, conn, toolName, showSchema, json) {
|
|
|
13684
14789
|
}
|
|
13685
14790
|
return EXIT_OK;
|
|
13686
14791
|
}
|
|
13687
|
-
async function
|
|
14792
|
+
async function runResources(platform2, conn, json) {
|
|
14793
|
+
const resources = await conn.listResources();
|
|
14794
|
+
const templates = await conn.listResourceTemplates();
|
|
14795
|
+
if (json) {
|
|
14796
|
+
await renderResourcesJson(platform2, resources, templates);
|
|
14797
|
+
return EXIT_OK;
|
|
14798
|
+
}
|
|
14799
|
+
if (resources.length === 0 && templates.length === 0) {
|
|
14800
|
+
await platform2.write(1, "(no resources)\n");
|
|
14801
|
+
return EXIT_OK;
|
|
14802
|
+
}
|
|
14803
|
+
for (const r of resources) {
|
|
14804
|
+
const desc = r.description ? ` ${r.description}` : "";
|
|
14805
|
+
const mime = r.mimeType ? ` [${r.mimeType}]` : "";
|
|
14806
|
+
await platform2.write(1, `${r.uri} ${r.name}${mime}${desc}
|
|
14807
|
+
`);
|
|
14808
|
+
}
|
|
14809
|
+
if (templates.length > 0) {
|
|
14810
|
+
if (resources.length > 0) await platform2.write(1, "\n");
|
|
14811
|
+
await platform2.write(1, "Templates:\n");
|
|
14812
|
+
for (const t of templates) {
|
|
14813
|
+
const desc = t.description ? ` ${t.description}` : "";
|
|
14814
|
+
await platform2.write(1, ` ${t.uriTemplate} ${t.name}${desc}
|
|
14815
|
+
`);
|
|
14816
|
+
}
|
|
14817
|
+
}
|
|
14818
|
+
return EXIT_OK;
|
|
14819
|
+
}
|
|
14820
|
+
async function runRead(platform2, conn, uri, json, outputPath) {
|
|
14821
|
+
const result = await conn.readResource(uri);
|
|
14822
|
+
if (json) {
|
|
14823
|
+
await renderReadResourceJson(platform2, result);
|
|
14824
|
+
return EXIT_OK;
|
|
14825
|
+
}
|
|
14826
|
+
for (const content of result.contents) {
|
|
14827
|
+
if ("text" in content) {
|
|
14828
|
+
if (outputPath) {
|
|
14829
|
+
await platform2.fs.writeFile(outputPath, content.text);
|
|
14830
|
+
await platform2.write(1, `${outputPath}
|
|
14831
|
+
`);
|
|
14832
|
+
} else {
|
|
14833
|
+
await platform2.write(1, `${content.text}
|
|
14834
|
+
`);
|
|
14835
|
+
}
|
|
14836
|
+
} else if ("blob" in content) {
|
|
14837
|
+
if (outputPath) {
|
|
14838
|
+
const bytes = Uint8Array.from(atob(content.blob), (c) => c.charCodeAt(0));
|
|
14839
|
+
await platform2.fs.writeFile(outputPath, bytes);
|
|
14840
|
+
await platform2.write(1, `${outputPath}
|
|
14841
|
+
`);
|
|
14842
|
+
} else {
|
|
14843
|
+
await platform2.write(1, `${content.blob}
|
|
14844
|
+
`);
|
|
14845
|
+
}
|
|
14846
|
+
}
|
|
14847
|
+
}
|
|
14848
|
+
return EXIT_OK;
|
|
14849
|
+
}
|
|
14850
|
+
async function runPrompts(platform2, conn, json) {
|
|
14851
|
+
const prompts = await conn.listPrompts();
|
|
14852
|
+
if (json) {
|
|
14853
|
+
await renderPromptsJson(platform2, prompts);
|
|
14854
|
+
return EXIT_OK;
|
|
14855
|
+
}
|
|
14856
|
+
if (prompts.length === 0) {
|
|
14857
|
+
await platform2.write(1, "(no prompts)\n");
|
|
14858
|
+
return EXIT_OK;
|
|
14859
|
+
}
|
|
14860
|
+
for (const p of prompts) {
|
|
14861
|
+
const desc = p.description ? ` ${p.description}` : "";
|
|
14862
|
+
await platform2.write(1, `${p.name}${desc}
|
|
14863
|
+
`);
|
|
14864
|
+
}
|
|
14865
|
+
return EXIT_OK;
|
|
14866
|
+
}
|
|
14867
|
+
async function runPrompt(platform2, conn, name, dataArg, toolArgs, json) {
|
|
14868
|
+
let args2 = {};
|
|
14869
|
+
if (dataArg) {
|
|
14870
|
+
args2 = await readDataArg(platform2, dataArg);
|
|
14871
|
+
}
|
|
14872
|
+
const prompts = await conn.listPrompts();
|
|
14873
|
+
const prompt = prompts.find((p) => p.name === name);
|
|
14874
|
+
const schema = {
|
|
14875
|
+
type: "object",
|
|
14876
|
+
properties: {},
|
|
14877
|
+
required: []
|
|
14878
|
+
};
|
|
14879
|
+
if (prompt?.arguments) {
|
|
14880
|
+
const props = {};
|
|
14881
|
+
const req = [];
|
|
14882
|
+
for (const a of prompt.arguments) {
|
|
14883
|
+
props[a.name] = { type: "string", description: a.description };
|
|
14884
|
+
if (a.required) req.push(a.name);
|
|
14885
|
+
}
|
|
14886
|
+
schema.properties = props;
|
|
14887
|
+
schema.required = req;
|
|
14888
|
+
}
|
|
14889
|
+
if (toolArgs.length > 0) {
|
|
14890
|
+
const skipRequired = !!dataArg;
|
|
14891
|
+
const c = coerceArgsBySchema(toolArgs, schema, { noValidate: skipRequired });
|
|
14892
|
+
if (!c.ok) {
|
|
14893
|
+
await writeErr(platform2, json, "usage", c.error);
|
|
14894
|
+
return EXIT_USAGE;
|
|
14895
|
+
}
|
|
14896
|
+
args2 = mergeArgs(args2, c.value);
|
|
14897
|
+
} else if (!dataArg) {
|
|
14898
|
+
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
14899
|
+
for (const r of required) {
|
|
14900
|
+
if (!Object.hasOwn(args2, r)) {
|
|
14901
|
+
await writeErr(platform2, json, "usage", `missing required flag --${r}`);
|
|
14902
|
+
return EXIT_USAGE;
|
|
14903
|
+
}
|
|
14904
|
+
}
|
|
14905
|
+
}
|
|
14906
|
+
const result = await conn.getPrompt(name, args2);
|
|
14907
|
+
if (json) {
|
|
14908
|
+
await renderPromptResultJson(platform2, result);
|
|
14909
|
+
return EXIT_OK;
|
|
14910
|
+
}
|
|
14911
|
+
if (result.description) {
|
|
14912
|
+
await platform2.write(1, `${result.description}
|
|
14913
|
+
|
|
14914
|
+
`);
|
|
14915
|
+
}
|
|
14916
|
+
for (const msg of result.messages) {
|
|
14917
|
+
const text = msg.content?.text ?? (msg.content?.data ? `[${msg.content.mimeType ?? "binary"}]` : "");
|
|
14918
|
+
await platform2.write(1, `[${msg.role}] ${text}
|
|
14919
|
+
`);
|
|
14920
|
+
}
|
|
14921
|
+
return EXIT_OK;
|
|
14922
|
+
}
|
|
14923
|
+
async function runVerb(platform2, parsed, conn, onProgress, respondData) {
|
|
13688
14924
|
switch (parsed.verb) {
|
|
13689
14925
|
case "info":
|
|
13690
14926
|
return runInfo(platform2, conn, parsed.jsonOutput);
|
|
13691
14927
|
case "tools":
|
|
13692
14928
|
return runTools(platform2, conn, parsed.jsonOutput);
|
|
13693
14929
|
case "call":
|
|
13694
|
-
return runCall(platform2, conn, parsed.toolName, parsed.dataArg,
|
|
14930
|
+
return runCall(platform2, conn, parsed.toolName, parsed.dataArg, parsed.toolArgs, {
|
|
13695
14931
|
json: parsed.jsonOutput,
|
|
13696
|
-
|
|
14932
|
+
noValidate: parsed.noValidate,
|
|
14933
|
+
outputPath: parsed.outputPath,
|
|
14934
|
+
onProgress,
|
|
14935
|
+
inputResponses: respondData
|
|
13697
14936
|
});
|
|
13698
14937
|
case "describe":
|
|
13699
14938
|
return runDescribe(platform2, conn, parsed.toolName, parsed.showSchema, parsed.jsonOutput);
|
|
14939
|
+
case "resources":
|
|
14940
|
+
return runResources(platform2, conn, parsed.jsonOutput);
|
|
14941
|
+
case "read":
|
|
14942
|
+
return runRead(platform2, conn, parsed.toolName, parsed.jsonOutput, parsed.outputPath);
|
|
14943
|
+
case "prompts":
|
|
14944
|
+
return runPrompts(platform2, conn, parsed.jsonOutput);
|
|
14945
|
+
case "prompt":
|
|
14946
|
+
return runPrompt(
|
|
14947
|
+
platform2,
|
|
14948
|
+
conn,
|
|
14949
|
+
parsed.toolName,
|
|
14950
|
+
parsed.dataArg,
|
|
14951
|
+
parsed.toolArgs,
|
|
14952
|
+
parsed.jsonOutput
|
|
14953
|
+
);
|
|
13700
14954
|
default:
|
|
13701
14955
|
await platform2.write(2, `mcpcat: unknown command '${parsed.verb}'
|
|
13702
14956
|
`);
|
|
@@ -13834,6 +15088,18 @@ async function resolveTargetNames(platform2, parsed) {
|
|
|
13834
15088
|
return config.list().filter((e) => e.config.enabled !== false).map((e) => e.name);
|
|
13835
15089
|
}
|
|
13836
15090
|
async function dispatchMultiTarget(platform2, parsed, version) {
|
|
15091
|
+
let activeCleanup;
|
|
15092
|
+
async function killTreeAndExit(exitCode) {
|
|
15093
|
+
if (activeCleanup) {
|
|
15094
|
+
try {
|
|
15095
|
+
await Promise.race([activeCleanup(), new Promise((r) => setTimeout(r, 3e3))]);
|
|
15096
|
+
} catch {
|
|
15097
|
+
}
|
|
15098
|
+
}
|
|
15099
|
+
platform2.exit(exitCode);
|
|
15100
|
+
}
|
|
15101
|
+
platform2.onSignal?.("SIGTERM", () => void killTreeAndExit(143));
|
|
15102
|
+
platform2.onSignal?.("SIGHUP", () => void killTreeAndExit(129));
|
|
13837
15103
|
const config = await MergedConfigSource.load(platform2);
|
|
13838
15104
|
if (parsed.verb === "code") {
|
|
13839
15105
|
let source;
|
|
@@ -13865,10 +15131,12 @@ async function dispatchMultiTarget(platform2, parsed, version) {
|
|
|
13865
15131
|
parsed,
|
|
13866
15132
|
version
|
|
13867
15133
|
);
|
|
15134
|
+
activeCleanup = cleanup;
|
|
13868
15135
|
try {
|
|
13869
15136
|
return await runCode(targets, source, {
|
|
13870
15137
|
json: parsed.jsonOutput,
|
|
13871
15138
|
timeoutMs: parsed.timeoutMs,
|
|
15139
|
+
memoryLimitMiB: parsed.memoryLimitMiB,
|
|
13872
15140
|
allowShell: parsed.allowShell,
|
|
13873
15141
|
platform: platform2
|
|
13874
15142
|
});
|
|
@@ -13894,6 +15162,7 @@ async function dispatchMultiTarget(platform2, parsed, version) {
|
|
|
13894
15162
|
parsed,
|
|
13895
15163
|
version
|
|
13896
15164
|
);
|
|
15165
|
+
activeCleanup = cleanup;
|
|
13897
15166
|
try {
|
|
13898
15167
|
return await runGrep(platform2, pattern, targets, {
|
|
13899
15168
|
rank: parsed.rank,
|
|
@@ -13912,6 +15181,7 @@ async function dispatchMultiTarget(platform2, parsed, version) {
|
|
|
13912
15181
|
parsed,
|
|
13913
15182
|
version
|
|
13914
15183
|
);
|
|
15184
|
+
activeCleanup = cleanup;
|
|
13915
15185
|
try {
|
|
13916
15186
|
return await runDescribeTs(platform2, targets, parsed.tsBudget);
|
|
13917
15187
|
} finally {
|
|
@@ -13921,6 +15191,18 @@ async function dispatchMultiTarget(platform2, parsed, version) {
|
|
|
13921
15191
|
return 2;
|
|
13922
15192
|
}
|
|
13923
15193
|
async function run(platform2, version, args2) {
|
|
15194
|
+
try {
|
|
15195
|
+
return await runInner(platform2, version, args2);
|
|
15196
|
+
} catch (err) {
|
|
15197
|
+
if (err.code === "EACCES") {
|
|
15198
|
+
await platform2.write(2, `mcpcat: ${err.message}
|
|
15199
|
+
`);
|
|
15200
|
+
return EXIT_USAGE;
|
|
15201
|
+
}
|
|
15202
|
+
throw err;
|
|
15203
|
+
}
|
|
15204
|
+
}
|
|
15205
|
+
async function runInner(platform2, version, args2) {
|
|
13924
15206
|
if (args2.includes("--version") || args2.includes("-V")) {
|
|
13925
15207
|
await platform2.write(1, `${version}
|
|
13926
15208
|
`);
|
|
@@ -13947,11 +15229,11 @@ async function run(platform2, version, args2) {
|
|
|
13947
15229
|
const discResult = await dispatchDiscoveryVerb(platform2, parsed, args2);
|
|
13948
15230
|
if (discResult !== void 0) return discResult;
|
|
13949
15231
|
if (parsed.verb === "login") {
|
|
13950
|
-
const source = createTokenSource(platform2);
|
|
15232
|
+
const source = await createTokenSource(platform2);
|
|
13951
15233
|
return runLogin(platform2, args2.slice(1), source);
|
|
13952
15234
|
}
|
|
13953
15235
|
if (parsed.verb === "logout") {
|
|
13954
|
-
const source = createTokenSource(platform2);
|
|
15236
|
+
const source = await createTokenSource(platform2);
|
|
13955
15237
|
return runLogout(platform2, args2.slice(1), source);
|
|
13956
15238
|
}
|
|
13957
15239
|
if (UNIMPLEMENTED_VERBS.has(parsed.verb)) {
|
|
@@ -13974,6 +15256,14 @@ async function run(platform2, version, args2) {
|
|
|
13974
15256
|
await platform2.write(2, "mcpcat: call requires a tool name\n");
|
|
13975
15257
|
return 2;
|
|
13976
15258
|
}
|
|
15259
|
+
if (parsed.verb === "read" && !parsed.toolName) {
|
|
15260
|
+
await platform2.write(2, "mcpcat: read requires a URI\n");
|
|
15261
|
+
return 2;
|
|
15262
|
+
}
|
|
15263
|
+
if (parsed.verb === "prompt" && !parsed.toolName) {
|
|
15264
|
+
await platform2.write(2, "mcpcat: prompt requires a name\n");
|
|
15265
|
+
return 2;
|
|
15266
|
+
}
|
|
13977
15267
|
if (!parsed.target) {
|
|
13978
15268
|
await platform2.write(2, `mcpcat: ${parsed.verb} requires a target
|
|
13979
15269
|
`);
|
|
@@ -13984,13 +15274,19 @@ async function run(platform2, version, args2) {
|
|
|
13984
15274
|
function expandAndCollect(headers, env) {
|
|
13985
15275
|
const secrets = [];
|
|
13986
15276
|
const result = {};
|
|
15277
|
+
const unset = [];
|
|
13987
15278
|
for (const [k, v] of Object.entries(headers)) {
|
|
15279
|
+
const missing = missingPlaceholders(v, env);
|
|
15280
|
+
if (missing.length > 0) {
|
|
15281
|
+
unset.push(...missing);
|
|
15282
|
+
continue;
|
|
15283
|
+
}
|
|
13988
15284
|
const e = expandPlaceholders(v, env);
|
|
13989
15285
|
result[k] = e;
|
|
13990
15286
|
if (e !== v) secrets.push(e);
|
|
13991
15287
|
if (v.length > 0) secrets.push(v);
|
|
13992
15288
|
}
|
|
13993
|
-
return { expanded: result, secrets };
|
|
15289
|
+
return { expanded: result, secrets, unset };
|
|
13994
15290
|
}
|
|
13995
15291
|
function classifyError(err) {
|
|
13996
15292
|
if (err instanceof InputRequiredError) {
|
|
@@ -14002,6 +15298,9 @@ function classifyError(err) {
|
|
|
14002
15298
|
if (err instanceof McpAuthError) {
|
|
14003
15299
|
return { code: EXIT_AUTH, kind: "auth", msg: err.message };
|
|
14004
15300
|
}
|
|
15301
|
+
if (err instanceof JerkSignInRequiredError) {
|
|
15302
|
+
return { code: EXIT_AUTH, kind: "auth", msg: err.message };
|
|
15303
|
+
}
|
|
14005
15304
|
if (err instanceof AuthStepUpError) {
|
|
14006
15305
|
const scopeHint = err.scope ? ` --scope ${err.scope}` : "";
|
|
14007
15306
|
return {
|
|
@@ -14023,25 +15322,112 @@ function classifyError(err) {
|
|
|
14023
15322
|
}
|
|
14024
15323
|
return { code: EXIT_RPC_ERROR, kind: "rpc_error", msg };
|
|
14025
15324
|
}
|
|
14026
|
-
function resolveAuthProvider(platform2, hasAuthHeader, url, target, config) {
|
|
15325
|
+
async function resolveAuthProvider(platform2, hasAuthHeader, url, target, config) {
|
|
14027
15326
|
if (hasAuthHeader || !url) return void 0;
|
|
14028
|
-
const source = createTokenSource(platform2);
|
|
15327
|
+
const source = await createTokenSource(platform2);
|
|
14029
15328
|
const entry = config.entry(target);
|
|
14030
15329
|
const oauth = entry && "url" in entry.config ? entry.config.oauth : void 0;
|
|
14031
15330
|
const name = entry ? target : `url:${new URL(url).origin}${new URL(url).pathname}`;
|
|
14032
15331
|
return createMcpAuthProvider({ name, serverUrl: url, source, oauth, platform: platform2 });
|
|
14033
15332
|
}
|
|
15333
|
+
function classifyConnectionError(err, target, interrupted) {
|
|
15334
|
+
if (err instanceof McpAuthError || err instanceof AuthStepUpError || err instanceof JerkSignInRequiredError) {
|
|
15335
|
+
return classifyError(err);
|
|
15336
|
+
}
|
|
15337
|
+
if (err.name === "McpOAuthAuthorizationRequiredError" || err.name === "McpAuthRequiredError") {
|
|
15338
|
+
return { code: EXIT_AUTH, kind: "auth", msg: `unauthorized. Run: mcpcat login ${target}` };
|
|
15339
|
+
}
|
|
15340
|
+
if (interrupted) {
|
|
15341
|
+
return { code: EXIT_INTERRUPTED, kind: "interrupted", msg: "interrupted" };
|
|
15342
|
+
}
|
|
15343
|
+
const msg = transportHint(err) ?? `connection failed: ${err.message}`;
|
|
15344
|
+
return { code: EXIT_CONNECTION, kind: "connection", msg };
|
|
15345
|
+
}
|
|
15346
|
+
function buildRoots(rootArgs, isStdio, cwd) {
|
|
15347
|
+
const roots = rootArgs.map((r) => ({
|
|
15348
|
+
uri: r.startsWith("file://") ? r : `file://${r}`
|
|
15349
|
+
}));
|
|
15350
|
+
if (roots.length === 0 && isStdio) {
|
|
15351
|
+
roots.push({ uri: `file://${cwd}` });
|
|
15352
|
+
}
|
|
15353
|
+
return roots.length > 0 ? roots : void 0;
|
|
15354
|
+
}
|
|
15355
|
+
async function loadRespondData(platform2, respondArg, json) {
|
|
15356
|
+
if (!respondArg) return { data: void 0 };
|
|
15357
|
+
try {
|
|
15358
|
+
return { data: await readDataArg(platform2, respondArg) };
|
|
15359
|
+
} catch (err) {
|
|
15360
|
+
await writeErr(
|
|
15361
|
+
platform2,
|
|
15362
|
+
json,
|
|
15363
|
+
"usage",
|
|
15364
|
+
`invalid --respond argument: ${err.message}`
|
|
15365
|
+
);
|
|
15366
|
+
return { exitCode: EXIT_USAGE };
|
|
15367
|
+
}
|
|
15368
|
+
}
|
|
15369
|
+
async function handleInputRequired(platform2, conn, err, respondData, json) {
|
|
15370
|
+
const parsed2 = JSON.parse(err.requestJson);
|
|
15371
|
+
const inputReq = parseInputRequest(parsed2.result ?? parsed2);
|
|
15372
|
+
if (!inputReq) {
|
|
15373
|
+
await platform2.write(2, `${err.requestJson}
|
|
15374
|
+
`);
|
|
15375
|
+
return EXIT_INPUT_REQUIRED;
|
|
15376
|
+
}
|
|
15377
|
+
const isTty = platform2.isatty(0);
|
|
15378
|
+
const elicitResult = await handleElicitation(platform2, inputReq, isTty, respondData);
|
|
15379
|
+
if ("exitCode" in elicitResult) {
|
|
15380
|
+
await platform2.write(2, `${err.requestJson}
|
|
15381
|
+
`);
|
|
15382
|
+
return EXIT_INPUT_REQUIRED;
|
|
15383
|
+
}
|
|
15384
|
+
try {
|
|
15385
|
+
const retryResult = await conn.callTool(parsed2.name, {
|
|
15386
|
+
...parsed2.arguments,
|
|
15387
|
+
_meta: { inputResponses: elicitResult.answers }
|
|
15388
|
+
});
|
|
15389
|
+
return renderCallResult(platform2, retryResult, { json });
|
|
15390
|
+
} catch (retryErr) {
|
|
15391
|
+
const e2 = classifyError(retryErr);
|
|
15392
|
+
await writeErr(platform2, json, e2.kind, e2.msg);
|
|
15393
|
+
return e2.code;
|
|
15394
|
+
}
|
|
15395
|
+
}
|
|
14034
15396
|
async function runClientVerb(platform2, version, parsed) {
|
|
14035
|
-
const { expanded, secrets } = expandAndCollect(parsed.headers, platform2.env);
|
|
15397
|
+
const { expanded, secrets, unset } = expandAndCollect(parsed.headers, platform2.env);
|
|
15398
|
+
if (unset.length > 0) {
|
|
15399
|
+
await platform2.write(
|
|
15400
|
+
2,
|
|
15401
|
+
`mcpcat: unset variable${unset.length > 1 ? "s" : ""} in -H: ${unset.join(", ")}
|
|
15402
|
+
`
|
|
15403
|
+
);
|
|
15404
|
+
return EXIT_USAGE;
|
|
15405
|
+
}
|
|
14036
15406
|
const verbose = createVerboseLogger(platform2, parsed.verbose);
|
|
14037
15407
|
for (const s of secrets) verbose.secrets.add(s);
|
|
14038
15408
|
const timeoutMs = parsed.timeoutMs;
|
|
14039
15409
|
const sigintController = new AbortController();
|
|
14040
15410
|
let interrupted = false;
|
|
15411
|
+
let termExitCode = EXIT_INTERRUPTED;
|
|
15412
|
+
let activeConn;
|
|
15413
|
+
async function killTreeAndExit(exitCode) {
|
|
15414
|
+
interrupted = true;
|
|
15415
|
+
termExitCode = exitCode;
|
|
15416
|
+
sigintController.abort();
|
|
15417
|
+
if (activeConn) {
|
|
15418
|
+
try {
|
|
15419
|
+
await Promise.race([activeConn.close(), new Promise((r) => setTimeout(r, 3e3))]);
|
|
15420
|
+
} catch {
|
|
15421
|
+
}
|
|
15422
|
+
}
|
|
15423
|
+
platform2.exit(exitCode);
|
|
15424
|
+
}
|
|
14041
15425
|
platform2.onSignal?.("SIGINT", () => {
|
|
14042
15426
|
interrupted = true;
|
|
14043
15427
|
sigintController.abort();
|
|
14044
15428
|
});
|
|
15429
|
+
platform2.onSignal?.("SIGTERM", () => void killTreeAndExit(143));
|
|
15430
|
+
platform2.onSignal?.("SIGHUP", () => void killTreeAndExit(129));
|
|
14045
15431
|
const showProgress = !parsed.silent && (parsed.verbose || platform2.isatty(2));
|
|
14046
15432
|
const onProgress = (progress, total, message) => {
|
|
14047
15433
|
if (!showProgress) return;
|
|
@@ -14060,59 +15446,91 @@ async function runClientVerb(platform2, version, parsed) {
|
|
|
14060
15446
|
await writeErr(platform2, parsed.jsonOutput, "usage", err.message);
|
|
14061
15447
|
return EXIT_USAGE;
|
|
14062
15448
|
}
|
|
14063
|
-
const
|
|
14064
|
-
|
|
15449
|
+
const configHeaders = expandHeaders(resolved.headers, platform2.env, (msg) => {
|
|
15450
|
+
platform2.write(2, `mcpcat: warning: ${msg}
|
|
15451
|
+
`).catch(() => {
|
|
15452
|
+
});
|
|
15453
|
+
});
|
|
15454
|
+
const configSecrets = collectConfigSecrets(resolved.headers, platform2.env);
|
|
15455
|
+
for (const s of configSecrets) verbose.secrets.add(s);
|
|
15456
|
+
const allSecrets = /* @__PURE__ */ new Set([...secrets, ...configSecrets]);
|
|
15457
|
+
const redact2 = (text) => {
|
|
15458
|
+
let result = text;
|
|
15459
|
+
for (const secret of allSecrets) {
|
|
15460
|
+
if (secret.length > 0) {
|
|
15461
|
+
while (result.includes(secret)) {
|
|
15462
|
+
result = result.replace(secret, "[REDACTED]");
|
|
15463
|
+
}
|
|
15464
|
+
}
|
|
15465
|
+
}
|
|
15466
|
+
return result;
|
|
15467
|
+
};
|
|
15468
|
+
const mergedHeaders = { ...configHeaders, ...expanded };
|
|
15469
|
+
const hasAuthHeader = "Authorization" in mergedHeaders;
|
|
15470
|
+
const authProvider = await resolveAuthProvider(
|
|
15471
|
+
platform2,
|
|
15472
|
+
hasAuthHeader,
|
|
15473
|
+
resolved.url,
|
|
15474
|
+
target,
|
|
15475
|
+
config
|
|
15476
|
+
);
|
|
15477
|
+
const roots = buildRoots(parsed.roots, !!resolved.command, platform2.cwd);
|
|
15478
|
+
let respondData;
|
|
15479
|
+
if (parsed.respondArg) {
|
|
15480
|
+
const loaded = await loadRespondData(platform2, parsed.respondArg, parsed.jsonOutput);
|
|
15481
|
+
if ("exitCode" in loaded) return loaded.exitCode;
|
|
15482
|
+
respondData = loaded.data;
|
|
15483
|
+
}
|
|
14065
15484
|
let conn;
|
|
14066
15485
|
try {
|
|
14067
15486
|
conn = await connect(platform2, {
|
|
14068
15487
|
...resolved,
|
|
14069
|
-
headers:
|
|
15488
|
+
headers: mergedHeaders,
|
|
14070
15489
|
protocolVersion: parsed.protocolVersion,
|
|
14071
15490
|
version,
|
|
14072
15491
|
timeoutMs,
|
|
14073
15492
|
signal: sigintController.signal,
|
|
14074
15493
|
verbose,
|
|
14075
|
-
authProvider
|
|
15494
|
+
authProvider,
|
|
15495
|
+
roots
|
|
14076
15496
|
});
|
|
15497
|
+
activeConn = conn;
|
|
14077
15498
|
} catch (err) {
|
|
14078
|
-
if (err
|
|
14079
|
-
|
|
14080
|
-
await writeErr(platform2, parsed.jsonOutput, e.kind, e.msg);
|
|
14081
|
-
return EXIT_AUTH;
|
|
14082
|
-
}
|
|
14083
|
-
if (err.name === "McpOAuthAuthorizationRequiredError" || err.name === "McpAuthRequiredError") {
|
|
14084
|
-
await writeErr(
|
|
15499
|
+
if (isSpawnNotFound(err)) {
|
|
15500
|
+
await writeErrRedacted(
|
|
14085
15501
|
platform2,
|
|
14086
15502
|
parsed.jsonOutput,
|
|
14087
|
-
"
|
|
14088
|
-
`
|
|
15503
|
+
"connection",
|
|
15504
|
+
`command not found: ${resolved.command}`,
|
|
15505
|
+
redact2
|
|
14089
15506
|
);
|
|
14090
|
-
return
|
|
15507
|
+
return EXIT_CONNECTION;
|
|
14091
15508
|
}
|
|
14092
|
-
const
|
|
14093
|
-
if (
|
|
15509
|
+
const e = classifyConnectionError(err, target, interrupted);
|
|
15510
|
+
if (e.code === EXIT_INTERRUPTED) {
|
|
14094
15511
|
await platform2.write(2, "mcpcat: interrupted\n");
|
|
14095
|
-
return
|
|
15512
|
+
return termExitCode;
|
|
14096
15513
|
}
|
|
14097
|
-
await
|
|
14098
|
-
return
|
|
15514
|
+
await writeErrRedacted(platform2, parsed.jsonOutput, e.kind, e.msg, redact2);
|
|
15515
|
+
return e.code;
|
|
14099
15516
|
}
|
|
14100
15517
|
try {
|
|
14101
|
-
const code2 = await runVerb(platform2, parsed, conn, onProgress);
|
|
15518
|
+
const code2 = await runVerb(platform2, parsed, conn, onProgress, respondData);
|
|
14102
15519
|
if (interrupted) {
|
|
14103
15520
|
await platform2.write(2, "mcpcat: interrupted\n");
|
|
14104
|
-
return
|
|
15521
|
+
return termExitCode;
|
|
14105
15522
|
}
|
|
14106
15523
|
return code2;
|
|
14107
15524
|
} catch (err) {
|
|
14108
15525
|
if (interrupted) {
|
|
14109
15526
|
await platform2.write(2, "mcpcat: interrupted\n");
|
|
14110
|
-
return
|
|
15527
|
+
return termExitCode;
|
|
15528
|
+
}
|
|
15529
|
+
if (err instanceof InputRequiredError) {
|
|
15530
|
+
return handleInputRequired(platform2, conn, err, respondData, parsed.jsonOutput);
|
|
14111
15531
|
}
|
|
14112
15532
|
const e = classifyError(err);
|
|
14113
|
-
|
|
14114
|
-
`);
|
|
14115
|
-
else await writeErr(platform2, parsed.jsonOutput, e.kind, e.msg);
|
|
15533
|
+
await writeErrRedacted(platform2, parsed.jsonOutput, e.kind, e.msg, redact2);
|
|
14116
15534
|
return e.code;
|
|
14117
15535
|
} finally {
|
|
14118
15536
|
try {
|
|
@@ -14126,53 +15544,85 @@ async function writeErr(p, json, kind, msg) {
|
|
|
14126
15544
|
else await p.write(2, `mcpcat: ${msg}
|
|
14127
15545
|
`);
|
|
14128
15546
|
}
|
|
15547
|
+
async function writeErrRedacted(p, json, kind, msg, redact2) {
|
|
15548
|
+
if (json) await renderErrorJson(p, kind, msg, void 0, void 0, redact2);
|
|
15549
|
+
else await p.write(2, `mcpcat: ${redact2(msg)}
|
|
15550
|
+
`);
|
|
15551
|
+
}
|
|
15552
|
+
function collectConfigSecrets(headers, env) {
|
|
15553
|
+
if (!headers) return [];
|
|
15554
|
+
const secrets = [];
|
|
15555
|
+
for (const [, v] of Object.entries(headers)) {
|
|
15556
|
+
const expanded = expandPlaceholders(v, env);
|
|
15557
|
+
if (expanded !== v && expanded.length > 0) secrets.push(expanded);
|
|
15558
|
+
if (v.length > 0 && v !== expanded) secrets.push(v);
|
|
15559
|
+
}
|
|
15560
|
+
return secrets;
|
|
15561
|
+
}
|
|
15562
|
+
function isSpawnNotFound(err) {
|
|
15563
|
+
if (!(err instanceof Error)) return false;
|
|
15564
|
+
const code2 = err.code;
|
|
15565
|
+
if (code2 === "ENOENT" || code2 === "EACCES") return true;
|
|
15566
|
+
const msg = err.message;
|
|
15567
|
+
return msg.includes("ENOENT") || msg.includes("EACCES");
|
|
15568
|
+
}
|
|
14129
15569
|
|
|
14130
15570
|
// src/platform/host.ts
|
|
14131
15571
|
import { spawn as cpSpawn } from "node:child_process";
|
|
14132
15572
|
import { constants } from "node:fs";
|
|
14133
|
-
import { access, mkdir, readFile as readFile3, rename, rmdir, stat, writeFile } from "node:fs/promises";
|
|
15573
|
+
import { access, mkdir, readFile as readFile3, rename, rmdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
14134
15574
|
import { createServer as createServer2 } from "node:net";
|
|
14135
15575
|
import { Readable, Writable } from "node:stream";
|
|
14136
15576
|
import * as tty from "node:tty";
|
|
14137
15577
|
var USE_PROCESS_GROUPS2 = process.platform !== "win32";
|
|
14138
15578
|
function nodeSpawn(options) {
|
|
14139
|
-
|
|
14140
|
-
|
|
14141
|
-
|
|
14142
|
-
|
|
14143
|
-
|
|
14144
|
-
|
|
14145
|
-
|
|
14146
|
-
|
|
14147
|
-
|
|
14148
|
-
|
|
14149
|
-
|
|
14150
|
-
|
|
14151
|
-
|
|
14152
|
-
|
|
14153
|
-
|
|
14154
|
-
|
|
14155
|
-
|
|
14156
|
-
|
|
14157
|
-
|
|
14158
|
-
|
|
15579
|
+
return new Promise((resolve, reject) => {
|
|
15580
|
+
let rejected = false;
|
|
15581
|
+
const child = cpSpawn(options.argv[0], options.argv.slice(1), {
|
|
15582
|
+
env: options.env ?? process.env,
|
|
15583
|
+
cwd: options.cwd,
|
|
15584
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
15585
|
+
detached: USE_PROCESS_GROUPS2
|
|
15586
|
+
});
|
|
15587
|
+
child.on("error", (err) => {
|
|
15588
|
+
if (!rejected) {
|
|
15589
|
+
rejected = true;
|
|
15590
|
+
reject(err);
|
|
15591
|
+
}
|
|
15592
|
+
});
|
|
15593
|
+
const stdout = Readable.toWeb(child.stdout);
|
|
15594
|
+
const stderr = Readable.toWeb(child.stderr);
|
|
15595
|
+
const stdin = Writable.toWeb(child.stdin);
|
|
15596
|
+
const closePromise = new Promise((res) => {
|
|
15597
|
+
child.on("close", (code2, signal) => {
|
|
15598
|
+
res({
|
|
15599
|
+
status: signal ? 128 + (signalNumber(signal) ?? 0) : code2 ?? 1,
|
|
15600
|
+
signal: signal ?? void 0
|
|
14159
15601
|
});
|
|
14160
15602
|
});
|
|
14161
|
-
}
|
|
14162
|
-
|
|
14163
|
-
|
|
14164
|
-
|
|
14165
|
-
|
|
14166
|
-
|
|
14167
|
-
|
|
14168
|
-
|
|
14169
|
-
|
|
15603
|
+
});
|
|
15604
|
+
if (child.pid !== void 0) {
|
|
15605
|
+
resolve({
|
|
15606
|
+
stdin,
|
|
15607
|
+
stdout,
|
|
15608
|
+
stderr,
|
|
15609
|
+
wait: () => closePromise,
|
|
15610
|
+
kill(signal) {
|
|
15611
|
+
const sig = signal ?? "SIGTERM";
|
|
15612
|
+
const pid = child.pid;
|
|
15613
|
+
if (USE_PROCESS_GROUPS2 && pid !== void 0) {
|
|
15614
|
+
try {
|
|
15615
|
+
process.kill(-pid, sig);
|
|
15616
|
+
return;
|
|
15617
|
+
} catch {
|
|
15618
|
+
}
|
|
15619
|
+
}
|
|
15620
|
+
try {
|
|
15621
|
+
child.kill(sig);
|
|
15622
|
+
} catch {
|
|
15623
|
+
}
|
|
14170
15624
|
}
|
|
14171
|
-
}
|
|
14172
|
-
try {
|
|
14173
|
-
child.kill(sig);
|
|
14174
|
-
} catch {
|
|
14175
|
-
}
|
|
15625
|
+
});
|
|
14176
15626
|
}
|
|
14177
15627
|
});
|
|
14178
15628
|
}
|
|
@@ -14193,8 +15643,11 @@ var hostFs = {
|
|
|
14193
15643
|
async writeFile(path, data, options) {
|
|
14194
15644
|
await writeFile(path, data, options?.mode !== void 0 ? { mode: options.mode } : void 0);
|
|
14195
15645
|
},
|
|
14196
|
-
async mkdir(path) {
|
|
14197
|
-
await mkdir(path, {
|
|
15646
|
+
async mkdir(path, options) {
|
|
15647
|
+
await mkdir(path, {
|
|
15648
|
+
recursive: true,
|
|
15649
|
+
...options?.mode !== void 0 ? { mode: options.mode } : {}
|
|
15650
|
+
});
|
|
14198
15651
|
},
|
|
14199
15652
|
async rename(from, to) {
|
|
14200
15653
|
await rename(from, to);
|
|
@@ -14216,6 +15669,9 @@ var hostFs = {
|
|
|
14216
15669
|
},
|
|
14217
15670
|
async rmdir(path) {
|
|
14218
15671
|
await rmdir(path);
|
|
15672
|
+
},
|
|
15673
|
+
async unlink(path) {
|
|
15674
|
+
await unlink(path);
|
|
14219
15675
|
}
|
|
14220
15676
|
};
|
|
14221
15677
|
function createStdinStream() {
|
|
@@ -14327,5 +15783,14 @@ function createHostPlatform() {
|
|
|
14327
15783
|
// src/host.ts
|
|
14328
15784
|
var platform = createHostPlatform();
|
|
14329
15785
|
var args = process.argv.slice(2);
|
|
14330
|
-
|
|
15786
|
+
try {
|
|
15787
|
+
process.stdout.on("error", () => {
|
|
15788
|
+
});
|
|
15789
|
+
process.stderr.on("error", () => {
|
|
15790
|
+
});
|
|
15791
|
+
process.on("SIGPIPE", () => {
|
|
15792
|
+
});
|
|
15793
|
+
} catch {
|
|
15794
|
+
}
|
|
15795
|
+
var code = await run(platform, "0.14.0", args);
|
|
14331
15796
|
process.exit(code);
|