@ai-ecoverse/mcpcat 0.15.1 → 0.15.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/host/mcpcat.mjs +82 -53
- package/dist/kernel/mcpcat.mjs +101 -55
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ Or via npm:
|
|
|
22
22
|
npm install -g @ai-ecoverse/mcpcat
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
mcpcat runs natively on macOS, Linux and Windows (Node ≥ 24), and inside Sliccy Seven as a kernel program, with the same flags, the same output and the same config file in both places. Orphan-free shutdown of stdio server trees in the kernel needs slicc-kernel ≥ 1.74.0; on older kernels only the direct child is signalled.
|
|
25
|
+
mcpcat runs natively on macOS, Linux and Windows (Node ≥ 24), and inside Sliccy Seven as a kernel program, with the same flags, the same output and the same config file in both places. Orphan-free shutdown of stdio server trees in the kernel needs slicc-kernel ≥ 1.74.0; on older kernels only the direct child is signalled. A SIGKILLed mcpcat runs no handler. On slicc-kernel ≥ 1.88.0, mcpcat spawns stdio servers and served commands with a parent death signal (like Linux `PR_SET_PDEATHSIG`), so the kernel SIGKILLs each server's process group when mcpcat dies. On older kernels, and on the host, where there is no portable equivalent, servers that exit on stdin EOF (most MCP servers) still stop, but servers that ignore it keep running.
|
|
26
26
|
|
|
27
27
|
## Quick start
|
|
28
28
|
|
package/dist/host/mcpcat.mjs
CHANGED
|
@@ -6477,6 +6477,7 @@ var McpOAuthProvider = class {
|
|
|
6477
6477
|
// src/auth/jerk-token-source.ts
|
|
6478
6478
|
var MIN_VERSION = [0, 14, 1];
|
|
6479
6479
|
var VERSION_TIMEOUT_MS = 2e3;
|
|
6480
|
+
var JERK_TIMEOUT_MS = 15e3;
|
|
6480
6481
|
var cachedAvailable;
|
|
6481
6482
|
function parseVersion(raw) {
|
|
6482
6483
|
const match = raw.trim().match(/(\d+)\.(\d+)\.(\d+)/);
|
|
@@ -6523,23 +6524,39 @@ async function jerkAvailable(platform2) {
|
|
|
6523
6524
|
return false;
|
|
6524
6525
|
}
|
|
6525
6526
|
try {
|
|
6526
|
-
const
|
|
6527
|
-
const
|
|
6528
|
-
|
|
6529
|
-
});
|
|
6530
|
-
const work = Promise.all([readAll(proc.stdout), proc.wait()]).then(([out, result]) => {
|
|
6531
|
-
proc.stderr.cancel().catch(() => {
|
|
6532
|
-
});
|
|
6533
|
-
if (result.status !== 0) return false;
|
|
6534
|
-
const ver = parseVersion(out);
|
|
6535
|
-
return ver !== void 0 && versionSatisfied(ver);
|
|
6536
|
-
});
|
|
6537
|
-
cachedAvailable = await Promise.race([work, timeout]);
|
|
6527
|
+
const result = await runJerk(platform2, ["jerk", "--version"], VERSION_TIMEOUT_MS);
|
|
6528
|
+
const ver = result.status === 0 ? parseVersion(result.stdout) : void 0;
|
|
6529
|
+
cachedAvailable = ver !== void 0 && versionSatisfied(ver);
|
|
6538
6530
|
} catch {
|
|
6539
6531
|
cachedAvailable = false;
|
|
6540
6532
|
}
|
|
6541
6533
|
return cachedAvailable;
|
|
6542
6534
|
}
|
|
6535
|
+
var JerkTimeoutError = class extends Error {
|
|
6536
|
+
command;
|
|
6537
|
+
constructor(argv) {
|
|
6538
|
+
const command = argv.slice(0, 3).join(" ");
|
|
6539
|
+
super(`jerk did not answer; run \`${command}\` yourself`);
|
|
6540
|
+
this.name = "JerkTimeoutError";
|
|
6541
|
+
this.command = command;
|
|
6542
|
+
}
|
|
6543
|
+
};
|
|
6544
|
+
async function runJerk(platform2, argv, timeoutMs) {
|
|
6545
|
+
const proc = await platform2.spawn({ argv, stdin: "ignore" });
|
|
6546
|
+
let timer;
|
|
6547
|
+
const expired = new Promise((resolve) => {
|
|
6548
|
+
timer = setTimeout(() => resolve("timeout"), timeoutMs);
|
|
6549
|
+
});
|
|
6550
|
+
const work = Promise.all([readAll(proc.stdout), readAll(proc.stderr), proc.wait()]);
|
|
6551
|
+
const outcome = await Promise.race([work, expired]);
|
|
6552
|
+
clearTimeout(timer);
|
|
6553
|
+
if (outcome === "timeout") {
|
|
6554
|
+
await proc.kill("SIGKILL");
|
|
6555
|
+
throw new JerkTimeoutError(argv);
|
|
6556
|
+
}
|
|
6557
|
+
const [stdout, stderr, result] = outcome;
|
|
6558
|
+
return { status: result.status, stdout, stderr };
|
|
6559
|
+
}
|
|
6543
6560
|
function jerkId(name) {
|
|
6544
6561
|
if (name.startsWith("mcp:")) return name;
|
|
6545
6562
|
if (name.startsWith("url:")) return `mcp:${name}`;
|
|
@@ -6560,8 +6577,10 @@ var JerkSignInRequiredError = class extends Error {
|
|
|
6560
6577
|
};
|
|
6561
6578
|
var JerkTokenSource = class {
|
|
6562
6579
|
platform;
|
|
6563
|
-
|
|
6580
|
+
timeoutMs;
|
|
6581
|
+
constructor(platform2, timeoutMs = JERK_TIMEOUT_MS) {
|
|
6564
6582
|
this.platform = platform2;
|
|
6583
|
+
this.timeoutMs = timeoutMs;
|
|
6565
6584
|
}
|
|
6566
6585
|
async getToken(name, serverUrl, scope) {
|
|
6567
6586
|
const id = jerkId(name);
|
|
@@ -6569,12 +6588,7 @@ var JerkTokenSource = class {
|
|
|
6569
6588
|
if (scope) {
|
|
6570
6589
|
argv.push("--scope", scope);
|
|
6571
6590
|
}
|
|
6572
|
-
const
|
|
6573
|
-
const [outRaw, , result] = await Promise.all([
|
|
6574
|
-
readAll(proc.stdout),
|
|
6575
|
-
readAll(proc.stderr),
|
|
6576
|
-
proc.wait()
|
|
6577
|
-
]);
|
|
6591
|
+
const result = await runJerk(this.platform, argv, this.timeoutMs);
|
|
6578
6592
|
if (result.status === JERK_EXIT_SIGN_IN) {
|
|
6579
6593
|
throw new JerkSignInRequiredError(id, scope);
|
|
6580
6594
|
}
|
|
@@ -6584,7 +6598,7 @@ var JerkTokenSource = class {
|
|
|
6584
6598
|
if (result.status !== 0) {
|
|
6585
6599
|
return void 0;
|
|
6586
6600
|
}
|
|
6587
|
-
const parsed = JSON.parse(
|
|
6601
|
+
const parsed = JSON.parse(result.stdout.trim());
|
|
6588
6602
|
if (!parsed.token) return void 0;
|
|
6589
6603
|
return {
|
|
6590
6604
|
accessToken: parsed.token,
|
|
@@ -8177,7 +8191,8 @@ var PlatformStdioTransport = class {
|
|
|
8177
8191
|
this.process = await this.options.platform.spawn({
|
|
8178
8192
|
argv: [this.options.command, ...this.options.args ?? []],
|
|
8179
8193
|
env,
|
|
8180
|
-
cwd: this.options.cwd
|
|
8194
|
+
cwd: this.options.cwd,
|
|
8195
|
+
dieWithParent: true
|
|
8181
8196
|
});
|
|
8182
8197
|
trackStdio(this.process);
|
|
8183
8198
|
}
|
|
@@ -8570,7 +8585,7 @@ async function createStatelessConnection(options) {
|
|
|
8570
8585
|
token
|
|
8571
8586
|
});
|
|
8572
8587
|
} catch (authErr) {
|
|
8573
|
-
if (authErr instanceof AuthStepUpError) throw authErr;
|
|
8588
|
+
if (authErr instanceof AuthStepUpError || authErr instanceof JerkTimeoutError) throw authErr;
|
|
8574
8589
|
throw new McpAuthError(err.response.status, url);
|
|
8575
8590
|
}
|
|
8576
8591
|
}
|
|
@@ -8911,7 +8926,8 @@ async function probeStdioStateless(options) {
|
|
|
8911
8926
|
const proc = await options.platform.spawn({
|
|
8912
8927
|
argv: [options.command, ...options.args ?? []],
|
|
8913
8928
|
env: inheritedEnv,
|
|
8914
|
-
cwd: options.cwd
|
|
8929
|
+
cwd: options.cwd,
|
|
8930
|
+
dieWithParent: true
|
|
8915
8931
|
});
|
|
8916
8932
|
trackStdio(proc);
|
|
8917
8933
|
const rpc = createStdioJsonRpc(proc, options.platform, {
|
|
@@ -11496,15 +11512,15 @@ function readMcpCommand(value) {
|
|
|
11496
11512
|
}
|
|
11497
11513
|
function readMcpPositionals(value) {
|
|
11498
11514
|
if (!Array.isArray(value)) return [];
|
|
11499
|
-
const
|
|
11515
|
+
const positionals2 = [];
|
|
11500
11516
|
for (const entry of value) {
|
|
11501
11517
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
|
|
11502
11518
|
const name = ownString(entry, "name");
|
|
11503
11519
|
if (!name) continue;
|
|
11504
11520
|
const required = Object.getOwnPropertyDescriptor(entry, "required")?.value;
|
|
11505
|
-
|
|
11521
|
+
positionals2.push({ name, required: required !== false, description: "" });
|
|
11506
11522
|
}
|
|
11507
|
-
return
|
|
11523
|
+
return positionals2;
|
|
11508
11524
|
}
|
|
11509
11525
|
function readMcpFlags(value) {
|
|
11510
11526
|
if (!Array.isArray(value)) return [];
|
|
@@ -12529,7 +12545,8 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
|
|
|
12529
12545
|
proc = await platform2.spawn({
|
|
12530
12546
|
argv: [cmdPath, ...argv],
|
|
12531
12547
|
env: childEnv,
|
|
12532
|
-
cwd
|
|
12548
|
+
cwd,
|
|
12549
|
+
dieWithParent: true
|
|
12533
12550
|
});
|
|
12534
12551
|
} catch (err) {
|
|
12535
12552
|
return { stdout: "", stderr: `spawn failed: ${err.message}`, exitCode: 127 };
|
|
@@ -13844,16 +13861,25 @@ async function signIn(options) {
|
|
|
13844
13861
|
|
|
13845
13862
|
// src/cli/auth-verbs.ts
|
|
13846
13863
|
var resolvedSource;
|
|
13847
|
-
async function createTokenSource(platform2) {
|
|
13864
|
+
async function createTokenSource(platform2, timeoutMs = JERK_TIMEOUT_MS) {
|
|
13848
13865
|
if (isKernel(platform2)) return new PiFileTokenSource(platform2);
|
|
13849
13866
|
if (resolvedSource) return resolvedSource;
|
|
13850
13867
|
if (await jerkAvailable(platform2)) {
|
|
13851
|
-
resolvedSource = new JerkTokenSource(platform2);
|
|
13868
|
+
resolvedSource = new JerkTokenSource(platform2, timeoutMs);
|
|
13852
13869
|
} else {
|
|
13853
13870
|
resolvedSource = new PiFileTokenSource(platform2);
|
|
13854
13871
|
}
|
|
13855
13872
|
return resolvedSource;
|
|
13856
13873
|
}
|
|
13874
|
+
var VALUE_FLAGS = /* @__PURE__ */ new Set(["--scope", "-m", "--timeout"]);
|
|
13875
|
+
function positionals(args2) {
|
|
13876
|
+
const out = [];
|
|
13877
|
+
for (let i = 0; i < args2.length; i++) {
|
|
13878
|
+
if (VALUE_FLAGS.has(args2[i])) i++;
|
|
13879
|
+
else if (!args2[i].startsWith("-")) out.push(args2[i]);
|
|
13880
|
+
}
|
|
13881
|
+
return out;
|
|
13882
|
+
}
|
|
13857
13883
|
function resolveServerName(target) {
|
|
13858
13884
|
try {
|
|
13859
13885
|
const u = new URL(target.startsWith("http") ? target : `https://${target}`);
|
|
@@ -13934,18 +13960,10 @@ async function jerkLogin(platform2, target, args2) {
|
|
|
13934
13960
|
if (result.status === JERK_EXIT_SIGN_IN) return 4;
|
|
13935
13961
|
return result.status;
|
|
13936
13962
|
}
|
|
13937
|
-
async function jerkLogout(platform2, target) {
|
|
13963
|
+
async function jerkLogout(platform2, target, source) {
|
|
13938
13964
|
const resolved = await resolveLoginTarget(platform2, target);
|
|
13939
13965
|
const id = jerkMcpId(resolved.name);
|
|
13940
|
-
const
|
|
13941
|
-
const proc = await platform2.spawn({ argv });
|
|
13942
|
-
proc.stdin.close().catch(() => {
|
|
13943
|
-
});
|
|
13944
|
-
const [, , result] = await Promise.all([
|
|
13945
|
-
readStream2(proc.stdout),
|
|
13946
|
-
readStream2(proc.stderr),
|
|
13947
|
-
proc.wait()
|
|
13948
|
-
]);
|
|
13966
|
+
const result = await runJerk(platform2, ["jerk", id, "drop", "--yes"], source.timeoutMs);
|
|
13949
13967
|
if (result.status === 0) {
|
|
13950
13968
|
await platform2.write(1, `Signed out of ${target}
|
|
13951
13969
|
`);
|
|
@@ -13963,7 +13981,7 @@ async function runLogin(platform2, args2, source) {
|
|
|
13963
13981
|
);
|
|
13964
13982
|
return 4;
|
|
13965
13983
|
}
|
|
13966
|
-
const positional = args2
|
|
13984
|
+
const positional = positionals(args2);
|
|
13967
13985
|
if (positional.length === 0) {
|
|
13968
13986
|
await platform2.write(2, "mcpcat: login requires a target\n");
|
|
13969
13987
|
return 2;
|
|
@@ -14054,7 +14072,7 @@ async function runLogout(platform2, args2, source) {
|
|
|
14054
14072
|
await platform2.write(2, "mcpcat: logout is not available in seven.\n");
|
|
14055
14073
|
return 4;
|
|
14056
14074
|
}
|
|
14057
|
-
const positional = args2
|
|
14075
|
+
const positional = positionals(args2);
|
|
14058
14076
|
if (positional.length === 0) {
|
|
14059
14077
|
await platform2.write(2, "mcpcat: logout requires a target\n");
|
|
14060
14078
|
return 2;
|
|
@@ -14062,8 +14080,13 @@ async function runLogout(platform2, args2, source) {
|
|
|
14062
14080
|
const target = positional[0];
|
|
14063
14081
|
if (source instanceof JerkTokenSource) {
|
|
14064
14082
|
try {
|
|
14065
|
-
return await jerkLogout(platform2, target);
|
|
14083
|
+
return await jerkLogout(platform2, target, source);
|
|
14066
14084
|
} catch (err) {
|
|
14085
|
+
if (err instanceof JerkTimeoutError) {
|
|
14086
|
+
await platform2.write(2, `mcpcat: ${err.message}
|
|
14087
|
+
`);
|
|
14088
|
+
return 4;
|
|
14089
|
+
}
|
|
14067
14090
|
await platform2.write(2, `mcpcat: logout failed: ${err.message}
|
|
14068
14091
|
`);
|
|
14069
14092
|
return 1;
|
|
@@ -15222,6 +15245,10 @@ var VERBS_WITH_TARGET = /* @__PURE__ */ new Set([
|
|
|
15222
15245
|
"prompts",
|
|
15223
15246
|
"prompt"
|
|
15224
15247
|
]);
|
|
15248
|
+
function withoutVerb(args2, verb) {
|
|
15249
|
+
const at = args2.indexOf(verb);
|
|
15250
|
+
return [...args2.slice(0, at), ...args2.slice(at + 1)];
|
|
15251
|
+
}
|
|
15225
15252
|
function resolveMaxPositional(positional) {
|
|
15226
15253
|
if (positional.length === 0) return 3;
|
|
15227
15254
|
const first = positional[0];
|
|
@@ -16049,12 +16076,12 @@ async function runInner(platform2, version, args2) {
|
|
|
16049
16076
|
const discResult = await dispatchDiscoveryVerb(platform2, parsed, args2);
|
|
16050
16077
|
if (discResult !== void 0) return discResult;
|
|
16051
16078
|
if (parsed.verb === "login") {
|
|
16052
|
-
const source = await createTokenSource(platform2);
|
|
16053
|
-
return runLogin(platform2, args2
|
|
16079
|
+
const source = await createTokenSource(platform2, parsed.timeoutMs);
|
|
16080
|
+
return runLogin(platform2, withoutVerb(args2, "login"), source);
|
|
16054
16081
|
}
|
|
16055
16082
|
if (parsed.verb === "logout") {
|
|
16056
|
-
const source = await createTokenSource(platform2);
|
|
16057
|
-
return runLogout(platform2, args2
|
|
16083
|
+
const source = await createTokenSource(platform2, parsed.timeoutMs);
|
|
16084
|
+
return runLogout(platform2, withoutVerb(args2, "logout"), source);
|
|
16058
16085
|
}
|
|
16059
16086
|
if (UNIMPLEMENTED_VERBS.has(parsed.verb)) {
|
|
16060
16087
|
await platform2.write(2, `mcpcat: '${parsed.verb}' is not implemented yet
|
|
@@ -16121,7 +16148,7 @@ function classifyError(err) {
|
|
|
16121
16148
|
if (err instanceof McpAuthError) {
|
|
16122
16149
|
return { code: EXIT_AUTH, kind: "auth", msg: err.message };
|
|
16123
16150
|
}
|
|
16124
|
-
if (err instanceof JerkSignInRequiredError) {
|
|
16151
|
+
if (err instanceof JerkSignInRequiredError || err instanceof JerkTimeoutError) {
|
|
16125
16152
|
return { code: EXIT_AUTH, kind: "auth", msg: err.message };
|
|
16126
16153
|
}
|
|
16127
16154
|
if (err instanceof AuthStepUpError) {
|
|
@@ -16145,9 +16172,9 @@ function classifyError(err) {
|
|
|
16145
16172
|
}
|
|
16146
16173
|
return { code: EXIT_RPC_ERROR, kind: "rpc_error", msg };
|
|
16147
16174
|
}
|
|
16148
|
-
async function resolveAuthProvider(platform2, hasAuthHeader, url, target, config, onCredentialObtained) {
|
|
16175
|
+
async function resolveAuthProvider(platform2, hasAuthHeader, url, target, config, timeoutMs, onCredentialObtained) {
|
|
16149
16176
|
if (hasAuthHeader || !url) return void 0;
|
|
16150
|
-
const source = await createTokenSource(platform2);
|
|
16177
|
+
const source = await createTokenSource(platform2, timeoutMs);
|
|
16151
16178
|
const entry = config.entry(target);
|
|
16152
16179
|
const oauth = entry && "url" in entry.config ? entry.config.oauth : void 0;
|
|
16153
16180
|
const name = entry ? target : `url:${new URL(url).origin}${new URL(url).pathname}`;
|
|
@@ -16179,7 +16206,7 @@ function classifyConnectionError(err, target, interrupted) {
|
|
|
16179
16206
|
msg: `${err.reason} (${hint})`
|
|
16180
16207
|
};
|
|
16181
16208
|
}
|
|
16182
|
-
if (err instanceof McpAuthError || err instanceof AuthStepUpError || err instanceof JerkSignInRequiredError) {
|
|
16209
|
+
if (err instanceof McpAuthError || err instanceof AuthStepUpError || err instanceof JerkSignInRequiredError || err instanceof JerkTimeoutError) {
|
|
16183
16210
|
return classifyError(err);
|
|
16184
16211
|
}
|
|
16185
16212
|
if (err.name === "McpOAuthAuthorizationRequiredError" || err.name === "McpAuthRequiredError") {
|
|
@@ -16327,6 +16354,7 @@ async function runClientVerb(platform2, version, parsed) {
|
|
|
16327
16354
|
resolved.url,
|
|
16328
16355
|
target,
|
|
16329
16356
|
config,
|
|
16357
|
+
timeoutMs,
|
|
16330
16358
|
(secret) => {
|
|
16331
16359
|
allSecrets.add(secret);
|
|
16332
16360
|
verbose.secrets.add(secret);
|
|
@@ -16463,10 +16491,11 @@ var USE_PROCESS_GROUPS2 = process.platform !== "win32";
|
|
|
16463
16491
|
function nodeSpawn(options) {
|
|
16464
16492
|
return new Promise((resolve, reject) => {
|
|
16465
16493
|
let rejected = false;
|
|
16494
|
+
const ignoreStdin = options.stdin === "ignore";
|
|
16466
16495
|
const child = cpSpawn(options.argv[0], options.argv.slice(1), {
|
|
16467
16496
|
env: options.env ?? process.env,
|
|
16468
16497
|
cwd: options.cwd,
|
|
16469
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
16498
|
+
stdio: [ignoreStdin ? "ignore" : "pipe", "pipe", "pipe"],
|
|
16470
16499
|
detached: USE_PROCESS_GROUPS2
|
|
16471
16500
|
});
|
|
16472
16501
|
child.on("error", (err) => {
|
|
@@ -16477,7 +16506,7 @@ function nodeSpawn(options) {
|
|
|
16477
16506
|
});
|
|
16478
16507
|
const stdout = Readable.toWeb(child.stdout);
|
|
16479
16508
|
const stderr = Readable.toWeb(child.stderr);
|
|
16480
|
-
const stdin = Writable.toWeb(child.stdin);
|
|
16509
|
+
const stdin = ignoreStdin ? new WritableStream() : Writable.toWeb(child.stdin);
|
|
16481
16510
|
const closePromise = new Promise((res) => {
|
|
16482
16511
|
child.on("close", (code2, signal) => {
|
|
16483
16512
|
res({
|
|
@@ -16684,5 +16713,5 @@ try {
|
|
|
16684
16713
|
});
|
|
16685
16714
|
} catch {
|
|
16686
16715
|
}
|
|
16687
|
-
var code = await run(platform, "0.15.
|
|
16716
|
+
var code = await run(platform, "0.15.3", args);
|
|
16688
16717
|
process.exit(code);
|
package/dist/kernel/mcpcat.mjs
CHANGED
|
@@ -6098,6 +6098,7 @@ var McpOAuthProvider = class {
|
|
|
6098
6098
|
// src/auth/jerk-token-source.ts
|
|
6099
6099
|
var MIN_VERSION = [0, 14, 1];
|
|
6100
6100
|
var VERSION_TIMEOUT_MS = 2e3;
|
|
6101
|
+
var JERK_TIMEOUT_MS = 15e3;
|
|
6101
6102
|
var cachedAvailable;
|
|
6102
6103
|
function parseVersion(raw) {
|
|
6103
6104
|
const match = raw.trim().match(/(\d+)\.(\d+)\.(\d+)/);
|
|
@@ -6144,23 +6145,39 @@ async function jerkAvailable(platform2) {
|
|
|
6144
6145
|
return false;
|
|
6145
6146
|
}
|
|
6146
6147
|
try {
|
|
6147
|
-
const
|
|
6148
|
-
const
|
|
6149
|
-
|
|
6150
|
-
});
|
|
6151
|
-
const work = Promise.all([readAll(proc.stdout), proc.wait()]).then(([out, result]) => {
|
|
6152
|
-
proc.stderr.cancel().catch(() => {
|
|
6153
|
-
});
|
|
6154
|
-
if (result.status !== 0) return false;
|
|
6155
|
-
const ver = parseVersion(out);
|
|
6156
|
-
return ver !== void 0 && versionSatisfied(ver);
|
|
6157
|
-
});
|
|
6158
|
-
cachedAvailable = await Promise.race([work, timeout]);
|
|
6148
|
+
const result = await runJerk(platform2, ["jerk", "--version"], VERSION_TIMEOUT_MS);
|
|
6149
|
+
const ver = result.status === 0 ? parseVersion(result.stdout) : void 0;
|
|
6150
|
+
cachedAvailable = ver !== void 0 && versionSatisfied(ver);
|
|
6159
6151
|
} catch {
|
|
6160
6152
|
cachedAvailable = false;
|
|
6161
6153
|
}
|
|
6162
6154
|
return cachedAvailable;
|
|
6163
6155
|
}
|
|
6156
|
+
var JerkTimeoutError = class extends Error {
|
|
6157
|
+
command;
|
|
6158
|
+
constructor(argv) {
|
|
6159
|
+
const command = argv.slice(0, 3).join(" ");
|
|
6160
|
+
super(`jerk did not answer; run \`${command}\` yourself`);
|
|
6161
|
+
this.name = "JerkTimeoutError";
|
|
6162
|
+
this.command = command;
|
|
6163
|
+
}
|
|
6164
|
+
};
|
|
6165
|
+
async function runJerk(platform2, argv, timeoutMs) {
|
|
6166
|
+
const proc = await platform2.spawn({ argv, stdin: "ignore" });
|
|
6167
|
+
let timer;
|
|
6168
|
+
const expired = new Promise((resolve2) => {
|
|
6169
|
+
timer = setTimeout(() => resolve2("timeout"), timeoutMs);
|
|
6170
|
+
});
|
|
6171
|
+
const work = Promise.all([readAll(proc.stdout), readAll(proc.stderr), proc.wait()]);
|
|
6172
|
+
const outcome = await Promise.race([work, expired]);
|
|
6173
|
+
clearTimeout(timer);
|
|
6174
|
+
if (outcome === "timeout") {
|
|
6175
|
+
await proc.kill("SIGKILL");
|
|
6176
|
+
throw new JerkTimeoutError(argv);
|
|
6177
|
+
}
|
|
6178
|
+
const [stdout, stderr, result] = outcome;
|
|
6179
|
+
return { status: result.status, stdout, stderr };
|
|
6180
|
+
}
|
|
6164
6181
|
function jerkId(name) {
|
|
6165
6182
|
if (name.startsWith("mcp:")) return name;
|
|
6166
6183
|
if (name.startsWith("url:")) return `mcp:${name}`;
|
|
@@ -6181,8 +6198,10 @@ var JerkSignInRequiredError = class extends Error {
|
|
|
6181
6198
|
};
|
|
6182
6199
|
var JerkTokenSource = class {
|
|
6183
6200
|
platform;
|
|
6184
|
-
|
|
6201
|
+
timeoutMs;
|
|
6202
|
+
constructor(platform2, timeoutMs = JERK_TIMEOUT_MS) {
|
|
6185
6203
|
this.platform = platform2;
|
|
6204
|
+
this.timeoutMs = timeoutMs;
|
|
6186
6205
|
}
|
|
6187
6206
|
async getToken(name, serverUrl, scope) {
|
|
6188
6207
|
const id = jerkId(name);
|
|
@@ -6190,12 +6209,7 @@ var JerkTokenSource = class {
|
|
|
6190
6209
|
if (scope) {
|
|
6191
6210
|
argv.push("--scope", scope);
|
|
6192
6211
|
}
|
|
6193
|
-
const
|
|
6194
|
-
const [outRaw, , result] = await Promise.all([
|
|
6195
|
-
readAll(proc.stdout),
|
|
6196
|
-
readAll(proc.stderr),
|
|
6197
|
-
proc.wait()
|
|
6198
|
-
]);
|
|
6212
|
+
const result = await runJerk(this.platform, argv, this.timeoutMs);
|
|
6199
6213
|
if (result.status === JERK_EXIT_SIGN_IN) {
|
|
6200
6214
|
throw new JerkSignInRequiredError(id, scope);
|
|
6201
6215
|
}
|
|
@@ -6205,7 +6219,7 @@ var JerkTokenSource = class {
|
|
|
6205
6219
|
if (result.status !== 0) {
|
|
6206
6220
|
return void 0;
|
|
6207
6221
|
}
|
|
6208
|
-
const parsed = JSON.parse(
|
|
6222
|
+
const parsed = JSON.parse(result.stdout.trim());
|
|
6209
6223
|
if (!parsed.token) return void 0;
|
|
6210
6224
|
return {
|
|
6211
6225
|
accessToken: parsed.token,
|
|
@@ -7798,7 +7812,8 @@ var PlatformStdioTransport = class {
|
|
|
7798
7812
|
this.process = await this.options.platform.spawn({
|
|
7799
7813
|
argv: [this.options.command, ...this.options.args ?? []],
|
|
7800
7814
|
env: env2,
|
|
7801
|
-
cwd: this.options.cwd
|
|
7815
|
+
cwd: this.options.cwd,
|
|
7816
|
+
dieWithParent: true
|
|
7802
7817
|
});
|
|
7803
7818
|
trackStdio(this.process);
|
|
7804
7819
|
}
|
|
@@ -8191,7 +8206,7 @@ async function createStatelessConnection(options) {
|
|
|
8191
8206
|
token
|
|
8192
8207
|
});
|
|
8193
8208
|
} catch (authErr) {
|
|
8194
|
-
if (authErr instanceof AuthStepUpError) throw authErr;
|
|
8209
|
+
if (authErr instanceof AuthStepUpError || authErr instanceof JerkTimeoutError) throw authErr;
|
|
8195
8210
|
throw new McpAuthError(err.response.status, url);
|
|
8196
8211
|
}
|
|
8197
8212
|
}
|
|
@@ -8532,7 +8547,8 @@ async function probeStdioStateless(options) {
|
|
|
8532
8547
|
const proc = await options.platform.spawn({
|
|
8533
8548
|
argv: [options.command, ...options.args ?? []],
|
|
8534
8549
|
env: inheritedEnv,
|
|
8535
|
-
cwd: options.cwd
|
|
8550
|
+
cwd: options.cwd,
|
|
8551
|
+
dieWithParent: true
|
|
8536
8552
|
});
|
|
8537
8553
|
trackStdio(proc);
|
|
8538
8554
|
const rpc = createStdioJsonRpc(proc, options.platform, {
|
|
@@ -11117,15 +11133,15 @@ function readMcpCommand(value) {
|
|
|
11117
11133
|
}
|
|
11118
11134
|
function readMcpPositionals(value) {
|
|
11119
11135
|
if (!Array.isArray(value)) return [];
|
|
11120
|
-
const
|
|
11136
|
+
const positionals2 = [];
|
|
11121
11137
|
for (const entry of value) {
|
|
11122
11138
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
|
|
11123
11139
|
const name = ownString(entry, "name");
|
|
11124
11140
|
if (!name) continue;
|
|
11125
11141
|
const required = Object.getOwnPropertyDescriptor(entry, "required")?.value;
|
|
11126
|
-
|
|
11142
|
+
positionals2.push({ name, required: required !== false, description: "" });
|
|
11127
11143
|
}
|
|
11128
|
-
return
|
|
11144
|
+
return positionals2;
|
|
11129
11145
|
}
|
|
11130
11146
|
function readMcpFlags(value) {
|
|
11131
11147
|
if (!Array.isArray(value)) return [];
|
|
@@ -12150,7 +12166,8 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
|
|
|
12150
12166
|
proc = await platform2.spawn({
|
|
12151
12167
|
argv: [cmdPath, ...argv],
|
|
12152
12168
|
env: childEnv,
|
|
12153
|
-
cwd
|
|
12169
|
+
cwd,
|
|
12170
|
+
dieWithParent: true
|
|
12154
12171
|
});
|
|
12155
12172
|
} catch (err) {
|
|
12156
12173
|
return { stdout: "", stderr: `spawn failed: ${err.message}`, exitCode: 127 };
|
|
@@ -13465,16 +13482,25 @@ async function signIn(options) {
|
|
|
13465
13482
|
|
|
13466
13483
|
// src/cli/auth-verbs.ts
|
|
13467
13484
|
var resolvedSource;
|
|
13468
|
-
async function createTokenSource(platform2) {
|
|
13485
|
+
async function createTokenSource(platform2, timeoutMs = JERK_TIMEOUT_MS) {
|
|
13469
13486
|
if (isKernel(platform2)) return new PiFileTokenSource(platform2);
|
|
13470
13487
|
if (resolvedSource) return resolvedSource;
|
|
13471
13488
|
if (await jerkAvailable(platform2)) {
|
|
13472
|
-
resolvedSource = new JerkTokenSource(platform2);
|
|
13489
|
+
resolvedSource = new JerkTokenSource(platform2, timeoutMs);
|
|
13473
13490
|
} else {
|
|
13474
13491
|
resolvedSource = new PiFileTokenSource(platform2);
|
|
13475
13492
|
}
|
|
13476
13493
|
return resolvedSource;
|
|
13477
13494
|
}
|
|
13495
|
+
var VALUE_FLAGS = /* @__PURE__ */ new Set(["--scope", "-m", "--timeout"]);
|
|
13496
|
+
function positionals(args) {
|
|
13497
|
+
const out = [];
|
|
13498
|
+
for (let i = 0; i < args.length; i++) {
|
|
13499
|
+
if (VALUE_FLAGS.has(args[i])) i++;
|
|
13500
|
+
else if (!args[i].startsWith("-")) out.push(args[i]);
|
|
13501
|
+
}
|
|
13502
|
+
return out;
|
|
13503
|
+
}
|
|
13478
13504
|
function resolveServerName(target) {
|
|
13479
13505
|
try {
|
|
13480
13506
|
const u = new URL(target.startsWith("http") ? target : `https://${target}`);
|
|
@@ -13555,18 +13581,10 @@ async function jerkLogin(platform2, target, args) {
|
|
|
13555
13581
|
if (result.status === JERK_EXIT_SIGN_IN) return 4;
|
|
13556
13582
|
return result.status;
|
|
13557
13583
|
}
|
|
13558
|
-
async function jerkLogout(platform2, target) {
|
|
13584
|
+
async function jerkLogout(platform2, target, source) {
|
|
13559
13585
|
const resolved = await resolveLoginTarget(platform2, target);
|
|
13560
13586
|
const id = jerkMcpId(resolved.name);
|
|
13561
|
-
const
|
|
13562
|
-
const proc = await platform2.spawn({ argv });
|
|
13563
|
-
proc.stdin.close().catch(() => {
|
|
13564
|
-
});
|
|
13565
|
-
const [, , result] = await Promise.all([
|
|
13566
|
-
readStream2(proc.stdout),
|
|
13567
|
-
readStream2(proc.stderr),
|
|
13568
|
-
proc.wait()
|
|
13569
|
-
]);
|
|
13587
|
+
const result = await runJerk(platform2, ["jerk", id, "drop", "--yes"], source.timeoutMs);
|
|
13570
13588
|
if (result.status === 0) {
|
|
13571
13589
|
await platform2.write(1, `Signed out of ${target}
|
|
13572
13590
|
`);
|
|
@@ -13584,7 +13602,7 @@ async function runLogin(platform2, args, source) {
|
|
|
13584
13602
|
);
|
|
13585
13603
|
return 4;
|
|
13586
13604
|
}
|
|
13587
|
-
const positional = args
|
|
13605
|
+
const positional = positionals(args);
|
|
13588
13606
|
if (positional.length === 0) {
|
|
13589
13607
|
await platform2.write(2, "mcpcat: login requires a target\n");
|
|
13590
13608
|
return 2;
|
|
@@ -13675,7 +13693,7 @@ async function runLogout(platform2, args, source) {
|
|
|
13675
13693
|
await platform2.write(2, "mcpcat: logout is not available in seven.\n");
|
|
13676
13694
|
return 4;
|
|
13677
13695
|
}
|
|
13678
|
-
const positional = args
|
|
13696
|
+
const positional = positionals(args);
|
|
13679
13697
|
if (positional.length === 0) {
|
|
13680
13698
|
await platform2.write(2, "mcpcat: logout requires a target\n");
|
|
13681
13699
|
return 2;
|
|
@@ -13683,8 +13701,13 @@ async function runLogout(platform2, args, source) {
|
|
|
13683
13701
|
const target = positional[0];
|
|
13684
13702
|
if (source instanceof JerkTokenSource) {
|
|
13685
13703
|
try {
|
|
13686
|
-
return await jerkLogout(platform2, target);
|
|
13704
|
+
return await jerkLogout(platform2, target, source);
|
|
13687
13705
|
} catch (err) {
|
|
13706
|
+
if (err instanceof JerkTimeoutError) {
|
|
13707
|
+
await platform2.write(2, `mcpcat: ${err.message}
|
|
13708
|
+
`);
|
|
13709
|
+
return 4;
|
|
13710
|
+
}
|
|
13688
13711
|
await platform2.write(2, `mcpcat: logout failed: ${err.message}
|
|
13689
13712
|
`);
|
|
13690
13713
|
return 1;
|
|
@@ -14843,6 +14866,10 @@ var VERBS_WITH_TARGET = /* @__PURE__ */ new Set([
|
|
|
14843
14866
|
"prompts",
|
|
14844
14867
|
"prompt"
|
|
14845
14868
|
]);
|
|
14869
|
+
function withoutVerb(args, verb) {
|
|
14870
|
+
const at = args.indexOf(verb);
|
|
14871
|
+
return [...args.slice(0, at), ...args.slice(at + 1)];
|
|
14872
|
+
}
|
|
14846
14873
|
function resolveMaxPositional(positional) {
|
|
14847
14874
|
if (positional.length === 0) return 3;
|
|
14848
14875
|
const first = positional[0];
|
|
@@ -15670,12 +15697,12 @@ async function runInner(platform2, version, args) {
|
|
|
15670
15697
|
const discResult = await dispatchDiscoveryVerb(platform2, parsed, args);
|
|
15671
15698
|
if (discResult !== void 0) return discResult;
|
|
15672
15699
|
if (parsed.verb === "login") {
|
|
15673
|
-
const source = await createTokenSource(platform2);
|
|
15674
|
-
return runLogin(platform2, args
|
|
15700
|
+
const source = await createTokenSource(platform2, parsed.timeoutMs);
|
|
15701
|
+
return runLogin(platform2, withoutVerb(args, "login"), source);
|
|
15675
15702
|
}
|
|
15676
15703
|
if (parsed.verb === "logout") {
|
|
15677
|
-
const source = await createTokenSource(platform2);
|
|
15678
|
-
return runLogout(platform2, args
|
|
15704
|
+
const source = await createTokenSource(platform2, parsed.timeoutMs);
|
|
15705
|
+
return runLogout(platform2, withoutVerb(args, "logout"), source);
|
|
15679
15706
|
}
|
|
15680
15707
|
if (UNIMPLEMENTED_VERBS.has(parsed.verb)) {
|
|
15681
15708
|
await platform2.write(2, `mcpcat: '${parsed.verb}' is not implemented yet
|
|
@@ -15742,7 +15769,7 @@ function classifyError(err) {
|
|
|
15742
15769
|
if (err instanceof McpAuthError) {
|
|
15743
15770
|
return { code: EXIT_AUTH, kind: "auth", msg: err.message };
|
|
15744
15771
|
}
|
|
15745
|
-
if (err instanceof JerkSignInRequiredError) {
|
|
15772
|
+
if (err instanceof JerkSignInRequiredError || err instanceof JerkTimeoutError) {
|
|
15746
15773
|
return { code: EXIT_AUTH, kind: "auth", msg: err.message };
|
|
15747
15774
|
}
|
|
15748
15775
|
if (err instanceof AuthStepUpError) {
|
|
@@ -15766,9 +15793,9 @@ function classifyError(err) {
|
|
|
15766
15793
|
}
|
|
15767
15794
|
return { code: EXIT_RPC_ERROR, kind: "rpc_error", msg };
|
|
15768
15795
|
}
|
|
15769
|
-
async function resolveAuthProvider(platform2, hasAuthHeader, url, target, config, onCredentialObtained) {
|
|
15796
|
+
async function resolveAuthProvider(platform2, hasAuthHeader, url, target, config, timeoutMs, onCredentialObtained) {
|
|
15770
15797
|
if (hasAuthHeader || !url) return void 0;
|
|
15771
|
-
const source = await createTokenSource(platform2);
|
|
15798
|
+
const source = await createTokenSource(platform2, timeoutMs);
|
|
15772
15799
|
const entry = config.entry(target);
|
|
15773
15800
|
const oauth = entry && "url" in entry.config ? entry.config.oauth : void 0;
|
|
15774
15801
|
const name = entry ? target : `url:${new URL(url).origin}${new URL(url).pathname}`;
|
|
@@ -15800,7 +15827,7 @@ function classifyConnectionError(err, target, interrupted) {
|
|
|
15800
15827
|
msg: `${err.reason} (${hint})`
|
|
15801
15828
|
};
|
|
15802
15829
|
}
|
|
15803
|
-
if (err instanceof McpAuthError || err instanceof AuthStepUpError || err instanceof JerkSignInRequiredError) {
|
|
15830
|
+
if (err instanceof McpAuthError || err instanceof AuthStepUpError || err instanceof JerkSignInRequiredError || err instanceof JerkTimeoutError) {
|
|
15804
15831
|
return classifyError(err);
|
|
15805
15832
|
}
|
|
15806
15833
|
if (err.name === "McpOAuthAuthorizationRequiredError" || err.name === "McpAuthRequiredError") {
|
|
@@ -15948,6 +15975,7 @@ async function runClientVerb(platform2, version, parsed) {
|
|
|
15948
15975
|
resolved.url,
|
|
15949
15976
|
target,
|
|
15950
15977
|
config,
|
|
15978
|
+
timeoutMs,
|
|
15951
15979
|
(secret) => {
|
|
15952
15980
|
allSecrets.add(secret);
|
|
15953
15981
|
verbose.secrets.add(secret);
|
|
@@ -16094,17 +16122,35 @@ function kernelFs(ctx) {
|
|
|
16094
16122
|
};
|
|
16095
16123
|
}
|
|
16096
16124
|
var groupSpawnSupported;
|
|
16125
|
+
var deathSignalSupported;
|
|
16126
|
+
var PARENT_DEATH_SIGNAL = "SIGKILL";
|
|
16127
|
+
function isInvalid(err) {
|
|
16128
|
+
return err?.code === "EINVAL";
|
|
16129
|
+
}
|
|
16097
16130
|
async function kernelSpawn(ctx, options) {
|
|
16098
16131
|
const tryGroup = groupSpawnSupported !== false;
|
|
16099
|
-
const
|
|
16132
|
+
const base = {
|
|
16100
16133
|
argv: options.argv,
|
|
16101
16134
|
env: options.env,
|
|
16102
16135
|
cwd: options.cwd,
|
|
16103
|
-
stdin: "pipe",
|
|
16136
|
+
stdin: options.stdin === "ignore" ? "null" : "pipe",
|
|
16104
16137
|
stdout: "pipe",
|
|
16105
16138
|
stderr: "pipe",
|
|
16106
16139
|
...tryGroup ? { pgid: 0 } : {}
|
|
16107
|
-
}
|
|
16140
|
+
};
|
|
16141
|
+
const tryDeath = options.dieWithParent === true && deathSignalSupported !== false;
|
|
16142
|
+
let result;
|
|
16143
|
+
if (tryDeath) {
|
|
16144
|
+
try {
|
|
16145
|
+
result = await ctx.spawn({ ...base, parentDeathSignal: PARENT_DEATH_SIGNAL });
|
|
16146
|
+
} catch (err) {
|
|
16147
|
+
if (!isInvalid(err)) throw err;
|
|
16148
|
+
deathSignalSupported = false;
|
|
16149
|
+
result = await ctx.spawn(base);
|
|
16150
|
+
}
|
|
16151
|
+
} else {
|
|
16152
|
+
result = await ctx.spawn(base);
|
|
16153
|
+
}
|
|
16108
16154
|
const childPid = result.pid;
|
|
16109
16155
|
if (groupSpawnSupported === void 0) {
|
|
16110
16156
|
try {
|
|
@@ -16116,7 +16162,7 @@ async function kernelSpawn(ctx, options) {
|
|
|
16116
16162
|
}
|
|
16117
16163
|
const useGroupKill = groupSpawnSupported;
|
|
16118
16164
|
return {
|
|
16119
|
-
stdin: result.stdin,
|
|
16165
|
+
stdin: result.stdin ?? new WritableStream(),
|
|
16120
16166
|
stdout: result.stdout,
|
|
16121
16167
|
stderr: result.stderr,
|
|
16122
16168
|
wait: () => result.wait(),
|
|
@@ -16348,7 +16394,7 @@ async function main(ctx) {
|
|
|
16348
16394
|
packageDir: "/node_modules/@ai-ecoverse/mcpcat"
|
|
16349
16395
|
});
|
|
16350
16396
|
const args = ctx.argv.slice(1);
|
|
16351
|
-
return run(platform2, "0.15.
|
|
16397
|
+
return run(platform2, "0.15.3", args);
|
|
16352
16398
|
}
|
|
16353
16399
|
export {
|
|
16354
16400
|
main as default
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/mcpcat",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.3",
|
|
4
4
|
"description": "A curl for the Model Context Protocol",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"trust:dry": "npx fledgling sync --dry-run -y --skip-publish"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@ai-ecoverse/slicc-kernel": "1.
|
|
59
|
+
"@ai-ecoverse/slicc-kernel": "1.88.1",
|
|
60
60
|
"@ai-ecoverse/slicc-shared-web": "1.13.1",
|
|
61
61
|
"@biomejs/biome": "2.5.15",
|
|
62
62
|
"@earendil-works/pi-codemode": "1.2.0",
|