@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/host/mcpcat.mjs
CHANGED
|
@@ -49,6 +49,101 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
49
49
|
mod
|
|
50
50
|
));
|
|
51
51
|
|
|
52
|
+
// src/config/lock.ts
|
|
53
|
+
var lock_exports = {};
|
|
54
|
+
__export(lock_exports, {
|
|
55
|
+
acquireLock: () => acquireLock
|
|
56
|
+
});
|
|
57
|
+
function isEexist(err) {
|
|
58
|
+
if (err instanceof Error && "code" in err)
|
|
59
|
+
return err.code === "EEXIST";
|
|
60
|
+
if (err instanceof Error) return err.message.includes("EEXIST");
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
async function removeStaleLockDir(fs, lockPath) {
|
|
64
|
+
try {
|
|
65
|
+
const s = await fs.stat(lockPath);
|
|
66
|
+
if (Date.now() - s.mtimeMs > LOCK_STALE_MS) {
|
|
67
|
+
try {
|
|
68
|
+
await fs.rmdir(lockPath);
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
async function removeStaleLockFile(fs, lockPath) {
|
|
78
|
+
try {
|
|
79
|
+
const s = await fs.stat(lockPath);
|
|
80
|
+
if (Date.now() - s.mtimeMs > LOCK_STALE_MS) {
|
|
81
|
+
try {
|
|
82
|
+
await fs.unlink(lockPath);
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
async function acquireLock(fs, configPath) {
|
|
92
|
+
const useFile = typeof fs.createExclusive === "function";
|
|
93
|
+
const lockPath = `${configPath}.lock`;
|
|
94
|
+
const start = Date.now();
|
|
95
|
+
const parentDir = lockPath.slice(0, lockPath.lastIndexOf("/"));
|
|
96
|
+
if (parentDir) {
|
|
97
|
+
try {
|
|
98
|
+
await fs.mkdir(parentDir);
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
while (true) {
|
|
103
|
+
try {
|
|
104
|
+
if (useFile) {
|
|
105
|
+
await fs.createExclusive(lockPath);
|
|
106
|
+
} else {
|
|
107
|
+
await fs.mkdirExclusive(lockPath);
|
|
108
|
+
}
|
|
109
|
+
break;
|
|
110
|
+
} catch (err) {
|
|
111
|
+
if (!isEexist(err)) throw err;
|
|
112
|
+
if (useFile) {
|
|
113
|
+
await removeStaleLockFile(fs, lockPath);
|
|
114
|
+
} else {
|
|
115
|
+
await removeStaleLockDir(fs, lockPath);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (Date.now() - start > LOCK_MAX_WAIT_MS) {
|
|
119
|
+
throw new Error(`mcpcat: could not acquire lock ${lockPath} within ${LOCK_MAX_WAIT_MS}ms`);
|
|
120
|
+
}
|
|
121
|
+
await new Promise((r) => setTimeout(r, LOCK_POLL_MS));
|
|
122
|
+
}
|
|
123
|
+
let released = false;
|
|
124
|
+
return async () => {
|
|
125
|
+
if (released) return;
|
|
126
|
+
released = true;
|
|
127
|
+
try {
|
|
128
|
+
if (useFile && fs.unlink) {
|
|
129
|
+
await fs.unlink(lockPath);
|
|
130
|
+
} else {
|
|
131
|
+
await fs.rmdir(lockPath);
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
var LOCK_STALE_MS, LOCK_POLL_MS, LOCK_MAX_WAIT_MS;
|
|
138
|
+
var init_lock = __esm({
|
|
139
|
+
"src/config/lock.ts"() {
|
|
140
|
+
"use strict";
|
|
141
|
+
LOCK_STALE_MS = 1e4;
|
|
142
|
+
LOCK_POLL_MS = 50;
|
|
143
|
+
LOCK_MAX_WAIT_MS = 5e3;
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
52
147
|
// node_modules/isexe/windows.js
|
|
53
148
|
var require_windows = __commonJS({
|
|
54
149
|
"node_modules/isexe/windows.js"(exports, module) {
|
|
@@ -6345,7 +6440,7 @@ var McpOAuthProvider = class {
|
|
|
6345
6440
|
};
|
|
6346
6441
|
|
|
6347
6442
|
// src/auth/jerk-token-source.ts
|
|
6348
|
-
var MIN_VERSION = [0,
|
|
6443
|
+
var MIN_VERSION = [0, 14, 1];
|
|
6349
6444
|
var VERSION_TIMEOUT_MS = 2e3;
|
|
6350
6445
|
var cachedAvailable;
|
|
6351
6446
|
function parseVersion(raw) {
|
|
@@ -6471,6 +6566,192 @@ var JerkTokenSource = class {
|
|
|
6471
6566
|
}
|
|
6472
6567
|
};
|
|
6473
6568
|
|
|
6569
|
+
// src/auth/pi-store.ts
|
|
6570
|
+
init_lock();
|
|
6571
|
+
|
|
6572
|
+
// src/config/paths.ts
|
|
6573
|
+
function userConfigDir(platform2) {
|
|
6574
|
+
const piDir = platform2.env.PI_CODING_AGENT_DIR;
|
|
6575
|
+
if (piDir) return piDir;
|
|
6576
|
+
const home = platform2.env.HOME ?? platform2.env.USERPROFILE ?? "/";
|
|
6577
|
+
return `${home}/.pi/agent`;
|
|
6578
|
+
}
|
|
6579
|
+
function userConfigPath(platform2) {
|
|
6580
|
+
return `${userConfigDir(platform2)}/mcp.json`;
|
|
6581
|
+
}
|
|
6582
|
+
function projectConfigPath(platform2) {
|
|
6583
|
+
return `${platform2.cwd}/.pi/mcp.json`;
|
|
6584
|
+
}
|
|
6585
|
+
function stateDir(platform2) {
|
|
6586
|
+
if (platform2.env.XDG_STATE_HOME) return `${platform2.env.XDG_STATE_HOME}/mcpcat`;
|
|
6587
|
+
const home = platform2.env.HOME ?? platform2.env.USERPROFILE ?? "/";
|
|
6588
|
+
if (platform2.env.LOCALAPPDATA) return `${platform2.env.LOCALAPPDATA}\\mcpcat\\state`;
|
|
6589
|
+
if (platform2.env.SLICC_KERNEL) return `${home}/.local/state/mcpcat`;
|
|
6590
|
+
return `${home}/Library/Application Support/mcpcat`;
|
|
6591
|
+
}
|
|
6592
|
+
|
|
6593
|
+
// src/auth/token-source.ts
|
|
6594
|
+
function mcpNamespace(server) {
|
|
6595
|
+
return `mcp__${server.replace(/-/g, "_")}`;
|
|
6596
|
+
}
|
|
6597
|
+
function storeKey(name, serverUrl) {
|
|
6598
|
+
const ns = mcpNamespace(name);
|
|
6599
|
+
const url = String(new URL(serverUrl));
|
|
6600
|
+
return `${ns}|${url}`;
|
|
6601
|
+
}
|
|
6602
|
+
function isKernel(platform2) {
|
|
6603
|
+
return platform2.kind === "kernel";
|
|
6604
|
+
}
|
|
6605
|
+
|
|
6606
|
+
// src/auth/pi-store.ts
|
|
6607
|
+
var AUTH_FILE = "mcp-auth.json";
|
|
6608
|
+
var FILE_MODE = 384;
|
|
6609
|
+
var DIR_MODE = 448;
|
|
6610
|
+
function parseStates(raw) {
|
|
6611
|
+
if (!raw?.trim()) return {};
|
|
6612
|
+
const parsed = JSON.parse(raw);
|
|
6613
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
|
|
6614
|
+
}
|
|
6615
|
+
function serializeStates(states) {
|
|
6616
|
+
return `${JSON.stringify(states, null, 2)}
|
|
6617
|
+
`;
|
|
6618
|
+
}
|
|
6619
|
+
function legacyKey(serverUrl) {
|
|
6620
|
+
return String(new URL(serverUrl));
|
|
6621
|
+
}
|
|
6622
|
+
var PiFileTokenSource = class {
|
|
6623
|
+
platform;
|
|
6624
|
+
authPath;
|
|
6625
|
+
dirPath;
|
|
6626
|
+
constructor(platform2) {
|
|
6627
|
+
this.platform = platform2;
|
|
6628
|
+
this.dirPath = userConfigDir(platform2);
|
|
6629
|
+
this.authPath = `${this.dirPath}/${AUTH_FILE}`;
|
|
6630
|
+
}
|
|
6631
|
+
get path() {
|
|
6632
|
+
return this.authPath;
|
|
6633
|
+
}
|
|
6634
|
+
async loadWithoutLock(name, serverUrl) {
|
|
6635
|
+
const key = storeKey(name, serverUrl);
|
|
6636
|
+
const legacy = legacyKey(serverUrl);
|
|
6637
|
+
try {
|
|
6638
|
+
const raw = await this.readRaw();
|
|
6639
|
+
const states = parseStates(raw);
|
|
6640
|
+
return states[key] ?? states[legacy];
|
|
6641
|
+
} catch {
|
|
6642
|
+
return void 0;
|
|
6643
|
+
}
|
|
6644
|
+
}
|
|
6645
|
+
async getToken(name, serverUrl) {
|
|
6646
|
+
const state = await this.load(name, serverUrl);
|
|
6647
|
+
if (!state?.tokens?.access_token) return void 0;
|
|
6648
|
+
return {
|
|
6649
|
+
accessToken: state.tokens.access_token,
|
|
6650
|
+
expiresAt: state.tokensExpireAt
|
|
6651
|
+
};
|
|
6652
|
+
}
|
|
6653
|
+
async load(name, serverUrl) {
|
|
6654
|
+
const key = storeKey(name, serverUrl);
|
|
6655
|
+
const legacy = legacyKey(serverUrl);
|
|
6656
|
+
const release = await this.acquireLock();
|
|
6657
|
+
try {
|
|
6658
|
+
const raw = await this.readRaw();
|
|
6659
|
+
const states = parseStates(raw);
|
|
6660
|
+
if (states[key]) return states[key];
|
|
6661
|
+
if (states[legacy]) {
|
|
6662
|
+
states[key] = states[legacy];
|
|
6663
|
+
delete states[legacy];
|
|
6664
|
+
await this.writeStates(states);
|
|
6665
|
+
return states[key];
|
|
6666
|
+
}
|
|
6667
|
+
return void 0;
|
|
6668
|
+
} finally {
|
|
6669
|
+
await release();
|
|
6670
|
+
}
|
|
6671
|
+
}
|
|
6672
|
+
async save(name, serverUrl, state) {
|
|
6673
|
+
const key = storeKey(name, serverUrl);
|
|
6674
|
+
const release = await this.acquireLock();
|
|
6675
|
+
try {
|
|
6676
|
+
const raw = await this.readRaw();
|
|
6677
|
+
const states = parseStates(raw);
|
|
6678
|
+
states[key] = state;
|
|
6679
|
+
await this.writeStates(states);
|
|
6680
|
+
} finally {
|
|
6681
|
+
await release();
|
|
6682
|
+
}
|
|
6683
|
+
}
|
|
6684
|
+
async remove(name, serverUrl) {
|
|
6685
|
+
const key = storeKey(name, serverUrl);
|
|
6686
|
+
const legacy = legacyKey(serverUrl);
|
|
6687
|
+
const release = await this.acquireLock();
|
|
6688
|
+
try {
|
|
6689
|
+
const raw = await this.readRaw();
|
|
6690
|
+
const states = parseStates(raw);
|
|
6691
|
+
const stored = key in states ? key : legacy in states ? legacy : void 0;
|
|
6692
|
+
if (!stored) return false;
|
|
6693
|
+
delete states[stored];
|
|
6694
|
+
await this.writeStates(states);
|
|
6695
|
+
return true;
|
|
6696
|
+
} finally {
|
|
6697
|
+
await release();
|
|
6698
|
+
}
|
|
6699
|
+
}
|
|
6700
|
+
async acquireLock() {
|
|
6701
|
+
await this.ensureDir();
|
|
6702
|
+
return acquireLock(this.platform.fs, this.authPath);
|
|
6703
|
+
}
|
|
6704
|
+
async ensureDir() {
|
|
6705
|
+
const fs = this.platform.fs;
|
|
6706
|
+
try {
|
|
6707
|
+
await fs.mkdir(this.dirPath, { mode: DIR_MODE });
|
|
6708
|
+
} catch {
|
|
6709
|
+
}
|
|
6710
|
+
if (fs.chmod) {
|
|
6711
|
+
try {
|
|
6712
|
+
await fs.chmod(this.dirPath, DIR_MODE);
|
|
6713
|
+
} catch {
|
|
6714
|
+
}
|
|
6715
|
+
const piDir = this.dirPath.slice(0, this.dirPath.lastIndexOf("/"));
|
|
6716
|
+
if (piDir) {
|
|
6717
|
+
try {
|
|
6718
|
+
await fs.chmod(piDir, DIR_MODE);
|
|
6719
|
+
} catch {
|
|
6720
|
+
}
|
|
6721
|
+
}
|
|
6722
|
+
}
|
|
6723
|
+
}
|
|
6724
|
+
async readRaw() {
|
|
6725
|
+
try {
|
|
6726
|
+
const data = await this.platform.fs.readFile(this.authPath);
|
|
6727
|
+
return new TextDecoder().decode(data);
|
|
6728
|
+
} catch {
|
|
6729
|
+
return void 0;
|
|
6730
|
+
}
|
|
6731
|
+
}
|
|
6732
|
+
async writeStates(states) {
|
|
6733
|
+
const fs = this.platform.fs;
|
|
6734
|
+
const content = serializeStates(states);
|
|
6735
|
+
const tmpPath = `${this.authPath}.tmp.${Date.now()}`;
|
|
6736
|
+
try {
|
|
6737
|
+
await fs.writeFile(tmpPath, content, { mode: FILE_MODE });
|
|
6738
|
+
await fs.rename(tmpPath, this.authPath);
|
|
6739
|
+
} catch (renameErr) {
|
|
6740
|
+
try {
|
|
6741
|
+
if (fs.unlink) await fs.unlink(tmpPath);
|
|
6742
|
+
} catch {
|
|
6743
|
+
}
|
|
6744
|
+
throw renameErr;
|
|
6745
|
+
}
|
|
6746
|
+
if (fs.chmod) {
|
|
6747
|
+
try {
|
|
6748
|
+
await fs.chmod(this.authPath, FILE_MODE);
|
|
6749
|
+
} catch {
|
|
6750
|
+
}
|
|
6751
|
+
}
|
|
6752
|
+
}
|
|
6753
|
+
};
|
|
6754
|
+
|
|
6474
6755
|
// src/auth/auth-provider.ts
|
|
6475
6756
|
var CIMD_URL = "https://mcpcat.sliccy.ai/oauth/client.json";
|
|
6476
6757
|
var CLIENT_NAME = "mcpcat";
|
|
@@ -6492,10 +6773,12 @@ var SourceBackedStore = class {
|
|
|
6492
6773
|
source;
|
|
6493
6774
|
name;
|
|
6494
6775
|
serverUrl;
|
|
6495
|
-
|
|
6776
|
+
platform;
|
|
6777
|
+
constructor(source, name, serverUrl, platform2) {
|
|
6496
6778
|
this.source = source;
|
|
6497
6779
|
this.name = name;
|
|
6498
6780
|
this.serverUrl = serverUrl;
|
|
6781
|
+
this.platform = platform2;
|
|
6499
6782
|
}
|
|
6500
6783
|
async load() {
|
|
6501
6784
|
return this.source.load(this.name, this.serverUrl);
|
|
@@ -6503,6 +6786,17 @@ var SourceBackedStore = class {
|
|
|
6503
6786
|
async save(state) {
|
|
6504
6787
|
await this.source.save(this.name, this.serverUrl, state);
|
|
6505
6788
|
}
|
|
6789
|
+
async withRefreshLock(fn) {
|
|
6790
|
+
if (!(this.source instanceof PiFileTokenSource)) return fn();
|
|
6791
|
+
const refreshPath = `${this.source.path}.refresh`;
|
|
6792
|
+
const { acquireLock: acquireLock2 } = await Promise.resolve().then(() => (init_lock(), lock_exports));
|
|
6793
|
+
const release = await acquireLock2(this.platform.fs, refreshPath);
|
|
6794
|
+
try {
|
|
6795
|
+
return await fn();
|
|
6796
|
+
} finally {
|
|
6797
|
+
await release();
|
|
6798
|
+
}
|
|
6799
|
+
}
|
|
6506
6800
|
};
|
|
6507
6801
|
function cimdFn(redirectUrl) {
|
|
6508
6802
|
return (metadata) => {
|
|
@@ -6531,10 +6825,9 @@ function createMcpAuthProvider(options) {
|
|
|
6531
6825
|
function createJerkAuthProvider(options) {
|
|
6532
6826
|
const { name, serverUrl, source } = options;
|
|
6533
6827
|
let currentToken;
|
|
6828
|
+
const _fetched = false;
|
|
6534
6829
|
return {
|
|
6535
6830
|
async token() {
|
|
6536
|
-
const result = await source.getToken(name, serverUrl);
|
|
6537
|
-
currentToken = result?.accessToken;
|
|
6538
6831
|
return currentToken;
|
|
6539
6832
|
},
|
|
6540
6833
|
async onUnauthorized(context) {
|
|
@@ -6565,10 +6858,10 @@ function createJerkAuthProvider(options) {
|
|
|
6565
6858
|
}
|
|
6566
6859
|
function createPiAuthProvider(options) {
|
|
6567
6860
|
const { name, serverUrl, source, oauth, platform: platform2 } = options;
|
|
6568
|
-
const store = new SourceBackedStore(source, name, serverUrl);
|
|
6861
|
+
const store = new SourceBackedStore(source, name, serverUrl, platform2);
|
|
6569
6862
|
let refreshing;
|
|
6570
6863
|
const refresh = async (staleToken) => {
|
|
6571
|
-
refreshing ??= (async () => {
|
|
6864
|
+
refreshing ??= store.withRefreshLock(async () => {
|
|
6572
6865
|
const state = await store.load();
|
|
6573
6866
|
if (state?.tokens?.access_token !== staleToken) return;
|
|
6574
6867
|
if (!state?.tokens?.refresh_token) throw new McpOAuthAuthorizationRequiredError();
|
|
@@ -6591,7 +6884,7 @@ function createPiAuthProvider(options) {
|
|
|
6591
6884
|
fetch: timedFetch(platform2.fetch)
|
|
6592
6885
|
});
|
|
6593
6886
|
if (result === "REDIRECT") throw new McpOAuthAuthorizationRequiredError();
|
|
6594
|
-
})
|
|
6887
|
+
}).finally(() => {
|
|
6595
6888
|
refreshing = void 0;
|
|
6596
6889
|
});
|
|
6597
6890
|
return refreshing;
|
|
@@ -7718,11 +8011,45 @@ var StreamableHttpTransport = class extends TransportEvents {
|
|
|
7718
8011
|
}
|
|
7719
8012
|
};
|
|
7720
8013
|
|
|
7721
|
-
// src/client/
|
|
7722
|
-
var
|
|
7723
|
-
var
|
|
7724
|
-
|
|
7725
|
-
|
|
8014
|
+
// src/client/stdio-registry.ts
|
|
8015
|
+
var GRACE_MS = 2e3;
|
|
8016
|
+
var registry = /* @__PURE__ */ new Set();
|
|
8017
|
+
function trackStdio(proc) {
|
|
8018
|
+
registry.add(proc);
|
|
8019
|
+
void proc.wait().then(
|
|
8020
|
+
() => registry.delete(proc),
|
|
8021
|
+
() => registry.delete(proc)
|
|
8022
|
+
);
|
|
8023
|
+
}
|
|
8024
|
+
async function killAllStdio() {
|
|
8025
|
+
const procs = [...registry];
|
|
8026
|
+
if (procs.length === 0) return;
|
|
8027
|
+
await Promise.all(
|
|
8028
|
+
procs.map(async (proc) => {
|
|
8029
|
+
try {
|
|
8030
|
+
await proc.kill("SIGTERM");
|
|
8031
|
+
} catch {
|
|
8032
|
+
}
|
|
8033
|
+
const exited = await Promise.race([
|
|
8034
|
+
proc.wait().then(() => true),
|
|
8035
|
+
new Promise((r) => setTimeout(() => r(false), GRACE_MS))
|
|
8036
|
+
]);
|
|
8037
|
+
if (!exited) {
|
|
8038
|
+
try {
|
|
8039
|
+
await proc.kill("SIGKILL");
|
|
8040
|
+
} catch {
|
|
8041
|
+
}
|
|
8042
|
+
}
|
|
8043
|
+
})
|
|
8044
|
+
);
|
|
8045
|
+
registry.clear();
|
|
8046
|
+
}
|
|
8047
|
+
|
|
8048
|
+
// src/client/platform-stdio-transport.ts
|
|
8049
|
+
var DEFAULT_MAX_MESSAGE_BYTES2 = 4 * 1024 * 1024;
|
|
8050
|
+
var DEFAULT_MAX_STDERR_BYTES2 = 64 * 1024;
|
|
8051
|
+
var DEFAULT_CLOSE_TIMEOUT_MS2 = 2e3;
|
|
8052
|
+
var STDIN_CLOSE_GRACE_MS2 = 500;
|
|
7726
8053
|
var PlatformStdioTransport = class {
|
|
7727
8054
|
options;
|
|
7728
8055
|
process;
|
|
@@ -7774,16 +8101,21 @@ var PlatformStdioTransport = class {
|
|
|
7774
8101
|
if (this.started) throw new Error("MCP stdio transport already started");
|
|
7775
8102
|
if (this.closed) throw new Error("MCP connection closed");
|
|
7776
8103
|
this.started = true;
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
8104
|
+
if (this.options.existingProcess) {
|
|
8105
|
+
this.process = this.options.existingProcess;
|
|
8106
|
+
} else {
|
|
8107
|
+
const env = this.options.inheritEnv === false ? { ...this.options.env ?? {} } : {
|
|
8108
|
+
...this.options.platform.env,
|
|
8109
|
+
...this.options.env
|
|
8110
|
+
};
|
|
8111
|
+
this.process = await this.options.platform.spawn({
|
|
8112
|
+
argv: [this.options.command, ...this.options.args ?? []],
|
|
8113
|
+
env,
|
|
8114
|
+
cwd: this.options.cwd
|
|
8115
|
+
});
|
|
8116
|
+
trackStdio(this.process);
|
|
8117
|
+
}
|
|
8118
|
+
void this.readStdout(this.options.pendingStdout);
|
|
7787
8119
|
void this.readStderr();
|
|
7788
8120
|
void this.waitForExit();
|
|
7789
8121
|
}
|
|
@@ -7816,22 +8148,22 @@ var PlatformStdioTransport = class {
|
|
|
7816
8148
|
}
|
|
7817
8149
|
const exited = await Promise.race([done, delay(grace)]);
|
|
7818
8150
|
if (!exited) {
|
|
7819
|
-
proc.kill("SIGTERM");
|
|
8151
|
+
await proc.kill("SIGTERM");
|
|
7820
8152
|
const exitedAfterTerm = await Promise.race([done, delay(closeTimeoutMs)]);
|
|
7821
8153
|
if (!exitedAfterTerm) {
|
|
7822
|
-
proc.kill("SIGKILL");
|
|
8154
|
+
await proc.kill("SIGKILL");
|
|
7823
8155
|
await done;
|
|
7824
8156
|
}
|
|
7825
8157
|
}
|
|
7826
|
-
proc.kill("SIGTERM");
|
|
8158
|
+
await proc.kill("SIGTERM");
|
|
7827
8159
|
this.emitClose();
|
|
7828
8160
|
}
|
|
7829
|
-
async readStdout() {
|
|
8161
|
+
async readStdout(pending) {
|
|
7830
8162
|
const proc = this.process;
|
|
7831
8163
|
if (!proc) return;
|
|
7832
8164
|
const reader = proc.stdout.getReader();
|
|
7833
8165
|
const maxBytes = this.options.maxMessageBytes ?? DEFAULT_MAX_MESSAGE_BYTES2;
|
|
7834
|
-
let buffer = "";
|
|
8166
|
+
let buffer = pending ?? "";
|
|
7835
8167
|
try {
|
|
7836
8168
|
for (; ; ) {
|
|
7837
8169
|
const { done, value } = await reader.read();
|
|
@@ -7891,6 +8223,10 @@ var PlatformStdioTransport = class {
|
|
|
7891
8223
|
await proc.wait();
|
|
7892
8224
|
} catch {
|
|
7893
8225
|
}
|
|
8226
|
+
try {
|
|
8227
|
+
await proc.kill("SIGTERM");
|
|
8228
|
+
} catch {
|
|
8229
|
+
}
|
|
7894
8230
|
if (!this.closed) {
|
|
7895
8231
|
this.emitClose();
|
|
7896
8232
|
}
|
|
@@ -8232,7 +8568,9 @@ function buildTransport(options) {
|
|
|
8232
8568
|
cwd: options.cwd,
|
|
8233
8569
|
inheritEnv: true,
|
|
8234
8570
|
platform: options.platform,
|
|
8235
|
-
onStderr: buildStderrHandler(options)
|
|
8571
|
+
onStderr: buildStderrHandler(options),
|
|
8572
|
+
existingProcess: options.existingProcess?.proc,
|
|
8573
|
+
pendingStdout: options.existingProcess?.pendingStdout
|
|
8236
8574
|
});
|
|
8237
8575
|
}
|
|
8238
8576
|
return new StdioTransport({
|
|
@@ -8419,6 +8757,9 @@ function createStdioJsonRpc(proc, platform2, options) {
|
|
|
8419
8757
|
reader.releaseLock();
|
|
8420
8758
|
} catch {
|
|
8421
8759
|
}
|
|
8760
|
+
},
|
|
8761
|
+
pendingBuffer() {
|
|
8762
|
+
return readBuffer;
|
|
8422
8763
|
}
|
|
8423
8764
|
};
|
|
8424
8765
|
}
|
|
@@ -8471,13 +8812,13 @@ function buildConnectionMethods(rpc, discoverResult2, proc) {
|
|
|
8471
8812
|
async close() {
|
|
8472
8813
|
rpc.close();
|
|
8473
8814
|
try {
|
|
8474
|
-
proc.kill("SIGTERM");
|
|
8815
|
+
await proc.kill("SIGTERM");
|
|
8475
8816
|
} catch {
|
|
8476
8817
|
}
|
|
8477
8818
|
}
|
|
8478
8819
|
};
|
|
8479
8820
|
}
|
|
8480
|
-
async function
|
|
8821
|
+
async function probeStdioStateless(options) {
|
|
8481
8822
|
const inheritedEnv = {
|
|
8482
8823
|
...options.platform.env,
|
|
8483
8824
|
...options.env
|
|
@@ -8487,22 +8828,30 @@ async function createStdioStatelessConnection(options) {
|
|
|
8487
8828
|
env: inheritedEnv,
|
|
8488
8829
|
cwd: options.cwd
|
|
8489
8830
|
});
|
|
8490
|
-
|
|
8831
|
+
trackStdio(proc);
|
|
8491
8832
|
const rpc = createStdioJsonRpc(proc, options.platform, {
|
|
8492
8833
|
version: options.version,
|
|
8493
8834
|
timeoutMs: options.timeoutMs,
|
|
8494
8835
|
signal: options.signal,
|
|
8495
8836
|
verbose: options.verbose
|
|
8496
8837
|
});
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8838
|
+
let discoverRaw;
|
|
8839
|
+
try {
|
|
8840
|
+
discoverRaw = await rpc.request("server/discover", {
|
|
8841
|
+
protocolVersion: PROTOCOL_VERSION
|
|
8842
|
+
});
|
|
8843
|
+
} catch (err) {
|
|
8844
|
+
const pending = rpc.pendingBuffer();
|
|
8845
|
+
rpc.close();
|
|
8846
|
+
return { proc, pendingStdout: pending, error: err };
|
|
8847
|
+
}
|
|
8848
|
+
void readStderr(proc, options.verbose);
|
|
8500
8849
|
const discoverResult2 = discoverRaw;
|
|
8501
8850
|
const supported = (discoverResult2?.supportedVersions ?? []).filter(
|
|
8502
8851
|
(v) => typeof v === "string"
|
|
8503
8852
|
);
|
|
8504
8853
|
if (!supported.includes(PROTOCOL_VERSION)) {
|
|
8505
|
-
proc.kill("SIGTERM");
|
|
8854
|
+
await proc.kill("SIGTERM");
|
|
8506
8855
|
throw new Error(
|
|
8507
8856
|
`Server does not support ${PROTOCOL_VERSION} (advertised: ${supported.join(", ") || "none"})`
|
|
8508
8857
|
);
|
|
@@ -8536,25 +8885,37 @@ async function connect(platform2, options) {
|
|
|
8536
8885
|
platform: platform2
|
|
8537
8886
|
};
|
|
8538
8887
|
if (options.command) {
|
|
8888
|
+
let fallbackProcess;
|
|
8539
8889
|
if (!options.protocolVersion || options.protocolVersion.startsWith("2026")) {
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8890
|
+
const result = await probeStdioStateless({
|
|
8891
|
+
command: options.command,
|
|
8892
|
+
args: options.args,
|
|
8893
|
+
env: options.env,
|
|
8894
|
+
cwd: options.cwd,
|
|
8895
|
+
platform: platform2,
|
|
8896
|
+
version: options.version,
|
|
8897
|
+
timeoutMs: options.timeoutMs,
|
|
8898
|
+
signal: options.signal,
|
|
8899
|
+
verbose: options.verbose
|
|
8900
|
+
});
|
|
8901
|
+
if (!("proc" in result)) return result;
|
|
8902
|
+
const isMethodNotFound = result.error instanceof StatelessRpcError && result.error.code === -32601;
|
|
8903
|
+
const isUnsupported = result.error.message.includes("does not support");
|
|
8904
|
+
if (options.protocolVersion?.startsWith("2026")) {
|
|
8905
|
+
try {
|
|
8906
|
+
await result.proc.kill("SIGTERM");
|
|
8907
|
+
} catch {
|
|
8908
|
+
}
|
|
8909
|
+
throw result.error;
|
|
8910
|
+
}
|
|
8911
|
+
if (!isMethodNotFound && !isUnsupported) {
|
|
8912
|
+
try {
|
|
8913
|
+
await result.proc.kill("SIGTERM");
|
|
8914
|
+
} catch {
|
|
8915
|
+
}
|
|
8916
|
+
throw result.error;
|
|
8557
8917
|
}
|
|
8918
|
+
fallbackProcess = { proc: result.proc, pendingStdout: result.pendingStdout };
|
|
8558
8919
|
}
|
|
8559
8920
|
return createPiConnection({
|
|
8560
8921
|
command: options.command,
|
|
@@ -8562,6 +8923,7 @@ async function connect(platform2, options) {
|
|
|
8562
8923
|
env: options.env,
|
|
8563
8924
|
cwd: options.cwd,
|
|
8564
8925
|
roots: options.roots,
|
|
8926
|
+
existingProcess: fallbackProcess,
|
|
8565
8927
|
...shared
|
|
8566
8928
|
});
|
|
8567
8929
|
}
|
|
@@ -9571,27 +9933,6 @@ async function loadConfig(input) {
|
|
|
9571
9933
|
};
|
|
9572
9934
|
}
|
|
9573
9935
|
|
|
9574
|
-
// src/config/paths.ts
|
|
9575
|
-
function userConfigDir(platform2) {
|
|
9576
|
-
const piDir = platform2.env.PI_CODING_AGENT_DIR;
|
|
9577
|
-
if (piDir) return piDir;
|
|
9578
|
-
const home = platform2.env.HOME ?? platform2.env.USERPROFILE ?? "/";
|
|
9579
|
-
return `${home}/.pi/agent`;
|
|
9580
|
-
}
|
|
9581
|
-
function userConfigPath(platform2) {
|
|
9582
|
-
return `${userConfigDir(platform2)}/mcp.json`;
|
|
9583
|
-
}
|
|
9584
|
-
function projectConfigPath(platform2) {
|
|
9585
|
-
return `${platform2.cwd}/.pi/mcp.json`;
|
|
9586
|
-
}
|
|
9587
|
-
function stateDir(platform2) {
|
|
9588
|
-
if (platform2.env.XDG_STATE_HOME) return `${platform2.env.XDG_STATE_HOME}/mcpcat`;
|
|
9589
|
-
const home = platform2.env.HOME ?? platform2.env.USERPROFILE ?? "/";
|
|
9590
|
-
if (platform2.env.LOCALAPPDATA) return `${platform2.env.LOCALAPPDATA}\\mcpcat\\state`;
|
|
9591
|
-
if (platform2.env.SLICC_KERNEL) return `${home}/.local/state/mcpcat`;
|
|
9592
|
-
return `${home}/Library/Application Support/mcpcat`;
|
|
9593
|
-
}
|
|
9594
|
-
|
|
9595
9936
|
// src/config/index.ts
|
|
9596
9937
|
var MergedConfigSource = class _MergedConfigSource {
|
|
9597
9938
|
entries;
|
|
@@ -9631,65 +9972,8 @@ var MergedConfigSource = class _MergedConfigSource {
|
|
|
9631
9972
|
}
|
|
9632
9973
|
};
|
|
9633
9974
|
|
|
9634
|
-
// src/config/lock.ts
|
|
9635
|
-
var LOCK_STALE_MS = 1e4;
|
|
9636
|
-
var LOCK_POLL_MS = 50;
|
|
9637
|
-
var LOCK_MAX_WAIT_MS = 5e3;
|
|
9638
|
-
function isEexist(err) {
|
|
9639
|
-
if (err instanceof Error && "code" in err)
|
|
9640
|
-
return err.code === "EEXIST";
|
|
9641
|
-
if (err instanceof Error) return err.message.includes("EEXIST");
|
|
9642
|
-
return false;
|
|
9643
|
-
}
|
|
9644
|
-
async function removeStaleLock(fs, lockPath) {
|
|
9645
|
-
try {
|
|
9646
|
-
const s = await fs.stat(lockPath);
|
|
9647
|
-
if (Date.now() - s.mtimeMs > LOCK_STALE_MS) {
|
|
9648
|
-
try {
|
|
9649
|
-
await fs.rmdir(lockPath);
|
|
9650
|
-
} catch {
|
|
9651
|
-
}
|
|
9652
|
-
return true;
|
|
9653
|
-
}
|
|
9654
|
-
} catch {
|
|
9655
|
-
}
|
|
9656
|
-
return false;
|
|
9657
|
-
}
|
|
9658
|
-
async function acquireLock(fs, configPath) {
|
|
9659
|
-
const lockPath = `${configPath}.lock`;
|
|
9660
|
-
const start = Date.now();
|
|
9661
|
-
const parentDir = lockPath.slice(0, lockPath.lastIndexOf("/"));
|
|
9662
|
-
if (parentDir) {
|
|
9663
|
-
try {
|
|
9664
|
-
await fs.mkdir(parentDir);
|
|
9665
|
-
} catch {
|
|
9666
|
-
}
|
|
9667
|
-
}
|
|
9668
|
-
while (true) {
|
|
9669
|
-
try {
|
|
9670
|
-
await fs.mkdirExclusive(lockPath);
|
|
9671
|
-
break;
|
|
9672
|
-
} catch (err) {
|
|
9673
|
-
if (!isEexist(err)) throw err;
|
|
9674
|
-
await removeStaleLock(fs, lockPath);
|
|
9675
|
-
}
|
|
9676
|
-
if (Date.now() - start > LOCK_MAX_WAIT_MS) {
|
|
9677
|
-
throw new Error(`mcpcat: could not acquire lock ${lockPath} within ${LOCK_MAX_WAIT_MS}ms`);
|
|
9678
|
-
}
|
|
9679
|
-
await new Promise((r) => setTimeout(r, LOCK_POLL_MS));
|
|
9680
|
-
}
|
|
9681
|
-
let released = false;
|
|
9682
|
-
return async () => {
|
|
9683
|
-
if (released) return;
|
|
9684
|
-
released = true;
|
|
9685
|
-
try {
|
|
9686
|
-
await fs.rmdir(lockPath);
|
|
9687
|
-
} catch {
|
|
9688
|
-
}
|
|
9689
|
-
};
|
|
9690
|
-
}
|
|
9691
|
-
|
|
9692
9975
|
// src/config/edit.ts
|
|
9976
|
+
init_lock();
|
|
9693
9977
|
function isRecord2(value) {
|
|
9694
9978
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9695
9979
|
}
|
|
@@ -11914,7 +12198,7 @@ function capOutput(value, limit) {
|
|
|
11914
12198
|
${value.slice(value.length - half)}`;
|
|
11915
12199
|
}
|
|
11916
12200
|
var inflightProcs = /* @__PURE__ */ new Set();
|
|
11917
|
-
async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs, maxOutput, cwd) {
|
|
12201
|
+
async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs, maxOutput, cwd, signal) {
|
|
11918
12202
|
let proc;
|
|
11919
12203
|
try {
|
|
11920
12204
|
proc = await platform2.spawn({
|
|
@@ -11953,23 +12237,41 @@ async function runCommand(platform2, cmdPath, argv, childEnv, stdin, timeoutMs,
|
|
|
11953
12237
|
const timer = new Promise(
|
|
11954
12238
|
(resolve) => setTimeout(() => resolve("timeout"), timeoutMs)
|
|
11955
12239
|
);
|
|
12240
|
+
const abortPromise = signal ? new Promise((resolve) => {
|
|
12241
|
+
if (signal.aborted) {
|
|
12242
|
+
resolve("timeout");
|
|
12243
|
+
return;
|
|
12244
|
+
}
|
|
12245
|
+
signal.addEventListener("abort", () => resolve("timeout"), { once: true });
|
|
12246
|
+
}) : null;
|
|
11956
12247
|
const work = Promise.all([
|
|
11957
12248
|
readStream3(proc.stdout, stdoutChunks),
|
|
11958
12249
|
readStream3(proc.stderr, stderrChunks),
|
|
11959
12250
|
proc.wait()
|
|
11960
12251
|
]);
|
|
11961
|
-
const
|
|
12252
|
+
const racers = [
|
|
12253
|
+
work,
|
|
12254
|
+
timer
|
|
12255
|
+
];
|
|
12256
|
+
if (abortPromise !== null) racers.push(abortPromise);
|
|
12257
|
+
const result = await Promise.race(racers);
|
|
11962
12258
|
if (result === "timeout") {
|
|
11963
|
-
proc.kill("SIGTERM");
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
12259
|
+
await proc.kill("SIGTERM");
|
|
12260
|
+
if (!signal?.aborted) {
|
|
12261
|
+
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
12262
|
+
try {
|
|
12263
|
+
await proc.kill("SIGKILL");
|
|
12264
|
+
} catch {
|
|
12265
|
+
}
|
|
11968
12266
|
}
|
|
11969
12267
|
inflightProcs.delete(proc);
|
|
11970
12268
|
return { stdout: "", stderr: "timed out", exitCode: 124 };
|
|
11971
12269
|
}
|
|
11972
12270
|
inflightProcs.delete(proc);
|
|
12271
|
+
try {
|
|
12272
|
+
await proc.kill("SIGTERM");
|
|
12273
|
+
} catch {
|
|
12274
|
+
}
|
|
11973
12275
|
const [, , exitResult] = result;
|
|
11974
12276
|
const dec = new TextDecoder();
|
|
11975
12277
|
return {
|
|
@@ -11988,25 +12290,31 @@ function concat(chunks) {
|
|
|
11988
12290
|
}
|
|
11989
12291
|
return out;
|
|
11990
12292
|
}
|
|
12293
|
+
var KILL_GRACE_MS = 2e3;
|
|
11991
12294
|
async function killInflight() {
|
|
11992
|
-
|
|
11993
|
-
|
|
11994
|
-
|
|
11995
|
-
|
|
11996
|
-
}
|
|
11997
|
-
}
|
|
11998
|
-
if (inflightProcs.size > 0) {
|
|
11999
|
-
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
12000
|
-
for (const proc of inflightProcs) {
|
|
12295
|
+
const procs = [...inflightProcs];
|
|
12296
|
+
if (procs.length === 0) return;
|
|
12297
|
+
await Promise.all(
|
|
12298
|
+
procs.map(async (proc) => {
|
|
12001
12299
|
try {
|
|
12002
|
-
proc.kill("
|
|
12300
|
+
await proc.kill("SIGTERM");
|
|
12003
12301
|
} catch {
|
|
12004
12302
|
}
|
|
12005
|
-
|
|
12006
|
-
|
|
12303
|
+
const exited = await Promise.race([
|
|
12304
|
+
proc.wait().then(() => true),
|
|
12305
|
+
new Promise((r) => setTimeout(() => r(false), KILL_GRACE_MS))
|
|
12306
|
+
]);
|
|
12307
|
+
if (!exited) {
|
|
12308
|
+
try {
|
|
12309
|
+
await proc.kill("SIGKILL");
|
|
12310
|
+
} catch {
|
|
12311
|
+
}
|
|
12312
|
+
}
|
|
12313
|
+
})
|
|
12314
|
+
);
|
|
12007
12315
|
inflightProcs.clear();
|
|
12008
12316
|
}
|
|
12009
|
-
async function runHelpForCli(platform2, filePath, childEnv, cwd) {
|
|
12317
|
+
async function runHelpForCli(platform2, filePath, childEnv, cwd, signal) {
|
|
12010
12318
|
const dashed = await runCommand(
|
|
12011
12319
|
platform2,
|
|
12012
12320
|
filePath,
|
|
@@ -12015,9 +12323,11 @@ async function runHelpForCli(platform2, filePath, childEnv, cwd) {
|
|
|
12015
12323
|
void 0,
|
|
12016
12324
|
1e4,
|
|
12017
12325
|
MAX_OUTPUT_DEFAULT,
|
|
12018
|
-
cwd
|
|
12326
|
+
cwd,
|
|
12327
|
+
signal
|
|
12019
12328
|
);
|
|
12020
12329
|
if (dashed.exitCode === 0 && dashed.stdout.trim()) return dashed.stdout;
|
|
12330
|
+
if (signal?.aborted) return "";
|
|
12021
12331
|
const plain = await runCommand(
|
|
12022
12332
|
platform2,
|
|
12023
12333
|
filePath,
|
|
@@ -12026,7 +12336,8 @@ async function runHelpForCli(platform2, filePath, childEnv, cwd) {
|
|
|
12026
12336
|
void 0,
|
|
12027
12337
|
1e4,
|
|
12028
12338
|
MAX_OUTPUT_DEFAULT,
|
|
12029
|
-
cwd
|
|
12339
|
+
cwd,
|
|
12340
|
+
signal
|
|
12030
12341
|
);
|
|
12031
12342
|
if (plain.exitCode === 0 && plain.stdout.trim()) return plain.stdout;
|
|
12032
12343
|
return dashed.stdout.trim() ? dashed.stdout : "";
|
|
@@ -12053,7 +12364,7 @@ async function expandGroupCommands(platform2, filePath, commands, childEnv, cwd)
|
|
|
12053
12364
|
}
|
|
12054
12365
|
return current;
|
|
12055
12366
|
}
|
|
12056
|
-
async function discoverCli(platform2, filePath, explicitName, childEnv, cwd) {
|
|
12367
|
+
async function discoverCli(platform2, filePath, explicitName, childEnv, cwd, signal) {
|
|
12057
12368
|
const mcp = await runCommand(
|
|
12058
12369
|
platform2,
|
|
12059
12370
|
filePath,
|
|
@@ -12062,10 +12373,11 @@ async function discoverCli(platform2, filePath, explicitName, childEnv, cwd) {
|
|
|
12062
12373
|
void 0,
|
|
12063
12374
|
1e4,
|
|
12064
12375
|
MAX_OUTPUT_DEFAULT,
|
|
12065
|
-
cwd
|
|
12376
|
+
cwd,
|
|
12377
|
+
signal
|
|
12066
12378
|
);
|
|
12067
12379
|
const override = mcp.exitCode === 0 ? parseMcpOverride(mcp.stdout) : null;
|
|
12068
|
-
const helpText2 = override ? "" : await runHelpForCli(platform2, filePath, childEnv, cwd);
|
|
12380
|
+
const helpText2 = override || signal?.aborted ? "" : await runHelpForCli(platform2, filePath, childEnv, cwd, signal);
|
|
12069
12381
|
if (!override && !helpText2.trim()) {
|
|
12070
12382
|
throw new Error(`${filePath} did not print help or --mcp descriptor`);
|
|
12071
12383
|
}
|
|
@@ -12185,6 +12497,32 @@ function createToolExecutor(platform2, clis, allTools, childEnv, options, serveC
|
|
|
12185
12497
|
}
|
|
12186
12498
|
return { invoke, rpcTools };
|
|
12187
12499
|
}
|
|
12500
|
+
async function discoverWithSignal(platform2, commands, childEnv, cwd) {
|
|
12501
|
+
const clis = [];
|
|
12502
|
+
let earlyShutdown = false;
|
|
12503
|
+
let inflightDone;
|
|
12504
|
+
const earlyAbort = new AbortController();
|
|
12505
|
+
const onShutdown = () => {
|
|
12506
|
+
earlyShutdown = true;
|
|
12507
|
+
earlyAbort.abort();
|
|
12508
|
+
inflightDone = killInflight();
|
|
12509
|
+
};
|
|
12510
|
+
platform2.onSignal?.("SIGTERM", onShutdown);
|
|
12511
|
+
platform2.onSignal?.("SIGINT", onShutdown);
|
|
12512
|
+
for (const cmd of commands) {
|
|
12513
|
+
try {
|
|
12514
|
+
const cli = await discoverCli(platform2, cmd.path, cmd.name, childEnv, cwd, earlyAbort.signal);
|
|
12515
|
+
clis.push(cli);
|
|
12516
|
+
} catch (err) {
|
|
12517
|
+
if (earlyShutdown) return { exitCode: 143, inflightDone };
|
|
12518
|
+
await platform2.write(2, `mcpcat serve: ${err.message}
|
|
12519
|
+
`);
|
|
12520
|
+
return { exitCode: 2 };
|
|
12521
|
+
}
|
|
12522
|
+
}
|
|
12523
|
+
if (earlyShutdown) return { exitCode: 143, inflightDone };
|
|
12524
|
+
return { clis };
|
|
12525
|
+
}
|
|
12188
12526
|
async function runServe(platform2, options, version) {
|
|
12189
12527
|
let resolvedBearer;
|
|
12190
12528
|
let generatedBearer = false;
|
|
@@ -12228,17 +12566,17 @@ async function runServe(platform2, options, version) {
|
|
|
12228
12566
|
}
|
|
12229
12567
|
const serveCwd = options.cwd ?? platform2.cwd;
|
|
12230
12568
|
const childEnv = buildChildEnv(platform2, options.envAllow);
|
|
12231
|
-
const
|
|
12232
|
-
|
|
12233
|
-
|
|
12234
|
-
|
|
12235
|
-
|
|
12236
|
-
|
|
12237
|
-
|
|
12238
|
-
`);
|
|
12239
|
-
return 2;
|
|
12569
|
+
const discovery = await discoverWithSignal(platform2, options.commands, childEnv, serveCwd);
|
|
12570
|
+
if ("exitCode" in discovery) {
|
|
12571
|
+
if (discovery.inflightDone !== void 0) {
|
|
12572
|
+
try {
|
|
12573
|
+
await discovery.inflightDone;
|
|
12574
|
+
} catch {
|
|
12575
|
+
}
|
|
12240
12576
|
}
|
|
12577
|
+
return discovery.exitCode;
|
|
12241
12578
|
}
|
|
12579
|
+
const { clis } = discovery;
|
|
12242
12580
|
const allTools = clis.flatMap((cli) => buildTools(cli.name, cli.commands));
|
|
12243
12581
|
for (const spec of options.toolTemplates) {
|
|
12244
12582
|
const tool = parseToolTemplate(spec);
|
|
@@ -12464,6 +12802,7 @@ async function runProxyHttp(platform2, options, proxy, resolvedBearer, generated
|
|
|
12464
12802
|
if (platform2.onSignal) {
|
|
12465
12803
|
platform2.onSignal("SIGINT", shutdown);
|
|
12466
12804
|
platform2.onSignal("SIGTERM", shutdown);
|
|
12805
|
+
platform2.onSignal("SIGHUP", shutdown);
|
|
12467
12806
|
}
|
|
12468
12807
|
const handler = (request) => handleProxyMcpRequest(
|
|
12469
12808
|
request,
|
|
@@ -12623,11 +12962,12 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
12623
12962
|
await platform2.write(2, `mcpcat serve: listening on ${listenUrl}
|
|
12624
12963
|
`);
|
|
12625
12964
|
let shutdownRequested = false;
|
|
12965
|
+
let inflightDone;
|
|
12626
12966
|
const shutdown = () => {
|
|
12627
12967
|
if (shutdownRequested) return;
|
|
12628
12968
|
shutdownRequested = true;
|
|
12629
12969
|
listener.close();
|
|
12630
|
-
|
|
12970
|
+
inflightDone = killInflight();
|
|
12631
12971
|
if (tokenFilePath) {
|
|
12632
12972
|
platform2.fs.unlink?.(tokenFilePath).catch(() => {
|
|
12633
12973
|
});
|
|
@@ -12636,6 +12976,7 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
12636
12976
|
if (platform2.onSignal) {
|
|
12637
12977
|
platform2.onSignal("SIGINT", shutdown);
|
|
12638
12978
|
platform2.onSignal("SIGTERM", shutdown);
|
|
12979
|
+
platform2.onSignal("SIGHUP", shutdown);
|
|
12639
12980
|
}
|
|
12640
12981
|
const handler = (request) => handleMcpRequest(
|
|
12641
12982
|
request,
|
|
@@ -12663,6 +13004,12 @@ async function runHttpServer(platform2, options, tools, call, serverName, versio
|
|
|
12663
13004
|
} catch {
|
|
12664
13005
|
if (!shutdownRequested) throw new Error("listener closed unexpectedly");
|
|
12665
13006
|
}
|
|
13007
|
+
if (inflightDone !== void 0) {
|
|
13008
|
+
try {
|
|
13009
|
+
await inflightDone;
|
|
13010
|
+
} catch {
|
|
13011
|
+
}
|
|
13012
|
+
}
|
|
12666
13013
|
return 0;
|
|
12667
13014
|
}
|
|
12668
13015
|
async function handleMcpRequest(request, tools, call, serverName, version, serverOrigins, allowedOrigins, allowedHosts, resolvedBearer) {
|
|
@@ -12934,124 +13281,6 @@ async function signIn(options) {
|
|
|
12934
13281
|
}
|
|
12935
13282
|
}
|
|
12936
13283
|
|
|
12937
|
-
// src/auth/token-source.ts
|
|
12938
|
-
function mcpNamespace(server) {
|
|
12939
|
-
return `mcp__${server.replace(/-/g, "_")}`;
|
|
12940
|
-
}
|
|
12941
|
-
function storeKey(name, serverUrl) {
|
|
12942
|
-
const ns = mcpNamespace(name);
|
|
12943
|
-
const url = String(new URL(serverUrl));
|
|
12944
|
-
return `${ns}|${url}`;
|
|
12945
|
-
}
|
|
12946
|
-
function isKernel(platform2) {
|
|
12947
|
-
return platform2.kind === "kernel";
|
|
12948
|
-
}
|
|
12949
|
-
|
|
12950
|
-
// src/auth/pi-store.ts
|
|
12951
|
-
var AUTH_FILE = "mcp-auth.json";
|
|
12952
|
-
var FILE_MODE = 384;
|
|
12953
|
-
function parseStates(raw) {
|
|
12954
|
-
if (!raw?.trim()) return {};
|
|
12955
|
-
const parsed = JSON.parse(raw);
|
|
12956
|
-
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
|
|
12957
|
-
}
|
|
12958
|
-
function serializeStates(states) {
|
|
12959
|
-
return `${JSON.stringify(states, null, 2)}
|
|
12960
|
-
`;
|
|
12961
|
-
}
|
|
12962
|
-
function legacyKey(serverUrl) {
|
|
12963
|
-
return String(new URL(serverUrl));
|
|
12964
|
-
}
|
|
12965
|
-
var PiFileTokenSource = class {
|
|
12966
|
-
platform;
|
|
12967
|
-
authPath;
|
|
12968
|
-
dirPath;
|
|
12969
|
-
constructor(platform2) {
|
|
12970
|
-
this.platform = platform2;
|
|
12971
|
-
this.dirPath = userConfigDir(platform2);
|
|
12972
|
-
this.authPath = `${this.dirPath}/${AUTH_FILE}`;
|
|
12973
|
-
}
|
|
12974
|
-
get path() {
|
|
12975
|
-
return this.authPath;
|
|
12976
|
-
}
|
|
12977
|
-
async getToken(name, serverUrl) {
|
|
12978
|
-
const state = await this.load(name, serverUrl);
|
|
12979
|
-
if (!state?.tokens?.access_token) return void 0;
|
|
12980
|
-
return {
|
|
12981
|
-
accessToken: state.tokens.access_token,
|
|
12982
|
-
expiresAt: state.tokensExpireAt
|
|
12983
|
-
};
|
|
12984
|
-
}
|
|
12985
|
-
async load(name, serverUrl) {
|
|
12986
|
-
const key = storeKey(name, serverUrl);
|
|
12987
|
-
const legacy = legacyKey(serverUrl);
|
|
12988
|
-
const release = await this.acquireLock();
|
|
12989
|
-
try {
|
|
12990
|
-
const raw = await this.readRaw();
|
|
12991
|
-
const states = parseStates(raw);
|
|
12992
|
-
if (states[key]) return states[key];
|
|
12993
|
-
if (states[legacy]) {
|
|
12994
|
-
states[key] = states[legacy];
|
|
12995
|
-
delete states[legacy];
|
|
12996
|
-
await this.writeStates(states);
|
|
12997
|
-
return states[key];
|
|
12998
|
-
}
|
|
12999
|
-
return void 0;
|
|
13000
|
-
} finally {
|
|
13001
|
-
await release();
|
|
13002
|
-
}
|
|
13003
|
-
}
|
|
13004
|
-
async save(name, serverUrl, state) {
|
|
13005
|
-
const key = storeKey(name, serverUrl);
|
|
13006
|
-
const release = await this.acquireLock();
|
|
13007
|
-
try {
|
|
13008
|
-
const raw = await this.readRaw();
|
|
13009
|
-
const states = parseStates(raw);
|
|
13010
|
-
states[key] = state;
|
|
13011
|
-
await this.writeStates(states);
|
|
13012
|
-
} finally {
|
|
13013
|
-
await release();
|
|
13014
|
-
}
|
|
13015
|
-
}
|
|
13016
|
-
async remove(name, serverUrl) {
|
|
13017
|
-
const key = storeKey(name, serverUrl);
|
|
13018
|
-
const legacy = legacyKey(serverUrl);
|
|
13019
|
-
const release = await this.acquireLock();
|
|
13020
|
-
try {
|
|
13021
|
-
const raw = await this.readRaw();
|
|
13022
|
-
const states = parseStates(raw);
|
|
13023
|
-
const stored = key in states ? key : legacy in states ? legacy : void 0;
|
|
13024
|
-
if (!stored) return false;
|
|
13025
|
-
delete states[stored];
|
|
13026
|
-
await this.writeStates(states);
|
|
13027
|
-
return true;
|
|
13028
|
-
} finally {
|
|
13029
|
-
await release();
|
|
13030
|
-
}
|
|
13031
|
-
}
|
|
13032
|
-
async acquireLock() {
|
|
13033
|
-
await this.ensureDir();
|
|
13034
|
-
return acquireLock(this.platform.fs, this.authPath);
|
|
13035
|
-
}
|
|
13036
|
-
async ensureDir() {
|
|
13037
|
-
try {
|
|
13038
|
-
await this.platform.fs.mkdir(this.dirPath);
|
|
13039
|
-
} catch {
|
|
13040
|
-
}
|
|
13041
|
-
}
|
|
13042
|
-
async readRaw() {
|
|
13043
|
-
try {
|
|
13044
|
-
const data = await this.platform.fs.readFile(this.authPath);
|
|
13045
|
-
return new TextDecoder().decode(data);
|
|
13046
|
-
} catch {
|
|
13047
|
-
return void 0;
|
|
13048
|
-
}
|
|
13049
|
-
}
|
|
13050
|
-
async writeStates(states) {
|
|
13051
|
-
await this.platform.fs.writeFile(this.authPath, serializeStates(states), { mode: FILE_MODE });
|
|
13052
|
-
}
|
|
13053
|
-
};
|
|
13054
|
-
|
|
13055
13284
|
// src/cli/auth-verbs.ts
|
|
13056
13285
|
var resolvedSource;
|
|
13057
13286
|
async function createTokenSource(platform2) {
|
|
@@ -14745,6 +14974,17 @@ async function runCall(platform2, conn, toolName, dataArg, toolArgs, opts) {
|
|
|
14745
14974
|
return EXIT_USAGE;
|
|
14746
14975
|
}
|
|
14747
14976
|
fin = mergeArgs(base, c.value);
|
|
14977
|
+
} else if (!dataArg && !opts.noValidate) {
|
|
14978
|
+
const tools = await conn.listTools();
|
|
14979
|
+
const tool = tools.find((t) => t.name === toolName);
|
|
14980
|
+
const schema = tool?.inputSchema;
|
|
14981
|
+
const required = Array.isArray(schema?.required) ? schema.required : [];
|
|
14982
|
+
for (const r of required) {
|
|
14983
|
+
if (!Object.hasOwn(base, r)) {
|
|
14984
|
+
await writeErr(platform2, opts.json ?? false, "usage", `missing required flag --${r}`);
|
|
14985
|
+
return EXIT_USAGE;
|
|
14986
|
+
}
|
|
14987
|
+
}
|
|
14748
14988
|
}
|
|
14749
14989
|
return renderCallResult(
|
|
14750
14990
|
platform2,
|
|
@@ -14962,7 +15202,10 @@ async function probeServer(_name, config, platform2) {
|
|
|
14962
15202
|
const opts = { version: "probe" };
|
|
14963
15203
|
if ("url" in config && config.url) {
|
|
14964
15204
|
opts.url = config.url;
|
|
14965
|
-
if ("headers" in config)
|
|
15205
|
+
if ("headers" in config) {
|
|
15206
|
+
const expanded = expandHeaders(config.headers, platform2.env);
|
|
15207
|
+
if (expanded) opts.headers = expanded;
|
|
15208
|
+
}
|
|
14966
15209
|
} else if ("command" in config && config.command) {
|
|
14967
15210
|
opts.command = config.command;
|
|
14968
15211
|
if ("args" in config) opts.args = config.args;
|
|
@@ -15096,6 +15339,10 @@ async function dispatchMultiTarget(platform2, parsed, version) {
|
|
|
15096
15339
|
} catch {
|
|
15097
15340
|
}
|
|
15098
15341
|
}
|
|
15342
|
+
try {
|
|
15343
|
+
await Promise.race([killAllStdio(), new Promise((r) => setTimeout(r, 3e3))]);
|
|
15344
|
+
} catch {
|
|
15345
|
+
}
|
|
15099
15346
|
platform2.exit(exitCode);
|
|
15100
15347
|
}
|
|
15101
15348
|
platform2.onSignal?.("SIGTERM", () => void killTreeAndExit(143));
|
|
@@ -15324,11 +15571,25 @@ function classifyError(err) {
|
|
|
15324
15571
|
}
|
|
15325
15572
|
async function resolveAuthProvider(platform2, hasAuthHeader, url, target, config) {
|
|
15326
15573
|
if (hasAuthHeader || !url) return void 0;
|
|
15327
|
-
const
|
|
15574
|
+
const piSource = new PiFileTokenSource(platform2);
|
|
15328
15575
|
const entry = config.entry(target);
|
|
15329
15576
|
const oauth = entry && "url" in entry.config ? entry.config.oauth : void 0;
|
|
15330
15577
|
const name = entry ? target : `url:${new URL(url).origin}${new URL(url).pathname}`;
|
|
15331
|
-
|
|
15578
|
+
const provider = createMcpAuthProvider({
|
|
15579
|
+
name,
|
|
15580
|
+
serverUrl: url,
|
|
15581
|
+
source: piSource,
|
|
15582
|
+
oauth,
|
|
15583
|
+
platform: platform2
|
|
15584
|
+
});
|
|
15585
|
+
const credentialSecrets = [];
|
|
15586
|
+
const state = await piSource.loadWithoutLock(name, url);
|
|
15587
|
+
if (state?.tokens?.access_token) credentialSecrets.push(state.tokens.access_token);
|
|
15588
|
+
if (state?.tokens?.refresh_token) credentialSecrets.push(state.tokens.refresh_token);
|
|
15589
|
+
if (state?.clientInformation && "client_secret" in state.clientInformation && state.clientInformation.client_secret) {
|
|
15590
|
+
credentialSecrets.push(state.clientInformation.client_secret);
|
|
15591
|
+
}
|
|
15592
|
+
return { provider, credentialSecrets };
|
|
15332
15593
|
}
|
|
15333
15594
|
function classifyConnectionError(err, target, interrupted) {
|
|
15334
15595
|
if (err instanceof McpAuthError || err instanceof AuthStepUpError || err instanceof JerkSignInRequiredError) {
|
|
@@ -15374,7 +15635,7 @@ async function handleInputRequired(platform2, conn, err, respondData, json) {
|
|
|
15374
15635
|
`);
|
|
15375
15636
|
return EXIT_INPUT_REQUIRED;
|
|
15376
15637
|
}
|
|
15377
|
-
const isTty = platform2.isatty(0);
|
|
15638
|
+
const isTty = platform2.isatty(0) && platform2.isatty(2);
|
|
15378
15639
|
const elicitResult = await handleElicitation(platform2, inputReq, isTty, respondData);
|
|
15379
15640
|
if ("exitCode" in elicitResult) {
|
|
15380
15641
|
await platform2.write(2, `${err.requestJson}
|
|
@@ -15420,6 +15681,10 @@ async function runClientVerb(platform2, version, parsed) {
|
|
|
15420
15681
|
} catch {
|
|
15421
15682
|
}
|
|
15422
15683
|
}
|
|
15684
|
+
try {
|
|
15685
|
+
await Promise.race([killAllStdio(), new Promise((r) => setTimeout(r, 3e3))]);
|
|
15686
|
+
} catch {
|
|
15687
|
+
}
|
|
15423
15688
|
platform2.exit(exitCode);
|
|
15424
15689
|
}
|
|
15425
15690
|
platform2.onSignal?.("SIGINT", () => {
|
|
@@ -15467,13 +15732,20 @@ async function runClientVerb(platform2, version, parsed) {
|
|
|
15467
15732
|
};
|
|
15468
15733
|
const mergedHeaders = { ...configHeaders, ...expanded };
|
|
15469
15734
|
const hasAuthHeader = "Authorization" in mergedHeaders;
|
|
15470
|
-
const
|
|
15735
|
+
const authResolved = await resolveAuthProvider(
|
|
15471
15736
|
platform2,
|
|
15472
15737
|
hasAuthHeader,
|
|
15473
15738
|
resolved.url,
|
|
15474
15739
|
target,
|
|
15475
15740
|
config
|
|
15476
15741
|
);
|
|
15742
|
+
const authProvider = authResolved?.provider;
|
|
15743
|
+
if (authResolved?.credentialSecrets) {
|
|
15744
|
+
for (const s of authResolved.credentialSecrets) {
|
|
15745
|
+
allSecrets.add(s);
|
|
15746
|
+
verbose.secrets.add(s);
|
|
15747
|
+
}
|
|
15748
|
+
}
|
|
15477
15749
|
const roots = buildRoots(parsed.roots, !!resolved.command, platform2.cwd);
|
|
15478
15750
|
let respondData;
|
|
15479
15751
|
if (parsed.respondArg) {
|
|
@@ -15570,7 +15842,18 @@ function isSpawnNotFound(err) {
|
|
|
15570
15842
|
// src/platform/host.ts
|
|
15571
15843
|
import { spawn as cpSpawn } from "node:child_process";
|
|
15572
15844
|
import { constants } from "node:fs";
|
|
15573
|
-
import {
|
|
15845
|
+
import {
|
|
15846
|
+
access,
|
|
15847
|
+
chmod,
|
|
15848
|
+
mkdir,
|
|
15849
|
+
open,
|
|
15850
|
+
readFile as readFile3,
|
|
15851
|
+
rename,
|
|
15852
|
+
rmdir,
|
|
15853
|
+
stat,
|
|
15854
|
+
unlink,
|
|
15855
|
+
writeFile
|
|
15856
|
+
} from "node:fs/promises";
|
|
15574
15857
|
import { createServer as createServer2 } from "node:net";
|
|
15575
15858
|
import { Readable, Writable } from "node:stream";
|
|
15576
15859
|
import * as tty from "node:tty";
|
|
@@ -15672,6 +15955,13 @@ var hostFs = {
|
|
|
15672
15955
|
},
|
|
15673
15956
|
async unlink(path) {
|
|
15674
15957
|
await unlink(path);
|
|
15958
|
+
},
|
|
15959
|
+
async createExclusive(path) {
|
|
15960
|
+
const fh = await open(path, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY);
|
|
15961
|
+
await fh.close();
|
|
15962
|
+
},
|
|
15963
|
+
async chmod(path, mode) {
|
|
15964
|
+
await chmod(path, mode);
|
|
15675
15965
|
}
|
|
15676
15966
|
};
|
|
15677
15967
|
function createStdinStream() {
|
|
@@ -15792,5 +16082,5 @@ try {
|
|
|
15792
16082
|
});
|
|
15793
16083
|
} catch {
|
|
15794
16084
|
}
|
|
15795
|
-
var code = await run(platform, "0.14.
|
|
16085
|
+
var code = await run(platform, "0.14.3", args);
|
|
15796
16086
|
process.exit(code);
|