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