@ai-ecoverse/mcpcat 0.14.1 → 0.14.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/THIRD_PARTY_NOTICES +77 -1
- package/dist/host/mcpcat.mjs +581 -291
- package/dist/kernel/mcpcat.mjs +588 -297
- package/package.json +2 -2
package/dist/kernel/mcpcat.mjs
CHANGED
|
@@ -167,6 +167,101 @@ var init_node_stub = __esm({
|
|
|
167
167
|
}
|
|
168
168
|
});
|
|
169
169
|
|
|
170
|
+
// src/config/lock.ts
|
|
171
|
+
var lock_exports = {};
|
|
172
|
+
__export(lock_exports, {
|
|
173
|
+
acquireLock: () => acquireLock
|
|
174
|
+
});
|
|
175
|
+
function isEexist(err) {
|
|
176
|
+
if (err instanceof Error && "code" in err)
|
|
177
|
+
return err.code === "EEXIST";
|
|
178
|
+
if (err instanceof Error) return err.message.includes("EEXIST");
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
async function removeStaleLockDir(fs, lockPath) {
|
|
182
|
+
try {
|
|
183
|
+
const s = await fs.stat(lockPath);
|
|
184
|
+
if (Date.now() - s.mtimeMs > LOCK_STALE_MS) {
|
|
185
|
+
try {
|
|
186
|
+
await fs.rmdir(lockPath);
|
|
187
|
+
} catch {
|
|
188
|
+
}
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
} catch {
|
|
192
|
+
}
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
async function removeStaleLockFile(fs, lockPath) {
|
|
196
|
+
try {
|
|
197
|
+
const s = await fs.stat(lockPath);
|
|
198
|
+
if (Date.now() - s.mtimeMs > LOCK_STALE_MS) {
|
|
199
|
+
try {
|
|
200
|
+
await fs.unlink(lockPath);
|
|
201
|
+
} catch {
|
|
202
|
+
}
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
} catch {
|
|
206
|
+
}
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
async function acquireLock(fs, configPath) {
|
|
210
|
+
const useFile = typeof fs.createExclusive === "function";
|
|
211
|
+
const lockPath = `${configPath}.lock`;
|
|
212
|
+
const start = Date.now();
|
|
213
|
+
const parentDir = lockPath.slice(0, lockPath.lastIndexOf("/"));
|
|
214
|
+
if (parentDir) {
|
|
215
|
+
try {
|
|
216
|
+
await fs.mkdir(parentDir);
|
|
217
|
+
} catch {
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
while (true) {
|
|
221
|
+
try {
|
|
222
|
+
if (useFile) {
|
|
223
|
+
await fs.createExclusive(lockPath);
|
|
224
|
+
} else {
|
|
225
|
+
await fs.mkdirExclusive(lockPath);
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
} catch (err) {
|
|
229
|
+
if (!isEexist(err)) throw err;
|
|
230
|
+
if (useFile) {
|
|
231
|
+
await removeStaleLockFile(fs, lockPath);
|
|
232
|
+
} else {
|
|
233
|
+
await removeStaleLockDir(fs, lockPath);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (Date.now() - start > LOCK_MAX_WAIT_MS) {
|
|
237
|
+
throw new Error(`mcpcat: could not acquire lock ${lockPath} within ${LOCK_MAX_WAIT_MS}ms`);
|
|
238
|
+
}
|
|
239
|
+
await new Promise((r) => setTimeout(r, LOCK_POLL_MS));
|
|
240
|
+
}
|
|
241
|
+
let released = false;
|
|
242
|
+
return async () => {
|
|
243
|
+
if (released) return;
|
|
244
|
+
released = true;
|
|
245
|
+
try {
|
|
246
|
+
if (useFile && fs.unlink) {
|
|
247
|
+
await fs.unlink(lockPath);
|
|
248
|
+
} else {
|
|
249
|
+
await fs.rmdir(lockPath);
|
|
250
|
+
}
|
|
251
|
+
} catch {
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
var LOCK_STALE_MS, LOCK_POLL_MS, LOCK_MAX_WAIT_MS;
|
|
256
|
+
var init_lock = __esm({
|
|
257
|
+
"src/config/lock.ts"() {
|
|
258
|
+
"use strict";
|
|
259
|
+
LOCK_STALE_MS = 1e4;
|
|
260
|
+
LOCK_POLL_MS = 50;
|
|
261
|
+
LOCK_MAX_WAIT_MS = 5e3;
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
170
265
|
// node_modules/@earendil-works/pi-codemode/dist/identifier.js
|
|
171
266
|
function toCodemodeIdentifier(name) {
|
|
172
267
|
let identifier = "";
|
|
@@ -5966,7 +6061,7 @@ var McpOAuthProvider = class {
|
|
|
5966
6061
|
};
|
|
5967
6062
|
|
|
5968
6063
|
// src/auth/jerk-token-source.ts
|
|
5969
|
-
var MIN_VERSION = [0,
|
|
6064
|
+
var MIN_VERSION = [0, 14, 1];
|
|
5970
6065
|
var VERSION_TIMEOUT_MS = 2e3;
|
|
5971
6066
|
var cachedAvailable;
|
|
5972
6067
|
function parseVersion(raw) {
|
|
@@ -6092,6 +6187,192 @@ var JerkTokenSource = class {
|
|
|
6092
6187
|
}
|
|
6093
6188
|
};
|
|
6094
6189
|
|
|
6190
|
+
// src/auth/pi-store.ts
|
|
6191
|
+
init_lock();
|
|
6192
|
+
|
|
6193
|
+
// src/config/paths.ts
|
|
6194
|
+
function userConfigDir(platform2) {
|
|
6195
|
+
const piDir = platform2.env.PI_CODING_AGENT_DIR;
|
|
6196
|
+
if (piDir) return piDir;
|
|
6197
|
+
const home = platform2.env.HOME ?? platform2.env.USERPROFILE ?? "/";
|
|
6198
|
+
return `${home}/.pi/agent`;
|
|
6199
|
+
}
|
|
6200
|
+
function userConfigPath(platform2) {
|
|
6201
|
+
return `${userConfigDir(platform2)}/mcp.json`;
|
|
6202
|
+
}
|
|
6203
|
+
function projectConfigPath(platform2) {
|
|
6204
|
+
return `${platform2.cwd}/.pi/mcp.json`;
|
|
6205
|
+
}
|
|
6206
|
+
function stateDir(platform2) {
|
|
6207
|
+
if (platform2.env.XDG_STATE_HOME) return `${platform2.env.XDG_STATE_HOME}/mcpcat`;
|
|
6208
|
+
const home = platform2.env.HOME ?? platform2.env.USERPROFILE ?? "/";
|
|
6209
|
+
if (platform2.env.LOCALAPPDATA) return `${platform2.env.LOCALAPPDATA}\\mcpcat\\state`;
|
|
6210
|
+
if (platform2.env.SLICC_KERNEL) return `${home}/.local/state/mcpcat`;
|
|
6211
|
+
return `${home}/Library/Application Support/mcpcat`;
|
|
6212
|
+
}
|
|
6213
|
+
|
|
6214
|
+
// src/auth/token-source.ts
|
|
6215
|
+
function mcpNamespace(server) {
|
|
6216
|
+
return `mcp__${server.replace(/-/g, "_")}`;
|
|
6217
|
+
}
|
|
6218
|
+
function storeKey(name, serverUrl) {
|
|
6219
|
+
const ns = mcpNamespace(name);
|
|
6220
|
+
const url = String(new URL(serverUrl));
|
|
6221
|
+
return `${ns}|${url}`;
|
|
6222
|
+
}
|
|
6223
|
+
function isKernel(platform2) {
|
|
6224
|
+
return platform2.kind === "kernel";
|
|
6225
|
+
}
|
|
6226
|
+
|
|
6227
|
+
// src/auth/pi-store.ts
|
|
6228
|
+
var AUTH_FILE = "mcp-auth.json";
|
|
6229
|
+
var FILE_MODE = 384;
|
|
6230
|
+
var DIR_MODE = 448;
|
|
6231
|
+
function parseStates(raw) {
|
|
6232
|
+
if (!raw?.trim()) return {};
|
|
6233
|
+
const parsed = JSON.parse(raw);
|
|
6234
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
|
|
6235
|
+
}
|
|
6236
|
+
function serializeStates(states) {
|
|
6237
|
+
return `${JSON.stringify(states, null, 2)}
|
|
6238
|
+
`;
|
|
6239
|
+
}
|
|
6240
|
+
function legacyKey(serverUrl) {
|
|
6241
|
+
return String(new URL(serverUrl));
|
|
6242
|
+
}
|
|
6243
|
+
var PiFileTokenSource = class {
|
|
6244
|
+
platform;
|
|
6245
|
+
authPath;
|
|
6246
|
+
dirPath;
|
|
6247
|
+
constructor(platform2) {
|
|
6248
|
+
this.platform = platform2;
|
|
6249
|
+
this.dirPath = userConfigDir(platform2);
|
|
6250
|
+
this.authPath = `${this.dirPath}/${AUTH_FILE}`;
|
|
6251
|
+
}
|
|
6252
|
+
get path() {
|
|
6253
|
+
return this.authPath;
|
|
6254
|
+
}
|
|
6255
|
+
async loadWithoutLock(name, serverUrl) {
|
|
6256
|
+
const key = storeKey(name, serverUrl);
|
|
6257
|
+
const legacy = legacyKey(serverUrl);
|
|
6258
|
+
try {
|
|
6259
|
+
const raw = await this.readRaw();
|
|
6260
|
+
const states = parseStates(raw);
|
|
6261
|
+
return states[key] ?? states[legacy];
|
|
6262
|
+
} catch {
|
|
6263
|
+
return void 0;
|
|
6264
|
+
}
|
|
6265
|
+
}
|
|
6266
|
+
async getToken(name, serverUrl) {
|
|
6267
|
+
const state = await this.load(name, serverUrl);
|
|
6268
|
+
if (!state?.tokens?.access_token) return void 0;
|
|
6269
|
+
return {
|
|
6270
|
+
accessToken: state.tokens.access_token,
|
|
6271
|
+
expiresAt: state.tokensExpireAt
|
|
6272
|
+
};
|
|
6273
|
+
}
|
|
6274
|
+
async load(name, serverUrl) {
|
|
6275
|
+
const key = storeKey(name, serverUrl);
|
|
6276
|
+
const legacy = legacyKey(serverUrl);
|
|
6277
|
+
const release = await this.acquireLock();
|
|
6278
|
+
try {
|
|
6279
|
+
const raw = await this.readRaw();
|
|
6280
|
+
const states = parseStates(raw);
|
|
6281
|
+
if (states[key]) return states[key];
|
|
6282
|
+
if (states[legacy]) {
|
|
6283
|
+
states[key] = states[legacy];
|
|
6284
|
+
delete states[legacy];
|
|
6285
|
+
await this.writeStates(states);
|
|
6286
|
+
return states[key];
|
|
6287
|
+
}
|
|
6288
|
+
return void 0;
|
|
6289
|
+
} finally {
|
|
6290
|
+
await release();
|
|
6291
|
+
}
|
|
6292
|
+
}
|
|
6293
|
+
async save(name, serverUrl, state) {
|
|
6294
|
+
const key = storeKey(name, serverUrl);
|
|
6295
|
+
const release = await this.acquireLock();
|
|
6296
|
+
try {
|
|
6297
|
+
const raw = await this.readRaw();
|
|
6298
|
+
const states = parseStates(raw);
|
|
6299
|
+
states[key] = state;
|
|
6300
|
+
await this.writeStates(states);
|
|
6301
|
+
} finally {
|
|
6302
|
+
await release();
|
|
6303
|
+
}
|
|
6304
|
+
}
|
|
6305
|
+
async remove(name, serverUrl) {
|
|
6306
|
+
const key = storeKey(name, serverUrl);
|
|
6307
|
+
const legacy = legacyKey(serverUrl);
|
|
6308
|
+
const release = await this.acquireLock();
|
|
6309
|
+
try {
|
|
6310
|
+
const raw = await this.readRaw();
|
|
6311
|
+
const states = parseStates(raw);
|
|
6312
|
+
const stored = key in states ? key : legacy in states ? legacy : void 0;
|
|
6313
|
+
if (!stored) return false;
|
|
6314
|
+
delete states[stored];
|
|
6315
|
+
await this.writeStates(states);
|
|
6316
|
+
return true;
|
|
6317
|
+
} finally {
|
|
6318
|
+
await release();
|
|
6319
|
+
}
|
|
6320
|
+
}
|
|
6321
|
+
async acquireLock() {
|
|
6322
|
+
await this.ensureDir();
|
|
6323
|
+
return acquireLock(this.platform.fs, this.authPath);
|
|
6324
|
+
}
|
|
6325
|
+
async ensureDir() {
|
|
6326
|
+
const fs = this.platform.fs;
|
|
6327
|
+
try {
|
|
6328
|
+
await fs.mkdir(this.dirPath, { mode: DIR_MODE });
|
|
6329
|
+
} catch {
|
|
6330
|
+
}
|
|
6331
|
+
if (fs.chmod) {
|
|
6332
|
+
try {
|
|
6333
|
+
await fs.chmod(this.dirPath, DIR_MODE);
|
|
6334
|
+
} catch {
|
|
6335
|
+
}
|
|
6336
|
+
const piDir = this.dirPath.slice(0, this.dirPath.lastIndexOf("/"));
|
|
6337
|
+
if (piDir) {
|
|
6338
|
+
try {
|
|
6339
|
+
await fs.chmod(piDir, DIR_MODE);
|
|
6340
|
+
} catch {
|
|
6341
|
+
}
|
|
6342
|
+
}
|
|
6343
|
+
}
|
|
6344
|
+
}
|
|
6345
|
+
async readRaw() {
|
|
6346
|
+
try {
|
|
6347
|
+
const data = await this.platform.fs.readFile(this.authPath);
|
|
6348
|
+
return new TextDecoder().decode(data);
|
|
6349
|
+
} catch {
|
|
6350
|
+
return void 0;
|
|
6351
|
+
}
|
|
6352
|
+
}
|
|
6353
|
+
async writeStates(states) {
|
|
6354
|
+
const fs = this.platform.fs;
|
|
6355
|
+
const content = serializeStates(states);
|
|
6356
|
+
const tmpPath = `${this.authPath}.tmp.${Date.now()}`;
|
|
6357
|
+
try {
|
|
6358
|
+
await fs.writeFile(tmpPath, content, { mode: FILE_MODE });
|
|
6359
|
+
await fs.rename(tmpPath, this.authPath);
|
|
6360
|
+
} catch (renameErr) {
|
|
6361
|
+
try {
|
|
6362
|
+
if (fs.unlink) await fs.unlink(tmpPath);
|
|
6363
|
+
} catch {
|
|
6364
|
+
}
|
|
6365
|
+
throw renameErr;
|
|
6366
|
+
}
|
|
6367
|
+
if (fs.chmod) {
|
|
6368
|
+
try {
|
|
6369
|
+
await fs.chmod(this.authPath, FILE_MODE);
|
|
6370
|
+
} catch {
|
|
6371
|
+
}
|
|
6372
|
+
}
|
|
6373
|
+
}
|
|
6374
|
+
};
|
|
6375
|
+
|
|
6095
6376
|
// src/auth/auth-provider.ts
|
|
6096
6377
|
var CIMD_URL = "https://mcpcat.sliccy.ai/oauth/client.json";
|
|
6097
6378
|
var CLIENT_NAME = "mcpcat";
|
|
@@ -6113,10 +6394,12 @@ var SourceBackedStore = class {
|
|
|
6113
6394
|
source;
|
|
6114
6395
|
name;
|
|
6115
6396
|
serverUrl;
|
|
6116
|
-
|
|
6397
|
+
platform;
|
|
6398
|
+
constructor(source, name, serverUrl, platform2) {
|
|
6117
6399
|
this.source = source;
|
|
6118
6400
|
this.name = name;
|
|
6119
6401
|
this.serverUrl = serverUrl;
|
|
6402
|
+
this.platform = platform2;
|
|
6120
6403
|
}
|
|
6121
6404
|
async load() {
|
|
6122
6405
|
return this.source.load(this.name, this.serverUrl);
|
|
@@ -6124,6 +6407,17 @@ var SourceBackedStore = class {
|
|
|
6124
6407
|
async save(state) {
|
|
6125
6408
|
await this.source.save(this.name, this.serverUrl, state);
|
|
6126
6409
|
}
|
|
6410
|
+
async withRefreshLock(fn) {
|
|
6411
|
+
if (!(this.source instanceof PiFileTokenSource)) return fn();
|
|
6412
|
+
const refreshPath = `${this.source.path}.refresh`;
|
|
6413
|
+
const { acquireLock: acquireLock2 } = await Promise.resolve().then(() => (init_lock(), lock_exports));
|
|
6414
|
+
const release = await acquireLock2(this.platform.fs, refreshPath);
|
|
6415
|
+
try {
|
|
6416
|
+
return await fn();
|
|
6417
|
+
} finally {
|
|
6418
|
+
await release();
|
|
6419
|
+
}
|
|
6420
|
+
}
|
|
6127
6421
|
};
|
|
6128
6422
|
function cimdFn(redirectUrl) {
|
|
6129
6423
|
return (metadata) => {
|
|
@@ -6152,10 +6446,9 @@ function createMcpAuthProvider(options) {
|
|
|
6152
6446
|
function createJerkAuthProvider(options) {
|
|
6153
6447
|
const { name, serverUrl, source } = options;
|
|
6154
6448
|
let currentToken;
|
|
6449
|
+
const _fetched = false;
|
|
6155
6450
|
return {
|
|
6156
6451
|
async token() {
|
|
6157
|
-
const result = await source.getToken(name, serverUrl);
|
|
6158
|
-
currentToken = result?.accessToken;
|
|
6159
6452
|
return currentToken;
|
|
6160
6453
|
},
|
|
6161
6454
|
async onUnauthorized(context) {
|
|
@@ -6186,10 +6479,10 @@ function createJerkAuthProvider(options) {
|
|
|
6186
6479
|
}
|
|
6187
6480
|
function createPiAuthProvider(options) {
|
|
6188
6481
|
const { name, serverUrl, source, oauth, platform: platform2 } = options;
|
|
6189
|
-
const store = new SourceBackedStore(source, name, serverUrl);
|
|
6482
|
+
const store = new SourceBackedStore(source, name, serverUrl, platform2);
|
|
6190
6483
|
let refreshing;
|
|
6191
6484
|
const refresh = async (staleToken) => {
|
|
6192
|
-
refreshing ??= (async () => {
|
|
6485
|
+
refreshing ??= store.withRefreshLock(async () => {
|
|
6193
6486
|
const state = await store.load();
|
|
6194
6487
|
if (state?.tokens?.access_token !== staleToken) return;
|
|
6195
6488
|
if (!state?.tokens?.refresh_token) throw new McpOAuthAuthorizationRequiredError();
|
|
@@ -6212,7 +6505,7 @@ function createPiAuthProvider(options) {
|
|
|
6212
6505
|
fetch: timedFetch(platform2.fetch)
|
|
6213
6506
|
});
|
|
6214
6507
|
if (result === "REDIRECT") throw new McpOAuthAuthorizationRequiredError();
|
|
6215
|
-
})
|
|
6508
|
+
}).finally(() => {
|
|
6216
6509
|
refreshing = void 0;
|
|
6217
6510
|
});
|
|
6218
6511
|
return refreshing;
|
|
@@ -7339,11 +7632,45 @@ var StreamableHttpTransport = class extends TransportEvents {
|
|
|
7339
7632
|
}
|
|
7340
7633
|
};
|
|
7341
7634
|
|
|
7342
|
-
// src/client/
|
|
7343
|
-
var
|
|
7344
|
-
var
|
|
7345
|
-
|
|
7346
|
-
|
|
7635
|
+
// src/client/stdio-registry.ts
|
|
7636
|
+
var GRACE_MS = 2e3;
|
|
7637
|
+
var registry = /* @__PURE__ */ new Set();
|
|
7638
|
+
function trackStdio(proc) {
|
|
7639
|
+
registry.add(proc);
|
|
7640
|
+
void proc.wait().then(
|
|
7641
|
+
() => registry.delete(proc),
|
|
7642
|
+
() => registry.delete(proc)
|
|
7643
|
+
);
|
|
7644
|
+
}
|
|
7645
|
+
async function killAllStdio() {
|
|
7646
|
+
const procs = [...registry];
|
|
7647
|
+
if (procs.length === 0) return;
|
|
7648
|
+
await Promise.all(
|
|
7649
|
+
procs.map(async (proc) => {
|
|
7650
|
+
try {
|
|
7651
|
+
await proc.kill("SIGTERM");
|
|
7652
|
+
} catch {
|
|
7653
|
+
}
|
|
7654
|
+
const exited = await Promise.race([
|
|
7655
|
+
proc.wait().then(() => true),
|
|
7656
|
+
new Promise((r) => setTimeout(() => r(false), GRACE_MS))
|
|
7657
|
+
]);
|
|
7658
|
+
if (!exited) {
|
|
7659
|
+
try {
|
|
7660
|
+
await proc.kill("SIGKILL");
|
|
7661
|
+
} catch {
|
|
7662
|
+
}
|
|
7663
|
+
}
|
|
7664
|
+
})
|
|
7665
|
+
);
|
|
7666
|
+
registry.clear();
|
|
7667
|
+
}
|
|
7668
|
+
|
|
7669
|
+
// src/client/platform-stdio-transport.ts
|
|
7670
|
+
var DEFAULT_MAX_MESSAGE_BYTES2 = 4 * 1024 * 1024;
|
|
7671
|
+
var DEFAULT_MAX_STDERR_BYTES2 = 64 * 1024;
|
|
7672
|
+
var DEFAULT_CLOSE_TIMEOUT_MS2 = 2e3;
|
|
7673
|
+
var STDIN_CLOSE_GRACE_MS2 = 500;
|
|
7347
7674
|
var PlatformStdioTransport = class {
|
|
7348
7675
|
options;
|
|
7349
7676
|
process;
|
|
@@ -7395,16 +7722,21 @@ var PlatformStdioTransport = class {
|
|
|
7395
7722
|
if (this.started) throw new Error("MCP stdio transport already started");
|
|
7396
7723
|
if (this.closed) throw new Error("MCP connection closed");
|
|
7397
7724
|
this.started = true;
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7725
|
+
if (this.options.existingProcess) {
|
|
7726
|
+
this.process = this.options.existingProcess;
|
|
7727
|
+
} else {
|
|
7728
|
+
const env2 = this.options.inheritEnv === false ? { ...this.options.env ?? {} } : {
|
|
7729
|
+
...this.options.platform.env,
|
|
7730
|
+
...this.options.env
|
|
7731
|
+
};
|
|
7732
|
+
this.process = await this.options.platform.spawn({
|
|
7733
|
+
argv: [this.options.command, ...this.options.args ?? []],
|
|
7734
|
+
env: env2,
|
|
7735
|
+
cwd: this.options.cwd
|
|
7736
|
+
});
|
|
7737
|
+
trackStdio(this.process);
|
|
7738
|
+
}
|
|
7739
|
+
void this.readStdout(this.options.pendingStdout);
|
|
7408
7740
|
void this.readStderr();
|
|
7409
7741
|
void this.waitForExit();
|
|
7410
7742
|
}
|
|
@@ -7437,22 +7769,22 @@ var PlatformStdioTransport = class {
|
|
|
7437
7769
|
}
|
|
7438
7770
|
const exited = await Promise.race([done, delay(grace)]);
|
|
7439
7771
|
if (!exited) {
|
|
7440
|
-
proc.kill("SIGTERM");
|
|
7772
|
+
await proc.kill("SIGTERM");
|
|
7441
7773
|
const exitedAfterTerm = await Promise.race([done, delay(closeTimeoutMs)]);
|
|
7442
7774
|
if (!exitedAfterTerm) {
|
|
7443
|
-
proc.kill("SIGKILL");
|
|
7775
|
+
await proc.kill("SIGKILL");
|
|
7444
7776
|
await done;
|
|
7445
7777
|
}
|
|
7446
7778
|
}
|
|
7447
|
-
proc.kill("SIGTERM");
|
|
7779
|
+
await proc.kill("SIGTERM");
|
|
7448
7780
|
this.emitClose();
|
|
7449
7781
|
}
|
|
7450
|
-
async readStdout() {
|
|
7782
|
+
async readStdout(pending) {
|
|
7451
7783
|
const proc = this.process;
|
|
7452
7784
|
if (!proc) return;
|
|
7453
7785
|
const reader = proc.stdout.getReader();
|
|
7454
7786
|
const maxBytes = this.options.maxMessageBytes ?? DEFAULT_MAX_MESSAGE_BYTES2;
|
|
7455
|
-
let buffer = "";
|
|
7787
|
+
let buffer = pending ?? "";
|
|
7456
7788
|
try {
|
|
7457
7789
|
for (; ; ) {
|
|
7458
7790
|
const { done, value } = await reader.read();
|
|
@@ -7512,6 +7844,10 @@ var PlatformStdioTransport = class {
|
|
|
7512
7844
|
await proc.wait();
|
|
7513
7845
|
} catch {
|
|
7514
7846
|
}
|
|
7847
|
+
try {
|
|
7848
|
+
await proc.kill("SIGTERM");
|
|
7849
|
+
} catch {
|
|
7850
|
+
}
|
|
7515
7851
|
if (!this.closed) {
|
|
7516
7852
|
this.emitClose();
|
|
7517
7853
|
}
|
|
@@ -7853,7 +8189,9 @@ function buildTransport(options) {
|
|
|
7853
8189
|
cwd: options.cwd,
|
|
7854
8190
|
inheritEnv: true,
|
|
7855
8191
|
platform: options.platform,
|
|
7856
|
-
onStderr: buildStderrHandler(options)
|
|
8192
|
+
onStderr: buildStderrHandler(options),
|
|
8193
|
+
existingProcess: options.existingProcess?.proc,
|
|
8194
|
+
pendingStdout: options.existingProcess?.pendingStdout
|
|
7857
8195
|
});
|
|
7858
8196
|
}
|
|
7859
8197
|
return new StdioTransport({
|
|
@@ -8040,6 +8378,9 @@ function createStdioJsonRpc(proc, platform2, options) {
|
|
|
8040
8378
|
reader.releaseLock();
|
|
8041
8379
|
} catch {
|
|
8042
8380
|
}
|
|
8381
|
+
},
|
|
8382
|
+
pendingBuffer() {
|
|
8383
|
+
return readBuffer;
|
|
8043
8384
|
}
|
|
8044
8385
|
};
|
|
8045
8386
|
}
|
|
@@ -8092,13 +8433,13 @@ function buildConnectionMethods(rpc, discoverResult2, proc) {
|
|
|
8092
8433
|
async close() {
|
|
8093
8434
|
rpc.close();
|
|
8094
8435
|
try {
|
|
8095
|
-
proc.kill("SIGTERM");
|
|
8436
|
+
await proc.kill("SIGTERM");
|
|
8096
8437
|
} catch {
|
|
8097
8438
|
}
|
|
8098
8439
|
}
|
|
8099
8440
|
};
|
|
8100
8441
|
}
|
|
8101
|
-
async function
|
|
8442
|
+
async function probeStdioStateless(options) {
|
|
8102
8443
|
const inheritedEnv = {
|
|
8103
8444
|
...options.platform.env,
|
|
8104
8445
|
...options.env
|
|
@@ -8108,22 +8449,30 @@ async function createStdioStatelessConnection(options) {
|
|
|
8108
8449
|
env: inheritedEnv,
|
|
8109
8450
|
cwd: options.cwd
|
|
8110
8451
|
});
|
|
8111
|
-
|
|
8452
|
+
trackStdio(proc);
|
|
8112
8453
|
const rpc = createStdioJsonRpc(proc, options.platform, {
|
|
8113
8454
|
version: options.version,
|
|
8114
8455
|
timeoutMs: options.timeoutMs,
|
|
8115
8456
|
signal: options.signal,
|
|
8116
8457
|
verbose: options.verbose
|
|
8117
8458
|
});
|
|
8118
|
-
|
|
8119
|
-
|
|
8120
|
-
|
|
8459
|
+
let discoverRaw;
|
|
8460
|
+
try {
|
|
8461
|
+
discoverRaw = await rpc.request("server/discover", {
|
|
8462
|
+
protocolVersion: PROTOCOL_VERSION
|
|
8463
|
+
});
|
|
8464
|
+
} catch (err) {
|
|
8465
|
+
const pending = rpc.pendingBuffer();
|
|
8466
|
+
rpc.close();
|
|
8467
|
+
return { proc, pendingStdout: pending, error: err };
|
|
8468
|
+
}
|
|
8469
|
+
void readStderr(proc, options.verbose);
|
|
8121
8470
|
const discoverResult2 = discoverRaw;
|
|
8122
8471
|
const supported = (discoverResult2?.supportedVersions ?? []).filter(
|
|
8123
8472
|
(v) => typeof v === "string"
|
|
8124
8473
|
);
|
|
8125
8474
|
if (!supported.includes(PROTOCOL_VERSION)) {
|
|
8126
|
-
proc.kill("SIGTERM");
|
|
8475
|
+
await proc.kill("SIGTERM");
|
|
8127
8476
|
throw new Error(
|
|
8128
8477
|
`Server does not support ${PROTOCOL_VERSION} (advertised: ${supported.join(", ") || "none"})`
|
|
8129
8478
|
);
|
|
@@ -8157,25 +8506,37 @@ async function connect(platform2, options) {
|
|
|
8157
8506
|
platform: platform2
|
|
8158
8507
|
};
|
|
8159
8508
|
if (options.command) {
|
|
8509
|
+
let fallbackProcess;
|
|
8160
8510
|
if (!options.protocolVersion || options.protocolVersion.startsWith("2026")) {
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
|
|
8169
|
-
|
|
8170
|
-
|
|
8171
|
-
|
|
8172
|
-
|
|
8173
|
-
|
|
8174
|
-
|
|
8175
|
-
|
|
8176
|
-
|
|
8177
|
-
|
|
8511
|
+
const result = await probeStdioStateless({
|
|
8512
|
+
command: options.command,
|
|
8513
|
+
args: options.args,
|
|
8514
|
+
env: options.env,
|
|
8515
|
+
cwd: options.cwd,
|
|
8516
|
+
platform: platform2,
|
|
8517
|
+
version: options.version,
|
|
8518
|
+
timeoutMs: options.timeoutMs,
|
|
8519
|
+
signal: options.signal,
|
|
8520
|
+
verbose: options.verbose
|
|
8521
|
+
});
|
|
8522
|
+
if (!("proc" in result)) return result;
|
|
8523
|
+
const isMethodNotFound = result.error instanceof StatelessRpcError && result.error.code === -32601;
|
|
8524
|
+
const isUnsupported = result.error.message.includes("does not support");
|
|
8525
|
+
if (options.protocolVersion?.startsWith("2026")) {
|
|
8526
|
+
try {
|
|
8527
|
+
await result.proc.kill("SIGTERM");
|
|
8528
|
+
} catch {
|
|
8529
|
+
}
|
|
8530
|
+
throw result.error;
|
|
8531
|
+
}
|
|
8532
|
+
if (!isMethodNotFound && !isUnsupported) {
|
|
8533
|
+
try {
|
|
8534
|
+
await result.proc.kill("SIGTERM");
|
|
8535
|
+
} catch {
|
|
8536
|
+
}
|
|
8537
|
+
throw result.error;
|
|
8178
8538
|
}
|
|
8539
|
+
fallbackProcess = { proc: result.proc, pendingStdout: result.pendingStdout };
|
|
8179
8540
|
}
|
|
8180
8541
|
return createPiConnection({
|
|
8181
8542
|
command: options.command,
|
|
@@ -8183,6 +8544,7 @@ async function connect(platform2, options) {
|
|
|
8183
8544
|
env: options.env,
|
|
8184
8545
|
cwd: options.cwd,
|
|
8185
8546
|
roots: options.roots,
|
|
8547
|
+
existingProcess: fallbackProcess,
|
|
8186
8548
|
...shared
|
|
8187
8549
|
});
|
|
8188
8550
|
}
|
|
@@ -9192,27 +9554,6 @@ async function loadConfig(input) {
|
|
|
9192
9554
|
};
|
|
9193
9555
|
}
|
|
9194
9556
|
|
|
9195
|
-
// src/config/paths.ts
|
|
9196
|
-
function userConfigDir(platform2) {
|
|
9197
|
-
const piDir = platform2.env.PI_CODING_AGENT_DIR;
|
|
9198
|
-
if (piDir) return piDir;
|
|
9199
|
-
const home = platform2.env.HOME ?? platform2.env.USERPROFILE ?? "/";
|
|
9200
|
-
return `${home}/.pi/agent`;
|
|
9201
|
-
}
|
|
9202
|
-
function userConfigPath(platform2) {
|
|
9203
|
-
return `${userConfigDir(platform2)}/mcp.json`;
|
|
9204
|
-
}
|
|
9205
|
-
function projectConfigPath(platform2) {
|
|
9206
|
-
return `${platform2.cwd}/.pi/mcp.json`;
|
|
9207
|
-
}
|
|
9208
|
-
function stateDir(platform2) {
|
|
9209
|
-
if (platform2.env.XDG_STATE_HOME) return `${platform2.env.XDG_STATE_HOME}/mcpcat`;
|
|
9210
|
-
const home = platform2.env.HOME ?? platform2.env.USERPROFILE ?? "/";
|
|
9211
|
-
if (platform2.env.LOCALAPPDATA) return `${platform2.env.LOCALAPPDATA}\\mcpcat\\state`;
|
|
9212
|
-
if (platform2.env.SLICC_KERNEL) return `${home}/.local/state/mcpcat`;
|
|
9213
|
-
return `${home}/Library/Application Support/mcpcat`;
|
|
9214
|
-
}
|
|
9215
|
-
|
|
9216
9557
|
// src/config/index.ts
|
|
9217
9558
|
var MergedConfigSource = class _MergedConfigSource {
|
|
9218
9559
|
entries;
|
|
@@ -9252,65 +9593,8 @@ var MergedConfigSource = class _MergedConfigSource {
|
|
|
9252
9593
|
}
|
|
9253
9594
|
};
|
|
9254
9595
|
|
|
9255
|
-
// src/config/lock.ts
|
|
9256
|
-
var LOCK_STALE_MS = 1e4;
|
|
9257
|
-
var LOCK_POLL_MS = 50;
|
|
9258
|
-
var LOCK_MAX_WAIT_MS = 5e3;
|
|
9259
|
-
function isEexist(err) {
|
|
9260
|
-
if (err instanceof Error && "code" in err)
|
|
9261
|
-
return err.code === "EEXIST";
|
|
9262
|
-
if (err instanceof Error) return err.message.includes("EEXIST");
|
|
9263
|
-
return false;
|
|
9264
|
-
}
|
|
9265
|
-
async function removeStaleLock(fs, lockPath) {
|
|
9266
|
-
try {
|
|
9267
|
-
const s = await fs.stat(lockPath);
|
|
9268
|
-
if (Date.now() - s.mtimeMs > LOCK_STALE_MS) {
|
|
9269
|
-
try {
|
|
9270
|
-
await fs.rmdir(lockPath);
|
|
9271
|
-
} catch {
|
|
9272
|
-
}
|
|
9273
|
-
return true;
|
|
9274
|
-
}
|
|
9275
|
-
} catch {
|
|
9276
|
-
}
|
|
9277
|
-
return false;
|
|
9278
|
-
}
|
|
9279
|
-
async function acquireLock(fs, configPath) {
|
|
9280
|
-
const lockPath = `${configPath}.lock`;
|
|
9281
|
-
const start = Date.now();
|
|
9282
|
-
const parentDir = lockPath.slice(0, lockPath.lastIndexOf("/"));
|
|
9283
|
-
if (parentDir) {
|
|
9284
|
-
try {
|
|
9285
|
-
await fs.mkdir(parentDir);
|
|
9286
|
-
} catch {
|
|
9287
|
-
}
|
|
9288
|
-
}
|
|
9289
|
-
while (true) {
|
|
9290
|
-
try {
|
|
9291
|
-
await fs.mkdirExclusive(lockPath);
|
|
9292
|
-
break;
|
|
9293
|
-
} catch (err) {
|
|
9294
|
-
if (!isEexist(err)) throw err;
|
|
9295
|
-
await removeStaleLock(fs, lockPath);
|
|
9296
|
-
}
|
|
9297
|
-
if (Date.now() - start > LOCK_MAX_WAIT_MS) {
|
|
9298
|
-
throw new Error(`mcpcat: could not acquire lock ${lockPath} within ${LOCK_MAX_WAIT_MS}ms`);
|
|
9299
|
-
}
|
|
9300
|
-
await new Promise((r) => setTimeout(r, LOCK_POLL_MS));
|
|
9301
|
-
}
|
|
9302
|
-
let released = false;
|
|
9303
|
-
return async () => {
|
|
9304
|
-
if (released) return;
|
|
9305
|
-
released = true;
|
|
9306
|
-
try {
|
|
9307
|
-
await fs.rmdir(lockPath);
|
|
9308
|
-
} catch {
|
|
9309
|
-
}
|
|
9310
|
-
};
|
|
9311
|
-
}
|
|
9312
|
-
|
|
9313
9596
|
// src/config/edit.ts
|
|
9597
|
+
init_lock();
|
|
9314
9598
|
function isRecord2(value) {
|
|
9315
9599
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9316
9600
|
}
|
|
@@ -11535,7 +11819,7 @@ function capOutput(value, limit) {
|
|
|
11535
11819
|
${value.slice(value.length - half)}`;
|
|
11536
11820
|
}
|
|
11537
11821
|
var inflightProcs = /* @__PURE__ */ new Set();
|
|
11538
|
-
async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs, maxOutput, cwd) {
|
|
11822
|
+
async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs, maxOutput, cwd, signal) {
|
|
11539
11823
|
let proc;
|
|
11540
11824
|
try {
|
|
11541
11825
|
proc = await platform2.spawn({
|
|
@@ -11574,23 +11858,41 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
|
|
|
11574
11858
|
const timer = new Promise(
|
|
11575
11859
|
(resolve2) => setTimeout(() => resolve2("timeout"), timeoutMs)
|
|
11576
11860
|
);
|
|
11861
|
+
const abortPromise = signal ? new Promise((resolve2) => {
|
|
11862
|
+
if (signal.aborted) {
|
|
11863
|
+
resolve2("timeout");
|
|
11864
|
+
return;
|
|
11865
|
+
}
|
|
11866
|
+
signal.addEventListener("abort", () => resolve2("timeout"), { once: true });
|
|
11867
|
+
}) : null;
|
|
11577
11868
|
const work = Promise.all([
|
|
11578
11869
|
readStream3(proc.stdout, stdoutChunks),
|
|
11579
11870
|
readStream3(proc.stderr, stderrChunks),
|
|
11580
11871
|
proc.wait()
|
|
11581
11872
|
]);
|
|
11582
|
-
const
|
|
11873
|
+
const racers = [
|
|
11874
|
+
work,
|
|
11875
|
+
timer
|
|
11876
|
+
];
|
|
11877
|
+
if (abortPromise !== null) racers.push(abortPromise);
|
|
11878
|
+
const result = await Promise.race(racers);
|
|
11583
11879
|
if (result === "timeout") {
|
|
11584
|
-
proc.kill("SIGTERM");
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
|
|
11588
|
-
|
|
11880
|
+
await proc.kill("SIGTERM");
|
|
11881
|
+
if (!signal?.aborted) {
|
|
11882
|
+
await new Promise((resolve2) => setTimeout(resolve2, 5e3));
|
|
11883
|
+
try {
|
|
11884
|
+
await proc.kill("SIGKILL");
|
|
11885
|
+
} catch {
|
|
11886
|
+
}
|
|
11589
11887
|
}
|
|
11590
11888
|
inflightProcs.delete(proc);
|
|
11591
11889
|
return { stdout: "", stderr: "timed out", exitCode: 124 };
|
|
11592
11890
|
}
|
|
11593
11891
|
inflightProcs.delete(proc);
|
|
11892
|
+
try {
|
|
11893
|
+
await proc.kill("SIGTERM");
|
|
11894
|
+
} catch {
|
|
11895
|
+
}
|
|
11594
11896
|
const [, , exitResult] = result;
|
|
11595
11897
|
const dec = new TextDecoder();
|
|
11596
11898
|
return {
|
|
@@ -11609,25 +11911,31 @@ function concat(chunks) {
|
|
|
11609
11911
|
}
|
|
11610
11912
|
return out;
|
|
11611
11913
|
}
|
|
11914
|
+
var KILL_GRACE_MS = 2e3;
|
|
11612
11915
|
async function killInflight() {
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
}
|
|
11618
|
-
}
|
|
11619
|
-
if (inflightProcs.size > 0) {
|
|
11620
|
-
await new Promise((resolve2) => setTimeout(resolve2, 5e3));
|
|
11621
|
-
for (const proc of inflightProcs) {
|
|
11916
|
+
const procs = [...inflightProcs];
|
|
11917
|
+
if (procs.length === 0) return;
|
|
11918
|
+
await Promise.all(
|
|
11919
|
+
procs.map(async (proc) => {
|
|
11622
11920
|
try {
|
|
11623
|
-
proc.kill("
|
|
11921
|
+
await proc.kill("SIGTERM");
|
|
11624
11922
|
} catch {
|
|
11625
11923
|
}
|
|
11626
|
-
|
|
11627
|
-
|
|
11924
|
+
const exited = await Promise.race([
|
|
11925
|
+
proc.wait().then(() => true),
|
|
11926
|
+
new Promise((r) => setTimeout(() => r(false), KILL_GRACE_MS))
|
|
11927
|
+
]);
|
|
11928
|
+
if (!exited) {
|
|
11929
|
+
try {
|
|
11930
|
+
await proc.kill("SIGKILL");
|
|
11931
|
+
} catch {
|
|
11932
|
+
}
|
|
11933
|
+
}
|
|
11934
|
+
})
|
|
11935
|
+
);
|
|
11628
11936
|
inflightProcs.clear();
|
|
11629
11937
|
}
|
|
11630
|
-
async function runHelpForCli(platform2, filePath, childEnv, cwd) {
|
|
11938
|
+
async function runHelpForCli(platform2, filePath, childEnv, cwd, signal) {
|
|
11631
11939
|
const dashed = await runCommand(
|
|
11632
11940
|
platform2,
|
|
11633
11941
|
filePath,
|
|
@@ -11636,9 +11944,11 @@ async function runHelpForCli(platform2, filePath, childEnv, cwd) {
|
|
|
11636
11944
|
void 0,
|
|
11637
11945
|
1e4,
|
|
11638
11946
|
MAX_OUTPUT_DEFAULT,
|
|
11639
|
-
cwd
|
|
11947
|
+
cwd,
|
|
11948
|
+
signal
|
|
11640
11949
|
);
|
|
11641
11950
|
if (dashed.exitCode === 0 && dashed.stdout.trim()) return dashed.stdout;
|
|
11951
|
+
if (signal?.aborted) return "";
|
|
11642
11952
|
const plain = await runCommand(
|
|
11643
11953
|
platform2,
|
|
11644
11954
|
filePath,
|
|
@@ -11647,7 +11957,8 @@ async function runHelpForCli(platform2, filePath, childEnv, cwd) {
|
|
|
11647
11957
|
void 0,
|
|
11648
11958
|
1e4,
|
|
11649
11959
|
MAX_OUTPUT_DEFAULT,
|
|
11650
|
-
cwd
|
|
11960
|
+
cwd,
|
|
11961
|
+
signal
|
|
11651
11962
|
);
|
|
11652
11963
|
if (plain.exitCode === 0 && plain.stdout.trim()) return plain.stdout;
|
|
11653
11964
|
return dashed.stdout.trim() ? dashed.stdout : "";
|
|
@@ -11674,7 +11985,7 @@ async function expandGroupCommands(platform2, filePath, commands, childEnv, cwd)
|
|
|
11674
11985
|
}
|
|
11675
11986
|
return current;
|
|
11676
11987
|
}
|
|
11677
|
-
async function discoverCli(platform2, filePath, explicitName, childEnv, cwd) {
|
|
11988
|
+
async function discoverCli(platform2, filePath, explicitName, childEnv, cwd, signal) {
|
|
11678
11989
|
const mcp = await runCommand(
|
|
11679
11990
|
platform2,
|
|
11680
11991
|
filePath,
|
|
@@ -11683,10 +11994,11 @@ async function discoverCli(platform2, filePath, explicitName, childEnv, cwd) {
|
|
|
11683
11994
|
void 0,
|
|
11684
11995
|
1e4,
|
|
11685
11996
|
MAX_OUTPUT_DEFAULT,
|
|
11686
|
-
cwd
|
|
11997
|
+
cwd,
|
|
11998
|
+
signal
|
|
11687
11999
|
);
|
|
11688
12000
|
const override = mcp.exitCode === 0 ? parseMcpOverride(mcp.stdout) : null;
|
|
11689
|
-
const helpText2 = override ? "" : await runHelpForCli(platform2, filePath, childEnv, cwd);
|
|
12001
|
+
const helpText2 = override || signal?.aborted ? "" : await runHelpForCli(platform2, filePath, childEnv, cwd, signal);
|
|
11690
12002
|
if (!override && !helpText2.trim()) {
|
|
11691
12003
|
throw new Error(`${filePath} did not print help or --mcp descriptor`);
|
|
11692
12004
|
}
|
|
@@ -11806,6 +12118,32 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
|
|
|
11806
12118
|
}
|
|
11807
12119
|
return { invoke, rpcTools };
|
|
11808
12120
|
}
|
|
12121
|
+
async function discoverWithSignal(platform2, commands, childEnv, cwd) {
|
|
12122
|
+
const clis = [];
|
|
12123
|
+
let earlyShutdown = false;
|
|
12124
|
+
let inflightDone;
|
|
12125
|
+
const earlyAbort = new AbortController();
|
|
12126
|
+
const onShutdown = () => {
|
|
12127
|
+
earlyShutdown = true;
|
|
12128
|
+
earlyAbort.abort();
|
|
12129
|
+
inflightDone = killInflight();
|
|
12130
|
+
};
|
|
12131
|
+
platform2.onSignal?.("SIGTERM", onShutdown);
|
|
12132
|
+
platform2.onSignal?.("SIGINT", onShutdown);
|
|
12133
|
+
for (const cmd of commands) {
|
|
12134
|
+
try {
|
|
12135
|
+
const cli = await discoverCli(platform2, cmd.path, cmd.name, childEnv, cwd, earlyAbort.signal);
|
|
12136
|
+
clis.push(cli);
|
|
12137
|
+
} catch (err) {
|
|
12138
|
+
if (earlyShutdown) return { exitCode: 143, inflightDone };
|
|
12139
|
+
await platform2.write(2, `mcpcat serve: ${err.message}
|
|
12140
|
+
`);
|
|
12141
|
+
return { exitCode: 2 };
|
|
12142
|
+
}
|
|
12143
|
+
}
|
|
12144
|
+
if (earlyShutdown) return { exitCode: 143, inflightDone };
|
|
12145
|
+
return { clis };
|
|
12146
|
+
}
|
|
11809
12147
|
async function runServe(platform2, options, version) {
|
|
11810
12148
|
let resolvedBearer;
|
|
11811
12149
|
let generatedBearer = false;
|
|
@@ -11849,17 +12187,17 @@ async function runServe(platform2, options, version) {
|
|
|
11849
12187
|
}
|
|
11850
12188
|
const serveCwd = options.cwd ?? platform2.cwd;
|
|
11851
12189
|
const childEnv = buildChildEnv(platform2, options.envAllow);
|
|
11852
|
-
const
|
|
11853
|
-
|
|
11854
|
-
|
|
11855
|
-
|
|
11856
|
-
|
|
11857
|
-
|
|
11858
|
-
|
|
11859
|
-
`);
|
|
11860
|
-
return 2;
|
|
12190
|
+
const discovery = await discoverWithSignal(platform2, options.commands, childEnv, serveCwd);
|
|
12191
|
+
if ("exitCode" in discovery) {
|
|
12192
|
+
if (discovery.inflightDone !== void 0) {
|
|
12193
|
+
try {
|
|
12194
|
+
await discovery.inflightDone;
|
|
12195
|
+
} catch {
|
|
12196
|
+
}
|
|
11861
12197
|
}
|
|
12198
|
+
return discovery.exitCode;
|
|
11862
12199
|
}
|
|
12200
|
+
const { clis } = discovery;
|
|
11863
12201
|
const allTools = clis.flatMap((cli) => buildTools(cli.name, cli.commands));
|
|
11864
12202
|
for (const spec of options.toolTemplates) {
|
|
11865
12203
|
const tool = parseToolTemplate(spec);
|
|
@@ -12085,6 +12423,7 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer, generated
|
|
|
12085
12423
|
if (platform2.onSignal) {
|
|
12086
12424
|
platform2.onSignal("SIGINT", shutdown);
|
|
12087
12425
|
platform2.onSignal("SIGTERM", shutdown);
|
|
12426
|
+
platform2.onSignal("SIGHUP", shutdown);
|
|
12088
12427
|
}
|
|
12089
12428
|
const handler = (request) => handleProxyMcpRequest(
|
|
12090
12429
|
request,
|
|
@@ -12244,11 +12583,12 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
12244
12583
|
await platform2.write(2, `mcpcat serve: listening on ${listenUrl}
|
|
12245
12584
|
`);
|
|
12246
12585
|
let shutdownRequested = false;
|
|
12586
|
+
let inflightDone;
|
|
12247
12587
|
const shutdown = () => {
|
|
12248
12588
|
if (shutdownRequested) return;
|
|
12249
12589
|
shutdownRequested = true;
|
|
12250
12590
|
listener.close();
|
|
12251
|
-
|
|
12591
|
+
inflightDone = killInflight();
|
|
12252
12592
|
if (tokenFilePath) {
|
|
12253
12593
|
platform2.fs.unlink?.(tokenFilePath).catch(() => {
|
|
12254
12594
|
});
|
|
@@ -12257,6 +12597,7 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
12257
12597
|
if (platform2.onSignal) {
|
|
12258
12598
|
platform2.onSignal("SIGINT", shutdown);
|
|
12259
12599
|
platform2.onSignal("SIGTERM", shutdown);
|
|
12600
|
+
platform2.onSignal("SIGHUP", shutdown);
|
|
12260
12601
|
}
|
|
12261
12602
|
const handler = (request) => handleMcpRequest(
|
|
12262
12603
|
request,
|
|
@@ -12284,6 +12625,12 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
12284
12625
|
} catch {
|
|
12285
12626
|
if (!shutdownRequested) throw new Error("listener closed unexpectedly");
|
|
12286
12627
|
}
|
|
12628
|
+
if (inflightDone !== void 0) {
|
|
12629
|
+
try {
|
|
12630
|
+
await inflightDone;
|
|
12631
|
+
} catch {
|
|
12632
|
+
}
|
|
12633
|
+
}
|
|
12287
12634
|
return 0;
|
|
12288
12635
|
}
|
|
12289
12636
|
async function handleMcpRequest(request, tools, call, serverName, version, serverOrigins, allowedOrigins, allowedHosts, resolvedBearer) {
|
|
@@ -12555,124 +12902,6 @@ async function signIn(options) {
|
|
|
12555
12902
|
}
|
|
12556
12903
|
}
|
|
12557
12904
|
|
|
12558
|
-
// src/auth/token-source.ts
|
|
12559
|
-
function mcpNamespace(server) {
|
|
12560
|
-
return `mcp__${server.replace(/-/g, "_")}`;
|
|
12561
|
-
}
|
|
12562
|
-
function storeKey(name, serverUrl) {
|
|
12563
|
-
const ns = mcpNamespace(name);
|
|
12564
|
-
const url = String(new URL(serverUrl));
|
|
12565
|
-
return `${ns}|${url}`;
|
|
12566
|
-
}
|
|
12567
|
-
function isKernel(platform2) {
|
|
12568
|
-
return platform2.kind === "kernel";
|
|
12569
|
-
}
|
|
12570
|
-
|
|
12571
|
-
// src/auth/pi-store.ts
|
|
12572
|
-
var AUTH_FILE = "mcp-auth.json";
|
|
12573
|
-
var FILE_MODE = 384;
|
|
12574
|
-
function parseStates(raw) {
|
|
12575
|
-
if (!raw?.trim()) return {};
|
|
12576
|
-
const parsed = JSON.parse(raw);
|
|
12577
|
-
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
|
|
12578
|
-
}
|
|
12579
|
-
function serializeStates(states) {
|
|
12580
|
-
return `${JSON.stringify(states, null, 2)}
|
|
12581
|
-
`;
|
|
12582
|
-
}
|
|
12583
|
-
function legacyKey(serverUrl) {
|
|
12584
|
-
return String(new URL(serverUrl));
|
|
12585
|
-
}
|
|
12586
|
-
var PiFileTokenSource = class {
|
|
12587
|
-
platform;
|
|
12588
|
-
authPath;
|
|
12589
|
-
dirPath;
|
|
12590
|
-
constructor(platform2) {
|
|
12591
|
-
this.platform = platform2;
|
|
12592
|
-
this.dirPath = userConfigDir(platform2);
|
|
12593
|
-
this.authPath = `${this.dirPath}/${AUTH_FILE}`;
|
|
12594
|
-
}
|
|
12595
|
-
get path() {
|
|
12596
|
-
return this.authPath;
|
|
12597
|
-
}
|
|
12598
|
-
async getToken(name, serverUrl) {
|
|
12599
|
-
const state = await this.load(name, serverUrl);
|
|
12600
|
-
if (!state?.tokens?.access_token) return void 0;
|
|
12601
|
-
return {
|
|
12602
|
-
accessToken: state.tokens.access_token,
|
|
12603
|
-
expiresAt: state.tokensExpireAt
|
|
12604
|
-
};
|
|
12605
|
-
}
|
|
12606
|
-
async load(name, serverUrl) {
|
|
12607
|
-
const key = storeKey(name, serverUrl);
|
|
12608
|
-
const legacy = legacyKey(serverUrl);
|
|
12609
|
-
const release = await this.acquireLock();
|
|
12610
|
-
try {
|
|
12611
|
-
const raw = await this.readRaw();
|
|
12612
|
-
const states = parseStates(raw);
|
|
12613
|
-
if (states[key]) return states[key];
|
|
12614
|
-
if (states[legacy]) {
|
|
12615
|
-
states[key] = states[legacy];
|
|
12616
|
-
delete states[legacy];
|
|
12617
|
-
await this.writeStates(states);
|
|
12618
|
-
return states[key];
|
|
12619
|
-
}
|
|
12620
|
-
return void 0;
|
|
12621
|
-
} finally {
|
|
12622
|
-
await release();
|
|
12623
|
-
}
|
|
12624
|
-
}
|
|
12625
|
-
async save(name, serverUrl, state) {
|
|
12626
|
-
const key = storeKey(name, serverUrl);
|
|
12627
|
-
const release = await this.acquireLock();
|
|
12628
|
-
try {
|
|
12629
|
-
const raw = await this.readRaw();
|
|
12630
|
-
const states = parseStates(raw);
|
|
12631
|
-
states[key] = state;
|
|
12632
|
-
await this.writeStates(states);
|
|
12633
|
-
} finally {
|
|
12634
|
-
await release();
|
|
12635
|
-
}
|
|
12636
|
-
}
|
|
12637
|
-
async remove(name, serverUrl) {
|
|
12638
|
-
const key = storeKey(name, serverUrl);
|
|
12639
|
-
const legacy = legacyKey(serverUrl);
|
|
12640
|
-
const release = await this.acquireLock();
|
|
12641
|
-
try {
|
|
12642
|
-
const raw = await this.readRaw();
|
|
12643
|
-
const states = parseStates(raw);
|
|
12644
|
-
const stored = key in states ? key : legacy in states ? legacy : void 0;
|
|
12645
|
-
if (!stored) return false;
|
|
12646
|
-
delete states[stored];
|
|
12647
|
-
await this.writeStates(states);
|
|
12648
|
-
return true;
|
|
12649
|
-
} finally {
|
|
12650
|
-
await release();
|
|
12651
|
-
}
|
|
12652
|
-
}
|
|
12653
|
-
async acquireLock() {
|
|
12654
|
-
await this.ensureDir();
|
|
12655
|
-
return acquireLock(this.platform.fs, this.authPath);
|
|
12656
|
-
}
|
|
12657
|
-
async ensureDir() {
|
|
12658
|
-
try {
|
|
12659
|
-
await this.platform.fs.mkdir(this.dirPath);
|
|
12660
|
-
} catch {
|
|
12661
|
-
}
|
|
12662
|
-
}
|
|
12663
|
-
async readRaw() {
|
|
12664
|
-
try {
|
|
12665
|
-
const data = await this.platform.fs.readFile(this.authPath);
|
|
12666
|
-
return new TextDecoder().decode(data);
|
|
12667
|
-
} catch {
|
|
12668
|
-
return void 0;
|
|
12669
|
-
}
|
|
12670
|
-
}
|
|
12671
|
-
async writeStates(states) {
|
|
12672
|
-
await this.platform.fs.writeFile(this.authPath, serializeStates(states), { mode: FILE_MODE });
|
|
12673
|
-
}
|
|
12674
|
-
};
|
|
12675
|
-
|
|
12676
12905
|
// src/cli/auth-verbs.ts
|
|
12677
12906
|
var resolvedSource;
|
|
12678
12907
|
async function createTokenSource(platform2) {
|
|
@@ -14366,6 +14595,17 @@ async function runCall(platform2, conn, toolName, dataArg, toolArgs, opts) {
|
|
|
14366
14595
|
return EXIT_USAGE;
|
|
14367
14596
|
}
|
|
14368
14597
|
fin = mergeArgs(base, c.value);
|
|
14598
|
+
} else if (!dataArg && !opts.noValidate) {
|
|
14599
|
+
const tools = await conn.listTools();
|
|
14600
|
+
const tool = tools.find((t) => t.name === toolName);
|
|
14601
|
+
const schema = tool?.inputSchema;
|
|
14602
|
+
const required = Array.isArray(schema?.required) ? schema.required : [];
|
|
14603
|
+
for (const r of required) {
|
|
14604
|
+
if (!Object.hasOwn(base, r)) {
|
|
14605
|
+
await writeErr(platform2, opts.json ?? false, "usage", `missing required flag --${r}`);
|
|
14606
|
+
return EXIT_USAGE;
|
|
14607
|
+
}
|
|
14608
|
+
}
|
|
14369
14609
|
}
|
|
14370
14610
|
return renderCallResult(
|
|
14371
14611
|
platform2,
|
|
@@ -14583,7 +14823,10 @@ async function probeServer(_name, config, platform2) {
|
|
|
14583
14823
|
const opts = { version: "probe" };
|
|
14584
14824
|
if ("url" in config && config.url) {
|
|
14585
14825
|
opts.url = config.url;
|
|
14586
|
-
if ("headers" in config)
|
|
14826
|
+
if ("headers" in config) {
|
|
14827
|
+
const expanded = expandHeaders(config.headers, platform2.env);
|
|
14828
|
+
if (expanded) opts.headers = expanded;
|
|
14829
|
+
}
|
|
14587
14830
|
} else if ("command" in config && config.command) {
|
|
14588
14831
|
opts.command = config.command;
|
|
14589
14832
|
if ("args" in config) opts.args = config.args;
|
|
@@ -14717,6 +14960,10 @@ async function dispatchMultiTarget(platform2, parsed, version) {
|
|
|
14717
14960
|
} catch {
|
|
14718
14961
|
}
|
|
14719
14962
|
}
|
|
14963
|
+
try {
|
|
14964
|
+
await Promise.race([killAllStdio(), new Promise((r) => setTimeout(r, 3e3))]);
|
|
14965
|
+
} catch {
|
|
14966
|
+
}
|
|
14720
14967
|
platform2.exit(exitCode);
|
|
14721
14968
|
}
|
|
14722
14969
|
platform2.onSignal?.("SIGTERM", () => void killTreeAndExit(143));
|
|
@@ -14945,11 +15192,25 @@ function classifyError(err) {
|
|
|
14945
15192
|
}
|
|
14946
15193
|
async function resolveAuthProvider(platform2, hasAuthHeader, url, target, config) {
|
|
14947
15194
|
if (hasAuthHeader || !url) return void 0;
|
|
14948
|
-
const
|
|
15195
|
+
const piSource = new PiFileTokenSource(platform2);
|
|
14949
15196
|
const entry = config.entry(target);
|
|
14950
15197
|
const oauth = entry && "url" in entry.config ? entry.config.oauth : void 0;
|
|
14951
15198
|
const name = entry ? target : `url:${new URL(url).origin}${new URL(url).pathname}`;
|
|
14952
|
-
|
|
15199
|
+
const provider = createMcpAuthProvider({
|
|
15200
|
+
name,
|
|
15201
|
+
serverUrl: url,
|
|
15202
|
+
source: piSource,
|
|
15203
|
+
oauth,
|
|
15204
|
+
platform: platform2
|
|
15205
|
+
});
|
|
15206
|
+
const credentialSecrets = [];
|
|
15207
|
+
const state = await piSource.loadWithoutLock(name, url);
|
|
15208
|
+
if (state?.tokens?.access_token) credentialSecrets.push(state.tokens.access_token);
|
|
15209
|
+
if (state?.tokens?.refresh_token) credentialSecrets.push(state.tokens.refresh_token);
|
|
15210
|
+
if (state?.clientInformation && "client_secret" in state.clientInformation && state.clientInformation.client_secret) {
|
|
15211
|
+
credentialSecrets.push(state.clientInformation.client_secret);
|
|
15212
|
+
}
|
|
15213
|
+
return { provider, credentialSecrets };
|
|
14953
15214
|
}
|
|
14954
15215
|
function classifyConnectionError(err, target, interrupted) {
|
|
14955
15216
|
if (err instanceof McpAuthError || err instanceof AuthStepUpError || err instanceof JerkSignInRequiredError) {
|
|
@@ -14995,7 +15256,7 @@ async function handleInputRequired(platform2, conn, err, respondData, json) {
|
|
|
14995
15256
|
`);
|
|
14996
15257
|
return EXIT_INPUT_REQUIRED;
|
|
14997
15258
|
}
|
|
14998
|
-
const isTty = platform2.isatty(0);
|
|
15259
|
+
const isTty = platform2.isatty(0) && platform2.isatty(2);
|
|
14999
15260
|
const elicitResult = await handleElicitation(platform2, inputReq, isTty, respondData);
|
|
15000
15261
|
if ("exitCode" in elicitResult) {
|
|
15001
15262
|
await platform2.write(2, `${err.requestJson}
|
|
@@ -15041,6 +15302,10 @@ async function runClientVerb(platform2, version, parsed) {
|
|
|
15041
15302
|
} catch {
|
|
15042
15303
|
}
|
|
15043
15304
|
}
|
|
15305
|
+
try {
|
|
15306
|
+
await Promise.race([killAllStdio(), new Promise((r) => setTimeout(r, 3e3))]);
|
|
15307
|
+
} catch {
|
|
15308
|
+
}
|
|
15044
15309
|
platform2.exit(exitCode);
|
|
15045
15310
|
}
|
|
15046
15311
|
platform2.onSignal?.("SIGINT", () => {
|
|
@@ -15088,13 +15353,20 @@ async function runClientVerb(platform2, version, parsed) {
|
|
|
15088
15353
|
};
|
|
15089
15354
|
const mergedHeaders = { ...configHeaders, ...expanded };
|
|
15090
15355
|
const hasAuthHeader = "Authorization" in mergedHeaders;
|
|
15091
|
-
const
|
|
15356
|
+
const authResolved = await resolveAuthProvider(
|
|
15092
15357
|
platform2,
|
|
15093
15358
|
hasAuthHeader,
|
|
15094
15359
|
resolved.url,
|
|
15095
15360
|
target,
|
|
15096
15361
|
config
|
|
15097
15362
|
);
|
|
15363
|
+
const authProvider = authResolved?.provider;
|
|
15364
|
+
if (authResolved?.credentialSecrets) {
|
|
15365
|
+
for (const s of authResolved.credentialSecrets) {
|
|
15366
|
+
allSecrets.add(s);
|
|
15367
|
+
verbose.secrets.add(s);
|
|
15368
|
+
}
|
|
15369
|
+
}
|
|
15098
15370
|
const roots = buildRoots(parsed.roots, !!resolved.command, platform2.cwd);
|
|
15099
15371
|
let respondData;
|
|
15100
15372
|
if (parsed.respondArg) {
|
|
@@ -15211,31 +15483,50 @@ function kernelFs(ctx) {
|
|
|
15211
15483
|
}
|
|
15212
15484
|
},
|
|
15213
15485
|
rmdir: (path) => ctx.fs.rm(path),
|
|
15214
|
-
unlink: (path) => ctx.fs.unlink(path)
|
|
15486
|
+
unlink: (path) => ctx.fs.unlink(path),
|
|
15487
|
+
async createExclusive(path) {
|
|
15488
|
+
const f = await ctx.open(path, { create: true, exclusive: true, write: true });
|
|
15489
|
+
await f.close();
|
|
15490
|
+
},
|
|
15491
|
+
chmod: ctx.fs.chmod ? (path, mode) => ctx.fs.chmod(path, mode) : void 0
|
|
15215
15492
|
};
|
|
15216
15493
|
}
|
|
15494
|
+
var groupSpawnSupported;
|
|
15217
15495
|
async function kernelSpawn(ctx, options) {
|
|
15496
|
+
const tryGroup = groupSpawnSupported !== false;
|
|
15218
15497
|
const result = await ctx.spawn({
|
|
15219
15498
|
argv: options.argv,
|
|
15220
15499
|
env: options.env,
|
|
15221
15500
|
cwd: options.cwd,
|
|
15222
15501
|
stdin: "pipe",
|
|
15223
15502
|
stdout: "pipe",
|
|
15224
|
-
stderr: "pipe"
|
|
15503
|
+
stderr: "pipe",
|
|
15504
|
+
...tryGroup ? { pgid: 0 } : {}
|
|
15225
15505
|
});
|
|
15226
15506
|
const childPid = result.pid;
|
|
15507
|
+
if (groupSpawnSupported === void 0) {
|
|
15508
|
+
try {
|
|
15509
|
+
await ctx.kill(-childPid, "SIGCONT");
|
|
15510
|
+
groupSpawnSupported = true;
|
|
15511
|
+
} catch {
|
|
15512
|
+
groupSpawnSupported = false;
|
|
15513
|
+
}
|
|
15514
|
+
}
|
|
15515
|
+
const useGroupKill = groupSpawnSupported;
|
|
15227
15516
|
return {
|
|
15228
15517
|
stdin: result.stdin,
|
|
15229
15518
|
stdout: result.stdout,
|
|
15230
15519
|
stderr: result.stderr,
|
|
15231
15520
|
wait: () => result.wait(),
|
|
15232
|
-
kill: (signal) => {
|
|
15233
|
-
|
|
15234
|
-
|
|
15235
|
-
|
|
15521
|
+
kill: async (signal) => {
|
|
15522
|
+
if (useGroupKill) {
|
|
15523
|
+
try {
|
|
15524
|
+
await ctx.kill(-childPid, signal);
|
|
15525
|
+
} catch {
|
|
15526
|
+
}
|
|
15236
15527
|
}
|
|
15237
15528
|
try {
|
|
15238
|
-
ctx.kill(childPid, signal);
|
|
15529
|
+
await ctx.kill(childPid, signal);
|
|
15239
15530
|
} catch {
|
|
15240
15531
|
}
|
|
15241
15532
|
}
|
|
@@ -15455,7 +15746,7 @@ async function main(ctx) {
|
|
|
15455
15746
|
packageDir: "/node_modules/@ai-ecoverse/mcpcat"
|
|
15456
15747
|
});
|
|
15457
15748
|
const args = ctx.argv.slice(1);
|
|
15458
|
-
return run(platform2, "0.14.
|
|
15749
|
+
return run(platform2, "0.14.3", args);
|
|
15459
15750
|
}
|
|
15460
15751
|
export {
|
|
15461
15752
|
main as default
|