@schoolai/shipyard 3.7.0 → 3.8.0-rc.20260529.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{auth-SS7LV5XK.js → auth-EXHO3AG5.js} +4 -4
- package/dist/capability-detector-worker.js +142 -0
- package/dist/capability-detector-worker.js.map +1 -0
- package/dist/{chunk-DKMDBOFU.js → chunk-2CNIEBKO.js} +21 -11
- package/dist/chunk-2CNIEBKO.js.map +1 -0
- package/dist/chunk-4T2OQAVL.js +51 -0
- package/dist/chunk-4T2OQAVL.js.map +1 -0
- package/dist/chunk-5ER6ZHA2.js +46 -0
- package/dist/chunk-5ER6ZHA2.js.map +1 -0
- package/dist/chunk-7LSEE26O.js +24227 -0
- package/dist/chunk-7LSEE26O.js.map +1 -0
- package/dist/{chunk-7AHRFPAL.js → chunk-7YOU7MBN.js} +183 -17
- package/dist/chunk-7YOU7MBN.js.map +1 -0
- package/dist/chunk-CMGJGK6R.js +382 -0
- package/dist/chunk-CMGJGK6R.js.map +1 -0
- package/dist/{chunk-VPMN47TL.js → chunk-CNR7O5YH.js} +1 -2
- package/dist/{chunk-2J3WSIAF.js → chunk-EF2DAODF.js} +18 -3
- package/dist/chunk-EF2DAODF.js.map +1 -0
- package/dist/chunk-HQ43PHOH.js +1203 -0
- package/dist/chunk-HQ43PHOH.js.map +1 -0
- package/dist/chunk-KITSAHTX.js +134 -0
- package/dist/chunk-KITSAHTX.js.map +1 -0
- package/dist/chunk-LESHN5J5.js +6898 -0
- package/dist/chunk-LESHN5J5.js.map +1 -0
- package/dist/{chunk-LW2MS4T5.js → chunk-LMJFHKRD.js} +15 -12
- package/dist/chunk-LMJFHKRD.js.map +1 -0
- package/dist/{chunk-SNYEQHUK.js → chunk-NACJENDW.js} +14 -21
- package/dist/chunk-NACJENDW.js.map +1 -0
- package/dist/{chunk-IISLTKYY.js → chunk-TU63KZFW.js} +2 -2
- package/dist/chunk-TX6DK4PK.js +186 -0
- package/dist/chunk-TX6DK4PK.js.map +1 -0
- package/dist/chunk-UQVXWOPT.js +48 -0
- package/dist/chunk-UQVXWOPT.js.map +1 -0
- package/dist/{chunk-3MNPDCO5.js → chunk-WBB4XHLH.js} +139 -140
- package/dist/chunk-WBB4XHLH.js.map +1 -0
- package/dist/chunk-X3MULCV5.js +11 -0
- package/dist/chunk-X3MULCV5.js.map +1 -0
- package/dist/chunk-YZ3Z3ZYI.js +787 -0
- package/dist/chunk-YZ3Z3ZYI.js.map +1 -0
- package/dist/{chunk-2UN5AR7V.js → chunk-ZAOPND5G.js} +2 -2
- package/dist/chunk-ZFKJAYAN.js +542 -0
- package/dist/chunk-ZFKJAYAN.js.map +1 -0
- package/dist/cursor-hook-shim.js +316 -0
- package/dist/cursor-hook-shim.js.map +1 -0
- package/dist/cursor-runner.js +358 -0
- package/dist/cursor-runner.js.map +1 -0
- package/dist/electron-utility.js +111 -0
- package/dist/electron-utility.js.map +1 -0
- package/dist/git-pool-V73Q53NX.js +18 -0
- package/dist/{git-repo-VRT57DGC.js → git-repo-TN3VZXQV.js} +9 -6
- package/dist/index.js +12 -12
- package/dist/index.js.map +1 -1
- package/dist/{logger-GQCSLSZH.js → logger-QHPTO22N.js} +4 -4
- package/dist/login-Q7SZI7JJ.js +20 -0
- package/dist/{logout-VUNCW5B2.js → logout-O4AVMO5S.js} +6 -6
- package/dist/mcp-servers-F64M5T4I.js +24 -0
- package/dist/{roi-Y3MX5UW4.js → roi-EYDLPOCS.js} +5 -5
- package/dist/rss-worker.js +159 -0
- package/dist/rss-worker.js.map +1 -0
- package/dist/{serve-O53FNK64.js → serve-6A7RJWEF.js} +89862 -102999
- package/dist/{serve-O53FNK64.js.map → serve-6A7RJWEF.js.map} +1 -1
- package/dist/skills-ZHEPSBHW.js +11 -0
- package/dist/{start-IDFDHRD6.js → start-YGYYIK53.js} +229 -27
- package/dist/start-YGYYIK53.js.map +1 -0
- package/dist/vault-crypto-BKDOA65F.js +13 -0
- package/dist/vault-crypto-BKDOA65F.js.map +1 -0
- package/dist/worker.js +6 -3
- package/dist/worker.js.map +1 -1
- package/package.json +17 -10
- package/dist/chunk-2J3WSIAF.js.map +0 -1
- package/dist/chunk-3MNPDCO5.js.map +0 -1
- package/dist/chunk-66OBOZ3X.js +0 -79
- package/dist/chunk-66OBOZ3X.js.map +0 -1
- package/dist/chunk-7AHRFPAL.js.map +0 -1
- package/dist/chunk-DKMDBOFU.js.map +0 -1
- package/dist/chunk-L2WQMPWS.js +0 -666
- package/dist/chunk-L2WQMPWS.js.map +0 -1
- package/dist/chunk-LW2MS4T5.js.map +0 -1
- package/dist/chunk-PI77CUEP.js +0 -49
- package/dist/chunk-PI77CUEP.js.map +0 -1
- package/dist/chunk-RXI4637N.js +0 -395
- package/dist/chunk-RXI4637N.js.map +0 -1
- package/dist/chunk-SNYEQHUK.js.map +0 -1
- package/dist/chunk-VBPHGPBR.js +0 -126
- package/dist/chunk-VBPHGPBR.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/login-L4BBPUYO.js +0 -20
- package/dist/mcp-servers-MXS5VAWI.js +0 -18
- package/dist/shell-V36EX2IJ.js +0 -27
- package/dist/skills-GPGRNV4R.js +0 -9
- package/dist/start-IDFDHRD6.js.map +0 -1
- package/dist/worker.d.ts +0 -49
- /package/dist/{auth-SS7LV5XK.js.map → auth-EXHO3AG5.js.map} +0 -0
- /package/dist/{chunk-VPMN47TL.js.map → chunk-CNR7O5YH.js.map} +0 -0
- /package/dist/{chunk-IISLTKYY.js.map → chunk-TU63KZFW.js.map} +0 -0
- /package/dist/{chunk-2UN5AR7V.js.map → chunk-ZAOPND5G.js.map} +0 -0
- /package/dist/{git-repo-VRT57DGC.js.map → git-pool-V73Q53NX.js.map} +0 -0
- /package/dist/{logger-GQCSLSZH.js.map → git-repo-TN3VZXQV.js.map} +0 -0
- /package/dist/{login-L4BBPUYO.js.map → logger-QHPTO22N.js.map} +0 -0
- /package/dist/{mcp-servers-MXS5VAWI.js.map → login-Q7SZI7JJ.js.map} +0 -0
- /package/dist/{logout-VUNCW5B2.js.map → logout-O4AVMO5S.js.map} +0 -0
- /package/dist/{shell-V36EX2IJ.js.map → mcp-servers-F64M5T4I.js.map} +0 -0
- /package/dist/{roi-Y3MX5UW4.js.map → roi-EYDLPOCS.js.map} +0 -0
- /package/dist/{skills-GPGRNV4R.js.map → skills-ZHEPSBHW.js.map} +0 -0
|
@@ -0,0 +1,1203 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
logger
|
|
4
|
+
} from "./chunk-ZAOPND5G.js";
|
|
5
|
+
import {
|
|
6
|
+
external_exports
|
|
7
|
+
} from "./chunk-CNR7O5YH.js";
|
|
8
|
+
|
|
9
|
+
// src/shared/capabilities/mcp-servers.ts
|
|
10
|
+
import { readFile as readFile2, stat } from "fs/promises";
|
|
11
|
+
import { homedir as homedir2 } from "os";
|
|
12
|
+
import { basename, join as join2, resolve } from "path";
|
|
13
|
+
import { parse as parseToml } from "toml";
|
|
14
|
+
|
|
15
|
+
// src/shared/mcp/claude-code-credentials.ts
|
|
16
|
+
import { execFile as execFileCb } from "child_process";
|
|
17
|
+
import { readFile } from "fs/promises";
|
|
18
|
+
import { homedir } from "os";
|
|
19
|
+
import { join } from "path";
|
|
20
|
+
import { promisify } from "util";
|
|
21
|
+
|
|
22
|
+
// src/shared/mcp/schemas.ts
|
|
23
|
+
import {
|
|
24
|
+
OAuthMetadataSchema,
|
|
25
|
+
OAuthProtectedResourceMetadataSchema,
|
|
26
|
+
OpenIdProviderDiscoveryMetadataSchema
|
|
27
|
+
} from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
28
|
+
var AuthorizationServerMetadataSchema = external_exports.union([
|
|
29
|
+
OAuthMetadataSchema,
|
|
30
|
+
OpenIdProviderDiscoveryMetadataSchema
|
|
31
|
+
]);
|
|
32
|
+
var DiscoveryStateSchema = external_exports.object({
|
|
33
|
+
authorizationServerUrl: external_exports.string(),
|
|
34
|
+
authorizationServerMetadata: AuthorizationServerMetadataSchema.optional(),
|
|
35
|
+
resourceMetadata: OAuthProtectedResourceMetadataSchema.optional(),
|
|
36
|
+
resourceMetadataUrl: external_exports.string().optional()
|
|
37
|
+
}).passthrough();
|
|
38
|
+
|
|
39
|
+
// src/shared/mcp/claude-code-credentials.ts
|
|
40
|
+
var execFile = promisify(execFileCb);
|
|
41
|
+
var ClaudeCodeOAuthEntrySchema = external_exports.object({
|
|
42
|
+
serverName: external_exports.string().nullable().optional(),
|
|
43
|
+
serverUrl: external_exports.string().nullable().optional(),
|
|
44
|
+
clientId: external_exports.string().nullable().optional(),
|
|
45
|
+
clientSecret: external_exports.string().nullable().optional(),
|
|
46
|
+
accessToken: external_exports.string().nullable().optional(),
|
|
47
|
+
refreshToken: external_exports.string().nullable().optional(),
|
|
48
|
+
expiresAt: external_exports.number().nullable().optional(),
|
|
49
|
+
discoveryState: DiscoveryStateSchema.nullable().optional()
|
|
50
|
+
}).passthrough();
|
|
51
|
+
var CredentialsFileSchema = external_exports.object({
|
|
52
|
+
mcpOAuth: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
53
|
+
}).passthrough();
|
|
54
|
+
var CREDENTIALS_PATH = join(homedir(), ".claude", ".credentials.json");
|
|
55
|
+
var KEYCHAIN_SERVICE = "Claude Code-credentials";
|
|
56
|
+
var PluginMcpJsonSchema = external_exports.record(
|
|
57
|
+
external_exports.string(),
|
|
58
|
+
external_exports.object({
|
|
59
|
+
url: external_exports.string().optional(),
|
|
60
|
+
oauth: external_exports.object({ clientId: external_exports.string(), callbackPort: external_exports.number().optional() }).optional()
|
|
61
|
+
}).passthrough()
|
|
62
|
+
);
|
|
63
|
+
async function extractClientIdsFromPlugin(installPath, result) {
|
|
64
|
+
const raw = await readFile(join(installPath, ".mcp.json"), "utf-8");
|
|
65
|
+
const json = JSON.parse(raw);
|
|
66
|
+
const outer = external_exports.record(external_exports.string(), external_exports.unknown()).safeParse(json);
|
|
67
|
+
if (!outer.success) return;
|
|
68
|
+
const servers = outer.data.mcpServers ?? outer.data;
|
|
69
|
+
const validated = PluginMcpJsonSchema.safeParse(servers);
|
|
70
|
+
if (!validated.success) return;
|
|
71
|
+
for (const config of Object.values(validated.data)) {
|
|
72
|
+
if (config.url && config.oauth?.clientId) {
|
|
73
|
+
result.set(config.url, config.oauth.clientId);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function readPluginClientIds() {
|
|
78
|
+
const result = /* @__PURE__ */ new Map();
|
|
79
|
+
try {
|
|
80
|
+
const installedPath = join(homedir(), ".claude", "plugins", "installed_plugins.json");
|
|
81
|
+
const InstalledPluginsSchema = external_exports.object({
|
|
82
|
+
plugins: external_exports.record(external_exports.string(), external_exports.array(external_exports.object({ installPath: external_exports.string().optional() }))).optional()
|
|
83
|
+
});
|
|
84
|
+
const parsed = InstalledPluginsSchema.safeParse(
|
|
85
|
+
JSON.parse(await readFile(installedPath, "utf-8"))
|
|
86
|
+
);
|
|
87
|
+
if (!parsed.success || !parsed.data.plugins) return result;
|
|
88
|
+
for (const installs of Object.values(parsed.data.plugins)) {
|
|
89
|
+
const installPath = installs?.[0]?.installPath;
|
|
90
|
+
if (!installPath) continue;
|
|
91
|
+
await extractClientIdsFromPlugin(installPath, result).catch(() => {
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
} catch {
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
async function readKeychainCredentials() {
|
|
99
|
+
if (process.platform !== "darwin") return null;
|
|
100
|
+
try {
|
|
101
|
+
const { stdout } = await execFile(
|
|
102
|
+
"security",
|
|
103
|
+
["find-generic-password", "-s", KEYCHAIN_SERVICE, "-w"],
|
|
104
|
+
{ timeout: 5e3 }
|
|
105
|
+
);
|
|
106
|
+
const result = external_exports.record(external_exports.string(), external_exports.unknown()).safeParse(JSON.parse(stdout.trim()));
|
|
107
|
+
return result.success ? result.data : null;
|
|
108
|
+
} catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function scanEntries(mcpOAuth, serverUrl) {
|
|
113
|
+
let bestWithToken = null;
|
|
114
|
+
let clientId;
|
|
115
|
+
let clientSecret;
|
|
116
|
+
let discoveryState;
|
|
117
|
+
for (const value of Object.values(mcpOAuth)) {
|
|
118
|
+
const entryResult = ClaudeCodeOAuthEntrySchema.safeParse(value);
|
|
119
|
+
if (!entryResult.success) continue;
|
|
120
|
+
const entry = entryResult.data;
|
|
121
|
+
if (entry.serverUrl !== serverUrl) continue;
|
|
122
|
+
if (entry.clientId && !clientId) {
|
|
123
|
+
clientId = entry.clientId;
|
|
124
|
+
clientSecret = entry.clientSecret || void 0;
|
|
125
|
+
}
|
|
126
|
+
if (entry.discoveryState && !discoveryState) {
|
|
127
|
+
discoveryState = entry.discoveryState;
|
|
128
|
+
}
|
|
129
|
+
if (entry.accessToken && !bestWithToken) {
|
|
130
|
+
bestWithToken = entry;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return { bestWithToken, clientId, clientSecret, discoveryState };
|
|
134
|
+
}
|
|
135
|
+
function findMatchingEntry(credentialsData, serverName, serverUrl) {
|
|
136
|
+
const fileResult = CredentialsFileSchema.safeParse(credentialsData);
|
|
137
|
+
if (!fileResult.success) return null;
|
|
138
|
+
const mcpOAuth = fileResult.data.mcpOAuth;
|
|
139
|
+
if (!mcpOAuth) return null;
|
|
140
|
+
const { bestWithToken, clientId, clientSecret, discoveryState } = scanEntries(
|
|
141
|
+
mcpOAuth,
|
|
142
|
+
serverUrl
|
|
143
|
+
);
|
|
144
|
+
if (!bestWithToken && !clientId) return null;
|
|
145
|
+
return {
|
|
146
|
+
serverName: bestWithToken?.serverName ?? serverName,
|
|
147
|
+
serverUrl,
|
|
148
|
+
clientId: bestWithToken?.clientId || clientId,
|
|
149
|
+
clientSecret: bestWithToken?.clientSecret || clientSecret || void 0,
|
|
150
|
+
accessToken: bestWithToken?.accessToken || void 0,
|
|
151
|
+
refreshToken: bestWithToken?.refreshToken || void 0,
|
|
152
|
+
expiresAt: bestWithToken?.expiresAt ?? void 0,
|
|
153
|
+
discoveryState: bestWithToken?.discoveryState ?? discoveryState ?? void 0
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
async function readClaudeCodeCredentials(serverName, serverUrl) {
|
|
157
|
+
if (!serverUrl) return null;
|
|
158
|
+
try {
|
|
159
|
+
const keychainData = await readKeychainCredentials();
|
|
160
|
+
if (keychainData) {
|
|
161
|
+
const entry = findMatchingEntry(keychainData, serverName, serverUrl);
|
|
162
|
+
if (entry) {
|
|
163
|
+
logger.debug({ serverUrl }, "Found Claude Code credentials in macOS Keychain");
|
|
164
|
+
return entry;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} catch (err) {
|
|
168
|
+
logger.debug({ err }, "Failed to read Claude Code credentials from Keychain");
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const raw = await readFile(CREDENTIALS_PATH, "utf-8");
|
|
172
|
+
const fileResult = CredentialsFileSchema.safeParse(JSON.parse(raw));
|
|
173
|
+
if (fileResult.success) {
|
|
174
|
+
const entry = findMatchingEntry(fileResult.data, serverName, serverUrl);
|
|
175
|
+
if (entry) {
|
|
176
|
+
logger.debug({ serverUrl }, "Found Claude Code credentials in legacy file");
|
|
177
|
+
return entry;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
} catch (err) {
|
|
182
|
+
logger.debug({ err }, "Failed to read Claude Code credentials from file");
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/shared/mcp/resolve-servers.ts
|
|
188
|
+
var ENV_VAR_PATTERN = /\$\{([^}]+)\}/g;
|
|
189
|
+
var VAULT_REF_PATTERN = /^\$\{vault:([^}]+)\}$/;
|
|
190
|
+
function interpolateEnvVars(headers) {
|
|
191
|
+
const result = {};
|
|
192
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
193
|
+
result[key] = value.replace(
|
|
194
|
+
ENV_VAR_PATTERN,
|
|
195
|
+
(_, varName) => process.env[varName] ?? ""
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
function resolveHeaders(server) {
|
|
201
|
+
if (!server.headers) return void 0;
|
|
202
|
+
return interpolateEnvVars(server.headers);
|
|
203
|
+
}
|
|
204
|
+
function isMcpServerCompatibleWithRuntime(server, runtimeId) {
|
|
205
|
+
if (!runtimeId) return true;
|
|
206
|
+
return server.runtimeId === void 0 || server.runtimeId === runtimeId;
|
|
207
|
+
}
|
|
208
|
+
function resolveVaultRefEnvValue(key, value, store, log) {
|
|
209
|
+
const match = VAULT_REF_PATTERN.exec(value);
|
|
210
|
+
if (!match) return void 0;
|
|
211
|
+
const vaultKey = match[1] ?? "";
|
|
212
|
+
const token = store.getTokenSync(vaultKey);
|
|
213
|
+
if (token && !store.isExpired(token)) {
|
|
214
|
+
log?.({
|
|
215
|
+
event: "vault_ref_resolved",
|
|
216
|
+
envKey: key,
|
|
217
|
+
vaultKey,
|
|
218
|
+
tokenType: token.tokenType,
|
|
219
|
+
hasExpiry: token.expiresAt !== void 0
|
|
220
|
+
});
|
|
221
|
+
return token.accessToken;
|
|
222
|
+
}
|
|
223
|
+
log?.({
|
|
224
|
+
event: "vault_ref_unresolved",
|
|
225
|
+
envKey: key,
|
|
226
|
+
vaultKey,
|
|
227
|
+
tokenFound: token !== null,
|
|
228
|
+
tokenExpired: token ? store.isExpired(token) : false
|
|
229
|
+
});
|
|
230
|
+
return value;
|
|
231
|
+
}
|
|
232
|
+
function resolveStdioEnv(env, store, log) {
|
|
233
|
+
if (!env) return void 0;
|
|
234
|
+
if (!store) return env;
|
|
235
|
+
const result = {};
|
|
236
|
+
let changed = false;
|
|
237
|
+
for (const [key, value] of Object.entries(env)) {
|
|
238
|
+
const vaultResolved = resolveVaultRefEnvValue(key, value, store, log);
|
|
239
|
+
if (vaultResolved !== void 0) {
|
|
240
|
+
result[key] = vaultResolved;
|
|
241
|
+
if (vaultResolved !== value) {
|
|
242
|
+
changed = true;
|
|
243
|
+
}
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const interpolated = value.replace(
|
|
247
|
+
ENV_VAR_PATTERN,
|
|
248
|
+
(_, varName) => process.env[varName] ?? ""
|
|
249
|
+
);
|
|
250
|
+
result[key] = interpolated;
|
|
251
|
+
if (interpolated !== value) {
|
|
252
|
+
changed = true;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return changed ? result : env;
|
|
256
|
+
}
|
|
257
|
+
function toResolvedMcpServerConfig(server, mcpTokenStore) {
|
|
258
|
+
if (server.type === "http" && server.url) {
|
|
259
|
+
return {
|
|
260
|
+
type: "http",
|
|
261
|
+
url: server.url,
|
|
262
|
+
headers: resolveHeaders(server)
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
if (server.type === "sse" && server.url) {
|
|
266
|
+
return {
|
|
267
|
+
type: "sse",
|
|
268
|
+
url: server.url,
|
|
269
|
+
headers: resolveHeaders(server)
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
if (server.command) {
|
|
273
|
+
return {
|
|
274
|
+
command: server.command,
|
|
275
|
+
args: server.args ?? void 0,
|
|
276
|
+
env: resolveStdioEnv(server.env, mcpTokenStore)
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
function resolveEnabledMcpServers(overrides, available, mcpTokenStore, runtimeId) {
|
|
282
|
+
if (!overrides || !available) return void 0;
|
|
283
|
+
const enabledNames = new Set(overrides.filter((o) => o.enabled).map((o) => o.name));
|
|
284
|
+
if (enabledNames.size === 0) return {};
|
|
285
|
+
const result = {};
|
|
286
|
+
for (const server of available) {
|
|
287
|
+
if (!isMcpServerCompatibleWithRuntime(server, runtimeId)) continue;
|
|
288
|
+
if (!enabledNames.has(server.name)) continue;
|
|
289
|
+
const config = toResolvedMcpServerConfig(server, mcpTokenStore);
|
|
290
|
+
if (config) result[server.name] = config;
|
|
291
|
+
}
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// src/shared/auth/anthropic-credentials.ts
|
|
296
|
+
import { execFile as execFileCb2 } from "child_process";
|
|
297
|
+
import { promisify as promisify2 } from "util";
|
|
298
|
+
var execFile2 = promisify2(execFileCb2);
|
|
299
|
+
var defaultLog = (entry) => logger.debug(entry, entry.event);
|
|
300
|
+
function isRecord(value) {
|
|
301
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
302
|
+
}
|
|
303
|
+
async function readKeychainEntry(service) {
|
|
304
|
+
if (process.platform !== "darwin") return null;
|
|
305
|
+
try {
|
|
306
|
+
const { stdout } = await execFile2("security", ["find-generic-password", "-s", service, "-w"], {
|
|
307
|
+
timeout: 5e3
|
|
308
|
+
});
|
|
309
|
+
const value = stdout.trim();
|
|
310
|
+
return value || null;
|
|
311
|
+
} catch {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
async function readKeychainOAuthToken(log = defaultLog) {
|
|
316
|
+
if (process.platform !== "darwin") {
|
|
317
|
+
log({ event: "anthropic_keychain_read_skipped", reason: "non_darwin" });
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
const raw = await readKeychainEntry("Claude Code-credentials");
|
|
321
|
+
if (!raw) {
|
|
322
|
+
log({ event: "anthropic_keychain_oauth_missing", reason: "no_credentials_entry" });
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
let parsed;
|
|
326
|
+
try {
|
|
327
|
+
parsed = JSON.parse(raw);
|
|
328
|
+
} catch {
|
|
329
|
+
log({ event: "anthropic_keychain_oauth_corrupt", reason: "json_parse_error" });
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
if (!isRecord(parsed)) {
|
|
333
|
+
log({ event: "anthropic_keychain_oauth_corrupt", reason: "json_parse_error" });
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
const oauth = parsed.claudeAiOauth;
|
|
337
|
+
if (!isRecord(oauth)) {
|
|
338
|
+
log({ event: "anthropic_keychain_oauth_missing", reason: "no_oauth_field" });
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
const token = oauth.accessToken;
|
|
342
|
+
if (typeof token !== "string" || token.length === 0) {
|
|
343
|
+
log({ event: "anthropic_keychain_oauth_missing", reason: "no_oauth_field" });
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
return token;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// src/shared/capabilities/runtime/catalog.ts
|
|
350
|
+
var CLAUDE_CODE_RUNTIME_ID = "claude-code";
|
|
351
|
+
var CODEX_RUNTIME_ID = "codex";
|
|
352
|
+
var CURSOR_RUNTIME_ID = "cursor";
|
|
353
|
+
var CLAUDE_RUNTIME = {
|
|
354
|
+
runtimeId: CLAUDE_CODE_RUNTIME_ID,
|
|
355
|
+
providerId: "anthropic",
|
|
356
|
+
displayName: "Claude Code",
|
|
357
|
+
buildInstallCommand(platform) {
|
|
358
|
+
if (platform === "win32") {
|
|
359
|
+
return {
|
|
360
|
+
command: "powershell",
|
|
361
|
+
args: ["-Command", "irm https://claude.ai/install.ps1 | iex"]
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
command: "bash",
|
|
366
|
+
args: ["-c", "curl -fsSL https://claude.ai/install.sh | bash"]
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
var CODEX_RUNTIME = {
|
|
371
|
+
runtimeId: CODEX_RUNTIME_ID,
|
|
372
|
+
providerId: "openai",
|
|
373
|
+
displayName: "Codex",
|
|
374
|
+
buildInstallCommand(platform) {
|
|
375
|
+
return {
|
|
376
|
+
command: platform === "win32" ? "npm.cmd" : "npm",
|
|
377
|
+
args: ["install", "-g", "@openai/codex"]
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
var CURSOR_RUNTIME = {
|
|
382
|
+
runtimeId: CURSOR_RUNTIME_ID,
|
|
383
|
+
providerId: "cursor",
|
|
384
|
+
displayName: "Cursor",
|
|
385
|
+
buildInstallCommand() {
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
var RUNTIME_DESCRIPTORS = /* @__PURE__ */ new Map([
|
|
390
|
+
[CLAUDE_RUNTIME.runtimeId, CLAUDE_RUNTIME],
|
|
391
|
+
[CODEX_RUNTIME.runtimeId, CODEX_RUNTIME],
|
|
392
|
+
[CURSOR_RUNTIME.runtimeId, CURSOR_RUNTIME]
|
|
393
|
+
]);
|
|
394
|
+
function getRuntimeDescriptor(runtimeId) {
|
|
395
|
+
return RUNTIME_DESCRIPTORS.get(runtimeId) ?? null;
|
|
396
|
+
}
|
|
397
|
+
function getRuntimeDisplayName(runtimeId) {
|
|
398
|
+
const known = getRuntimeDescriptor(runtimeId);
|
|
399
|
+
if (known) return known.displayName;
|
|
400
|
+
return runtimeId.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// src/shared/capabilities/account-integrations.ts
|
|
404
|
+
var CLAUDEAI_INTEGRATIONS_URL = "https://api.anthropic.com/v1/mcp_servers?limit=1000";
|
|
405
|
+
var ERROR_BODY_MAX_CHARS = 256;
|
|
406
|
+
function parseClaudeAiServers(data, log) {
|
|
407
|
+
if (typeof data !== "object" || data === null) return [];
|
|
408
|
+
const obj = data;
|
|
409
|
+
if (!Array.isArray(obj.data)) return [];
|
|
410
|
+
const results = [];
|
|
411
|
+
let legacyNameCount = 0;
|
|
412
|
+
for (const entry of obj.data) {
|
|
413
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
414
|
+
const rec = entry;
|
|
415
|
+
if (typeof rec.url !== "string") continue;
|
|
416
|
+
if (typeof rec.display_name === "string") {
|
|
417
|
+
results.push({ name: rec.display_name, url: rec.url });
|
|
418
|
+
} else if (typeof rec.name === "string") {
|
|
419
|
+
legacyNameCount += 1;
|
|
420
|
+
results.push({ name: rec.name, url: rec.url });
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (legacyNameCount > 0 && log) {
|
|
424
|
+
log({
|
|
425
|
+
event: "claudeai_integrations_legacy_name_fallback",
|
|
426
|
+
count: legacyNameCount
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
return results;
|
|
430
|
+
}
|
|
431
|
+
function classifyHttpStatus(status) {
|
|
432
|
+
if (status >= 400 && status < 500) return "http_4xx";
|
|
433
|
+
if (status >= 500 && status < 600) return "http_5xx";
|
|
434
|
+
return "http_other";
|
|
435
|
+
}
|
|
436
|
+
function classifyException(err) {
|
|
437
|
+
if (err instanceof Error) {
|
|
438
|
+
if (err.name === "AbortError") return "timeout";
|
|
439
|
+
if (err.name === "SyntaxError") return "parse";
|
|
440
|
+
}
|
|
441
|
+
return "network";
|
|
442
|
+
}
|
|
443
|
+
async function readErrorBody(response) {
|
|
444
|
+
try {
|
|
445
|
+
const text = await response.text();
|
|
446
|
+
if (text.length === 0) return null;
|
|
447
|
+
return text.length <= ERROR_BODY_MAX_CHARS ? text : text.slice(0, ERROR_BODY_MAX_CHARS);
|
|
448
|
+
} catch {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
async function fetchClaudeAiIntegrations(log) {
|
|
453
|
+
const url = CLAUDEAI_INTEGRATIONS_URL;
|
|
454
|
+
try {
|
|
455
|
+
const token = await readKeychainOAuthToken(log);
|
|
456
|
+
if (!token) {
|
|
457
|
+
log({ event: "claudeai_integrations_skipped", reason: "no_oauth_token" });
|
|
458
|
+
return [];
|
|
459
|
+
}
|
|
460
|
+
const controller = new AbortController();
|
|
461
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
462
|
+
try {
|
|
463
|
+
const response = await fetch(url, {
|
|
464
|
+
headers: {
|
|
465
|
+
Authorization: `Bearer ${token}`,
|
|
466
|
+
"anthropic-version": "2023-06-01",
|
|
467
|
+
"anthropic-beta": "mcp-servers-2025-12-04"
|
|
468
|
+
},
|
|
469
|
+
signal: controller.signal
|
|
470
|
+
});
|
|
471
|
+
if (!response.ok) {
|
|
472
|
+
const errorBody = await readErrorBody(response);
|
|
473
|
+
log({
|
|
474
|
+
event: "claudeai_integrations_fetch_failed",
|
|
475
|
+
url,
|
|
476
|
+
status: response.status,
|
|
477
|
+
errorClass: classifyHttpStatus(response.status),
|
|
478
|
+
errorBody
|
|
479
|
+
});
|
|
480
|
+
return [];
|
|
481
|
+
}
|
|
482
|
+
const body = await response.json();
|
|
483
|
+
const servers = parseClaudeAiServers(body, log);
|
|
484
|
+
return servers.map((s) => ({
|
|
485
|
+
name: s.name,
|
|
486
|
+
type: "http",
|
|
487
|
+
runtimeId: CLAUDE_CODE_RUNTIME_ID,
|
|
488
|
+
url: s.url,
|
|
489
|
+
enabled: true,
|
|
490
|
+
source: "claudeai",
|
|
491
|
+
authStatus: "unknown"
|
|
492
|
+
}));
|
|
493
|
+
} finally {
|
|
494
|
+
clearTimeout(timeout);
|
|
495
|
+
}
|
|
496
|
+
} catch (err) {
|
|
497
|
+
log({
|
|
498
|
+
event: "claudeai_integrations_fetch_failed",
|
|
499
|
+
url,
|
|
500
|
+
status: null,
|
|
501
|
+
errorClass: classifyException(err),
|
|
502
|
+
errorBody: null,
|
|
503
|
+
error: err instanceof Error ? err.message : String(err)
|
|
504
|
+
});
|
|
505
|
+
return [];
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// src/shared/capabilities/mcp-servers.ts
|
|
510
|
+
var MCPStdioEntrySchema = external_exports.object({
|
|
511
|
+
type: external_exports.literal("stdio").optional(),
|
|
512
|
+
command: external_exports.string(),
|
|
513
|
+
args: external_exports.array(external_exports.string()).optional(),
|
|
514
|
+
env: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
515
|
+
enabled: external_exports.boolean().optional()
|
|
516
|
+
}).passthrough();
|
|
517
|
+
var MCPOAuthConfigSchema = external_exports.object({
|
|
518
|
+
clientId: external_exports.string().optional(),
|
|
519
|
+
callbackPort: external_exports.number().optional()
|
|
520
|
+
}).passthrough();
|
|
521
|
+
var MCPHttpEntrySchema = external_exports.object({
|
|
522
|
+
type: external_exports.literal("http"),
|
|
523
|
+
url: external_exports.string(),
|
|
524
|
+
headers: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
525
|
+
oauth: MCPOAuthConfigSchema.optional(),
|
|
526
|
+
enabled: external_exports.boolean().optional()
|
|
527
|
+
}).passthrough();
|
|
528
|
+
var MCPSSEEntrySchema = external_exports.object({
|
|
529
|
+
type: external_exports.literal("sse"),
|
|
530
|
+
url: external_exports.string(),
|
|
531
|
+
headers: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
532
|
+
enabled: external_exports.boolean().optional()
|
|
533
|
+
}).passthrough();
|
|
534
|
+
var MCPServerEntrySchema = external_exports.union([MCPStdioEntrySchema, MCPHttpEntrySchema, MCPSSEEntrySchema]);
|
|
535
|
+
var CodexEnvVarSchema = external_exports.union([
|
|
536
|
+
external_exports.string(),
|
|
537
|
+
external_exports.object({
|
|
538
|
+
name: external_exports.string(),
|
|
539
|
+
source: external_exports.string().optional()
|
|
540
|
+
}).passthrough()
|
|
541
|
+
]);
|
|
542
|
+
var CodexMCPStdioEntrySchema = external_exports.object({
|
|
543
|
+
command: external_exports.string(),
|
|
544
|
+
args: external_exports.array(external_exports.string()).optional(),
|
|
545
|
+
env: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
546
|
+
env_vars: external_exports.array(CodexEnvVarSchema).optional(),
|
|
547
|
+
enabled: external_exports.boolean().optional()
|
|
548
|
+
}).passthrough();
|
|
549
|
+
var CodexMCPHttpEntrySchema = external_exports.object({
|
|
550
|
+
url: external_exports.string(),
|
|
551
|
+
http_headers: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
552
|
+
env_http_headers: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
553
|
+
bearer_token_env_var: external_exports.string().optional(),
|
|
554
|
+
enabled: external_exports.boolean().optional()
|
|
555
|
+
}).passthrough();
|
|
556
|
+
function shouldFetchClaudeAiIntegrations(preferredAuth) {
|
|
557
|
+
if (preferredAuth == null) return true;
|
|
558
|
+
return preferredAuth === "claude-ai";
|
|
559
|
+
}
|
|
560
|
+
var SECRET_PATTERNS = /^(sk-|ghp_|gho_|glpat-|xoxb-|xoxp-|Bearer\s|token\s)/i;
|
|
561
|
+
var SECRET_FLAGS = /* @__PURE__ */ new Set(["--api-key", "--token", "--secret", "--password", "--key", "-k"]);
|
|
562
|
+
function redactArgs(args) {
|
|
563
|
+
return args.map((arg, i) => {
|
|
564
|
+
if (SECRET_PATTERNS.test(arg)) return "***";
|
|
565
|
+
const prevArg = i > 0 ? args[i - 1] : void 0;
|
|
566
|
+
if (prevArg && SECRET_FLAGS.has(prevArg)) return "***";
|
|
567
|
+
const eqIdx = arg.indexOf("=");
|
|
568
|
+
if (eqIdx > 0 && SECRET_FLAGS.has(arg.slice(0, eqIdx))) {
|
|
569
|
+
return `${arg.slice(0, eqIdx + 1)}***`;
|
|
570
|
+
}
|
|
571
|
+
return arg;
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
function redactEnv(env) {
|
|
575
|
+
const result = {};
|
|
576
|
+
for (const key of Object.keys(env)) {
|
|
577
|
+
result[key] = "***";
|
|
578
|
+
}
|
|
579
|
+
return result;
|
|
580
|
+
}
|
|
581
|
+
function isHttpEntry(data) {
|
|
582
|
+
return "type" in data && data.type === "http";
|
|
583
|
+
}
|
|
584
|
+
function isSSEEntry(data) {
|
|
585
|
+
return "type" in data && data.type === "sse";
|
|
586
|
+
}
|
|
587
|
+
function entryToServerInfo(name, data, source, metadata = {}) {
|
|
588
|
+
if (isHttpEntry(data)) {
|
|
589
|
+
return {
|
|
590
|
+
name,
|
|
591
|
+
type: "http",
|
|
592
|
+
runtimeId: metadata.runtimeId,
|
|
593
|
+
url: data.url,
|
|
594
|
+
headers: data.headers,
|
|
595
|
+
oauth: data.oauth,
|
|
596
|
+
enabled: data.enabled ?? true,
|
|
597
|
+
source,
|
|
598
|
+
sourceLabel: metadata.sourceLabel,
|
|
599
|
+
sourcePath: metadata.sourcePath,
|
|
600
|
+
authStatus: "unknown"
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
if (isSSEEntry(data)) {
|
|
604
|
+
return {
|
|
605
|
+
name,
|
|
606
|
+
type: "sse",
|
|
607
|
+
runtimeId: metadata.runtimeId,
|
|
608
|
+
url: data.url,
|
|
609
|
+
headers: data.headers,
|
|
610
|
+
enabled: data.enabled ?? true,
|
|
611
|
+
source,
|
|
612
|
+
sourceLabel: metadata.sourceLabel,
|
|
613
|
+
sourcePath: metadata.sourcePath,
|
|
614
|
+
authStatus: "unknown"
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
return {
|
|
618
|
+
name,
|
|
619
|
+
type: "stdio",
|
|
620
|
+
runtimeId: metadata.runtimeId,
|
|
621
|
+
command: data.command,
|
|
622
|
+
args: data.args,
|
|
623
|
+
env: data.env,
|
|
624
|
+
enabled: data.enabled ?? true,
|
|
625
|
+
source,
|
|
626
|
+
sourceLabel: metadata.sourceLabel,
|
|
627
|
+
sourcePath: metadata.sourcePath,
|
|
628
|
+
authStatus: "unknown"
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
function codexEnvVarsToPlaceholders(envVars) {
|
|
632
|
+
if (!envVars || envVars.length === 0) return void 0;
|
|
633
|
+
const result = {};
|
|
634
|
+
for (const envVar of envVars) {
|
|
635
|
+
const name = typeof envVar === "string" ? envVar : envVar.name;
|
|
636
|
+
result[name] = `\${${name}}`;
|
|
637
|
+
}
|
|
638
|
+
return result;
|
|
639
|
+
}
|
|
640
|
+
function codexHttpHeadersToPlaceholders(envHeaders) {
|
|
641
|
+
if (!envHeaders) return void 0;
|
|
642
|
+
const result = {};
|
|
643
|
+
for (const [header, envVar] of Object.entries(envHeaders)) {
|
|
644
|
+
result[header] = `\${${envVar}}`;
|
|
645
|
+
}
|
|
646
|
+
return result;
|
|
647
|
+
}
|
|
648
|
+
function codexEntryToServerInfo(name, data, source, metadata) {
|
|
649
|
+
if (isCodexStdioEntry(data)) {
|
|
650
|
+
return {
|
|
651
|
+
name,
|
|
652
|
+
type: "stdio",
|
|
653
|
+
runtimeId: metadata.runtimeId,
|
|
654
|
+
command: data.command,
|
|
655
|
+
args: data.args,
|
|
656
|
+
env: {
|
|
657
|
+
...data.env ?? {},
|
|
658
|
+
...codexEnvVarsToPlaceholders(data.env_vars) ?? {}
|
|
659
|
+
},
|
|
660
|
+
enabled: data.enabled ?? true,
|
|
661
|
+
source,
|
|
662
|
+
sourceLabel: metadata.sourceLabel,
|
|
663
|
+
sourcePath: metadata.sourcePath,
|
|
664
|
+
authStatus: "unknown"
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
const headers = {
|
|
668
|
+
...data.http_headers ?? {},
|
|
669
|
+
...codexHttpHeadersToPlaceholders(data.env_http_headers) ?? {}
|
|
670
|
+
};
|
|
671
|
+
if (data.bearer_token_env_var) {
|
|
672
|
+
headers.Authorization = `Bearer \${${data.bearer_token_env_var}}`;
|
|
673
|
+
}
|
|
674
|
+
return {
|
|
675
|
+
name,
|
|
676
|
+
type: "http",
|
|
677
|
+
runtimeId: metadata.runtimeId,
|
|
678
|
+
url: data.url,
|
|
679
|
+
headers,
|
|
680
|
+
enabled: data.enabled ?? true,
|
|
681
|
+
source,
|
|
682
|
+
sourceLabel: metadata.sourceLabel,
|
|
683
|
+
sourcePath: metadata.sourcePath,
|
|
684
|
+
authStatus: "unknown"
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
function isCodexStdioEntry(data) {
|
|
688
|
+
return isObjectRecord(data) && typeof data.command === "string";
|
|
689
|
+
}
|
|
690
|
+
function isObjectRecord(value) {
|
|
691
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
692
|
+
}
|
|
693
|
+
function parseTomlRecord(raw) {
|
|
694
|
+
const parsed = parseToml(raw);
|
|
695
|
+
return isObjectRecord(parsed) ? parsed : {};
|
|
696
|
+
}
|
|
697
|
+
var WARN_THROTTLE_MS = 6e4;
|
|
698
|
+
var validationCache = /* @__PURE__ */ new Map();
|
|
699
|
+
var lastWarnAtByPath = /* @__PURE__ */ new Map();
|
|
700
|
+
function resetMCPConfigCaches() {
|
|
701
|
+
validationCache.clear();
|
|
702
|
+
lastWarnAtByPath.clear();
|
|
703
|
+
}
|
|
704
|
+
function validateJsonEntries(entries, source, metadata) {
|
|
705
|
+
const servers = [];
|
|
706
|
+
const invalid = [];
|
|
707
|
+
for (const [name, value] of Object.entries(entries)) {
|
|
708
|
+
if (name === "mcpServers") continue;
|
|
709
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) continue;
|
|
710
|
+
const result = MCPServerEntrySchema.safeParse(value);
|
|
711
|
+
if (!result.success) {
|
|
712
|
+
invalid.push({ name, error: result.error.message });
|
|
713
|
+
continue;
|
|
714
|
+
}
|
|
715
|
+
servers.push(entryToServerInfo(name, result.data, source, metadata));
|
|
716
|
+
}
|
|
717
|
+
return { servers, invalid };
|
|
718
|
+
}
|
|
719
|
+
function validateCodexEntries(entries, source, metadata) {
|
|
720
|
+
const servers = [];
|
|
721
|
+
const invalid = [];
|
|
722
|
+
for (const [name, value] of Object.entries(entries)) {
|
|
723
|
+
if (!isObjectRecord(value)) continue;
|
|
724
|
+
const result = "command" in value ? CodexMCPStdioEntrySchema.safeParse(value) : "url" in value ? CodexMCPHttpEntrySchema.safeParse(value) : { success: false, error: { message: "invalid transport" } };
|
|
725
|
+
if (!result.success) {
|
|
726
|
+
invalid.push({ name, error: result.error.message });
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
servers.push(codexEntryToServerInfo(name, result.data, source, metadata));
|
|
730
|
+
}
|
|
731
|
+
return { servers, invalid };
|
|
732
|
+
}
|
|
733
|
+
function emitAggregateInvalid(filePath, source, invalid, log, now) {
|
|
734
|
+
const payload = {
|
|
735
|
+
event: "config_entry_invalid",
|
|
736
|
+
source,
|
|
737
|
+
filePath,
|
|
738
|
+
invalidCount: invalid.length,
|
|
739
|
+
errors: invalid.slice(0, 3)
|
|
740
|
+
};
|
|
741
|
+
const last = lastWarnAtByPath.get(filePath) ?? 0;
|
|
742
|
+
if (now - last >= WARN_THROTTLE_MS) {
|
|
743
|
+
log(payload);
|
|
744
|
+
lastWarnAtByPath.set(filePath, now);
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
logger.debug(payload, payload.event);
|
|
748
|
+
}
|
|
749
|
+
async function readCachedMcpConfig(filePath, source, log, parse, now = Date.now) {
|
|
750
|
+
let mtime;
|
|
751
|
+
try {
|
|
752
|
+
const st = await stat(filePath);
|
|
753
|
+
mtime = st.mtimeMs;
|
|
754
|
+
} catch {
|
|
755
|
+
return [];
|
|
756
|
+
}
|
|
757
|
+
const cached = validationCache.get(filePath);
|
|
758
|
+
if (cached && cached.mtime === mtime) {
|
|
759
|
+
return cached.result;
|
|
760
|
+
}
|
|
761
|
+
let servers = [];
|
|
762
|
+
try {
|
|
763
|
+
const raw = await readFile2(filePath, "utf-8");
|
|
764
|
+
const parsed = parse(raw);
|
|
765
|
+
servers = parsed.servers;
|
|
766
|
+
if (parsed.invalid.length > 0) {
|
|
767
|
+
emitAggregateInvalid(filePath, source, parsed.invalid, log, now());
|
|
768
|
+
}
|
|
769
|
+
} catch {
|
|
770
|
+
servers = [];
|
|
771
|
+
}
|
|
772
|
+
validationCache.set(filePath, { mtime, result: servers });
|
|
773
|
+
return servers;
|
|
774
|
+
}
|
|
775
|
+
async function readMCPConfig(filePath, source, log, metadataOrNow = {}, now = Date.now) {
|
|
776
|
+
const metadata = typeof metadataOrNow === "function" ? {} : metadataOrNow;
|
|
777
|
+
const effectiveNow = typeof metadataOrNow === "function" ? metadataOrNow : now;
|
|
778
|
+
return readCachedMcpConfig(
|
|
779
|
+
filePath,
|
|
780
|
+
source,
|
|
781
|
+
log,
|
|
782
|
+
(raw) => {
|
|
783
|
+
const json = JSON.parse(raw);
|
|
784
|
+
const fileName = basename(filePath);
|
|
785
|
+
const isSettingsFile = fileName === "settings.json" || fileName === "settings.local.json";
|
|
786
|
+
let entries;
|
|
787
|
+
if (isObjectRecord(json.mcpServers)) {
|
|
788
|
+
entries = json.mcpServers;
|
|
789
|
+
} else if (isSettingsFile) {
|
|
790
|
+
entries = null;
|
|
791
|
+
} else {
|
|
792
|
+
entries = json;
|
|
793
|
+
}
|
|
794
|
+
return entries === null ? { servers: [], invalid: [] } : validateJsonEntries(entries, source, metadata);
|
|
795
|
+
},
|
|
796
|
+
effectiveNow
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
async function readCodexMCPConfig(filePath, source, log, metadata, now = Date.now) {
|
|
800
|
+
return readCachedMcpConfig(
|
|
801
|
+
filePath,
|
|
802
|
+
source,
|
|
803
|
+
log,
|
|
804
|
+
(raw) => {
|
|
805
|
+
const parsed = parseTomlRecord(raw);
|
|
806
|
+
return isObjectRecord(parsed.mcp_servers) ? validateCodexEntries(parsed.mcp_servers, source, metadata) : { servers: [], invalid: [] };
|
|
807
|
+
},
|
|
808
|
+
now
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
async function readPluginMCPServers(log) {
|
|
812
|
+
const servers = [];
|
|
813
|
+
try {
|
|
814
|
+
const settingsPath = join2(homedir2(), ".claude", "settings.json");
|
|
815
|
+
const settingsRaw = await readFile2(settingsPath, "utf-8");
|
|
816
|
+
const settings = JSON.parse(settingsRaw);
|
|
817
|
+
if (!settings.enabledPlugins) return [];
|
|
818
|
+
const installedPath = join2(homedir2(), ".claude", "plugins", "installed_plugins.json");
|
|
819
|
+
const installedRaw = await readFile2(installedPath, "utf-8");
|
|
820
|
+
const installed = JSON.parse(installedRaw);
|
|
821
|
+
if (!installed.plugins) return [];
|
|
822
|
+
for (const [pluginId, enabled] of Object.entries(settings.enabledPlugins)) {
|
|
823
|
+
if (!enabled) continue;
|
|
824
|
+
const installs = installed.plugins[pluginId];
|
|
825
|
+
if (!installs || installs.length === 0) continue;
|
|
826
|
+
const installPath = installs[0]?.installPath;
|
|
827
|
+
if (!installPath) continue;
|
|
828
|
+
const mcpJsonPath = join2(installPath, ".mcp.json");
|
|
829
|
+
const pluginServers = await readMCPConfig(mcpJsonPath, "plugin", log, {
|
|
830
|
+
sourceLabel: "Plugin",
|
|
831
|
+
sourcePath: "~/.claude/plugins/"
|
|
832
|
+
});
|
|
833
|
+
servers.push(...pluginServers);
|
|
834
|
+
}
|
|
835
|
+
} catch {
|
|
836
|
+
}
|
|
837
|
+
return servers;
|
|
838
|
+
}
|
|
839
|
+
var CodexMarketplaceEntrySchema = external_exports.object({
|
|
840
|
+
source: external_exports.string(),
|
|
841
|
+
source_type: external_exports.string().optional()
|
|
842
|
+
});
|
|
843
|
+
var CodexMarketplacesSchema = external_exports.record(external_exports.string(), CodexMarketplaceEntrySchema.passthrough());
|
|
844
|
+
var CodexPluginEntrySchema = external_exports.object({
|
|
845
|
+
enabled: external_exports.boolean().optional()
|
|
846
|
+
});
|
|
847
|
+
var CodexPluginsSchema = external_exports.record(external_exports.string(), CodexPluginEntrySchema.passthrough());
|
|
848
|
+
var CodexConfigTomlSchema = external_exports.object({
|
|
849
|
+
mcp_servers: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
850
|
+
marketplaces: CodexMarketplacesSchema.optional(),
|
|
851
|
+
plugins: CodexPluginsSchema.optional()
|
|
852
|
+
});
|
|
853
|
+
var MarketplaceJsonPluginSourceSchema = external_exports.object({
|
|
854
|
+
source: external_exports.string(),
|
|
855
|
+
path: external_exports.string()
|
|
856
|
+
});
|
|
857
|
+
var MarketplaceJsonPluginSchema = external_exports.object({
|
|
858
|
+
name: external_exports.string(),
|
|
859
|
+
source: MarketplaceJsonPluginSourceSchema.optional()
|
|
860
|
+
});
|
|
861
|
+
var MarketplaceJsonSchema = external_exports.object({
|
|
862
|
+
name: external_exports.string().optional(),
|
|
863
|
+
plugins: external_exports.array(MarketplaceJsonPluginSchema)
|
|
864
|
+
});
|
|
865
|
+
var CodexPluginManifestSchema = external_exports.object({
|
|
866
|
+
mcpServers: external_exports.string().optional(),
|
|
867
|
+
skills: external_exports.string().optional()
|
|
868
|
+
});
|
|
869
|
+
var CodexPluginMcpServersFileSchema = external_exports.object({
|
|
870
|
+
mcpServers: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
871
|
+
});
|
|
872
|
+
async function readCodexTomlConfig(configPath) {
|
|
873
|
+
try {
|
|
874
|
+
const raw = await readFile2(configPath, "utf-8");
|
|
875
|
+
const parsed = parseTomlRecord(raw);
|
|
876
|
+
const result = CodexConfigTomlSchema.safeParse(parsed);
|
|
877
|
+
return result.success ? result.data : null;
|
|
878
|
+
} catch {
|
|
879
|
+
return null;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
async function readMarketplaceJson(marketplaceSourceDir) {
|
|
883
|
+
const candidates = [
|
|
884
|
+
join2(marketplaceSourceDir, ".agents", "plugins", "marketplace.json"),
|
|
885
|
+
join2(marketplaceSourceDir, "marketplace.json")
|
|
886
|
+
];
|
|
887
|
+
for (const candidate of candidates) {
|
|
888
|
+
try {
|
|
889
|
+
const raw = await readFile2(candidate, "utf-8");
|
|
890
|
+
const parsed = JSON.parse(raw);
|
|
891
|
+
const result = MarketplaceJsonSchema.safeParse(parsed);
|
|
892
|
+
if (result.success) return result.data;
|
|
893
|
+
} catch {
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
return null;
|
|
897
|
+
}
|
|
898
|
+
async function readCodexPluginManifestMcps(pluginRoot, pluginId, marketplaceName, log) {
|
|
899
|
+
const manifestPath = join2(pluginRoot, ".codex-plugin", "plugin.json");
|
|
900
|
+
try {
|
|
901
|
+
const raw = await readFile2(manifestPath, "utf-8");
|
|
902
|
+
const parsed = JSON.parse(raw);
|
|
903
|
+
const manifestResult = CodexPluginManifestSchema.safeParse(parsed);
|
|
904
|
+
if (!manifestResult.success) {
|
|
905
|
+
log({
|
|
906
|
+
event: "codex_plugin_manifest_invalid",
|
|
907
|
+
pluginId,
|
|
908
|
+
marketplaceName,
|
|
909
|
+
error: manifestResult.error.message
|
|
910
|
+
});
|
|
911
|
+
return [];
|
|
912
|
+
}
|
|
913
|
+
const manifest = manifestResult.data;
|
|
914
|
+
if (!manifest.mcpServers) return [];
|
|
915
|
+
const mcpFilePath = resolve(pluginRoot, manifest.mcpServers);
|
|
916
|
+
try {
|
|
917
|
+
const mcpRaw = await readFile2(mcpFilePath, "utf-8");
|
|
918
|
+
const mcpParsed = JSON.parse(mcpRaw);
|
|
919
|
+
const mcpResult = CodexPluginMcpServersFileSchema.safeParse(mcpParsed);
|
|
920
|
+
if (!mcpResult.success || !mcpResult.data.mcpServers) return [];
|
|
921
|
+
const sourceLabel = `Codex Plugin (${pluginId})`;
|
|
922
|
+
const sourcePath = `~/.codex/plugins/${pluginId}`;
|
|
923
|
+
const { servers, invalid } = validateCodexEntries(mcpResult.data.mcpServers, "plugin", {
|
|
924
|
+
runtimeId: CODEX_RUNTIME_ID,
|
|
925
|
+
sourceLabel,
|
|
926
|
+
sourcePath
|
|
927
|
+
});
|
|
928
|
+
if (invalid.length > 0) {
|
|
929
|
+
log({
|
|
930
|
+
event: "codex_plugin_mcp_entries_invalid",
|
|
931
|
+
pluginId,
|
|
932
|
+
marketplaceName,
|
|
933
|
+
invalidCount: invalid.length,
|
|
934
|
+
errors: invalid.slice(0, 3)
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
return servers;
|
|
938
|
+
} catch {
|
|
939
|
+
log({ event: "codex_plugin_mcp_file_unreadable", pluginId, marketplaceName, mcpFilePath });
|
|
940
|
+
return [];
|
|
941
|
+
}
|
|
942
|
+
} catch {
|
|
943
|
+
return [];
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
async function detectCodexPluginMcps(codexHome, configToml, log) {
|
|
947
|
+
const marketplaces = configToml.marketplaces;
|
|
948
|
+
if (!marketplaces || Object.keys(marketplaces).length === 0) return [];
|
|
949
|
+
const pluginEnabledMap = configToml.plugins ?? {};
|
|
950
|
+
const allServers = [];
|
|
951
|
+
await Promise.all(
|
|
952
|
+
Object.entries(marketplaces).map(async ([marketplaceName, marketplaceEntry]) => {
|
|
953
|
+
if (marketplaceEntry.source_type && marketplaceEntry.source_type !== "local") return;
|
|
954
|
+
const marketplaceSourceDir = marketplaceEntry.source;
|
|
955
|
+
if (!marketplaceSourceDir) return;
|
|
956
|
+
const marketplaceJson = await readMarketplaceJson(marketplaceSourceDir);
|
|
957
|
+
if (!marketplaceJson) {
|
|
958
|
+
log({ event: "codex_marketplace_json_missing", marketplaceName, marketplaceSourceDir });
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
await Promise.all(
|
|
962
|
+
marketplaceJson.plugins.map(async (plugin) => {
|
|
963
|
+
if (!plugin.source || plugin.source.source !== "local") return;
|
|
964
|
+
const pluginId = `${plugin.name}@${marketplaceName}`;
|
|
965
|
+
const pluginEnabledEntry = pluginEnabledMap[pluginId];
|
|
966
|
+
if (pluginEnabledEntry?.enabled === false) return;
|
|
967
|
+
const pluginRoot = resolve(marketplaceSourceDir, plugin.source.path);
|
|
968
|
+
const servers = await readCodexPluginManifestMcps(
|
|
969
|
+
pluginRoot,
|
|
970
|
+
pluginId,
|
|
971
|
+
marketplaceName,
|
|
972
|
+
log
|
|
973
|
+
);
|
|
974
|
+
allServers.push(...servers);
|
|
975
|
+
})
|
|
976
|
+
);
|
|
977
|
+
})
|
|
978
|
+
);
|
|
979
|
+
if (allServers.length > 0) {
|
|
980
|
+
log({ event: "codex_plugin_mcps_detected", count: allServers.length, codexHome });
|
|
981
|
+
}
|
|
982
|
+
return allServers;
|
|
983
|
+
}
|
|
984
|
+
function pushCandidates(target, servers, priority) {
|
|
985
|
+
for (const server of servers) {
|
|
986
|
+
target.push({ server, priority });
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
async function resolveClaudeCodeAuthStatuses(servers) {
|
|
990
|
+
for (const server of servers.values()) {
|
|
991
|
+
if (server.authStatus !== "unknown") continue;
|
|
992
|
+
if (server.type === "stdio") continue;
|
|
993
|
+
const ccCreds = await readClaudeCodeCredentials(server.name, server.url ?? "");
|
|
994
|
+
if (ccCreds?.accessToken && ccCreds.expiresAt && ccCreds.expiresAt > Date.now()) {
|
|
995
|
+
server.authStatus = "authenticated";
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
function resolveStdioAuthStatus(server, tokens, tokenStore) {
|
|
1000
|
+
if (!server.env) return;
|
|
1001
|
+
for (const value of Object.values(server.env)) {
|
|
1002
|
+
const match = VAULT_REF_PATTERN.exec(value);
|
|
1003
|
+
if (!match) continue;
|
|
1004
|
+
const vaultKey = match[1] ?? "";
|
|
1005
|
+
const token = tokens[vaultKey];
|
|
1006
|
+
if (token) {
|
|
1007
|
+
server.authStatus = tokenStore.isExpired(token) ? "unauthenticated" : "authenticated";
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
async function resolveAuthStatuses(servers, tokenStore) {
|
|
1013
|
+
const tokens = await tokenStore.getAllTokens();
|
|
1014
|
+
for (const server of servers.values()) {
|
|
1015
|
+
if (server.type === "stdio") {
|
|
1016
|
+
resolveStdioAuthStatus(server, tokens, tokenStore);
|
|
1017
|
+
continue;
|
|
1018
|
+
}
|
|
1019
|
+
const token = tokens[server.name];
|
|
1020
|
+
if (token) {
|
|
1021
|
+
server.authStatus = tokenStore.isExpired(token) ? "unauthenticated" : "authenticated";
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
await resolveClaudeCodeAuthStatuses(servers);
|
|
1025
|
+
}
|
|
1026
|
+
async function detectMCPServers(environments, tokenStore, log, lastKnown, activeRuntimeId, preferredAuth, changedCwd) {
|
|
1027
|
+
try {
|
|
1028
|
+
return await detectMCPServersInner(
|
|
1029
|
+
environments,
|
|
1030
|
+
tokenStore,
|
|
1031
|
+
log,
|
|
1032
|
+
activeRuntimeId,
|
|
1033
|
+
preferredAuth,
|
|
1034
|
+
changedCwd,
|
|
1035
|
+
lastKnown
|
|
1036
|
+
);
|
|
1037
|
+
} catch (err) {
|
|
1038
|
+
const { logger: logger2 } = await import("./logger-QHPTO22N.js");
|
|
1039
|
+
if (lastKnown && lastKnown.length > 0) {
|
|
1040
|
+
logger2.warn(
|
|
1041
|
+
{ err, lastKnownCount: lastKnown.length },
|
|
1042
|
+
"detectMCPServers threw \u2014 preserving lastKnown"
|
|
1043
|
+
);
|
|
1044
|
+
return lastKnown;
|
|
1045
|
+
}
|
|
1046
|
+
logger2.debug({ err }, "detectMCPServers threw with no lastKnown \u2014 returning []");
|
|
1047
|
+
return [];
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
function runtimeScopeLabel(runtimeId, scope) {
|
|
1051
|
+
return `${getRuntimeDisplayName(runtimeId)} ${scope}`;
|
|
1052
|
+
}
|
|
1053
|
+
var SOURCE_PRIORITY_FOR_LASTKNOWN = {
|
|
1054
|
+
claudeai: 10,
|
|
1055
|
+
plugin: 20,
|
|
1056
|
+
user: 30,
|
|
1057
|
+
project: 50,
|
|
1058
|
+
local: 60,
|
|
1059
|
+
"mcp-json": 70
|
|
1060
|
+
};
|
|
1061
|
+
function seedLastKnownCandidates(target, lastKnown) {
|
|
1062
|
+
if (!lastKnown) return;
|
|
1063
|
+
for (const server of lastKnown) {
|
|
1064
|
+
target.push({ server, priority: SOURCE_PRIORITY_FOR_LASTKNOWN[server.source] });
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
function readEnvProjectMcpFiles(env, log) {
|
|
1068
|
+
const projectPath = join2(env.path, ".claude", "settings.json");
|
|
1069
|
+
const localPath = join2(env.path, ".claude", "settings.local.json");
|
|
1070
|
+
const codexProjectPath = join2(env.path, ".codex", "config.toml");
|
|
1071
|
+
const mcpJsonPath = join2(env.path, ".mcp.json");
|
|
1072
|
+
return Promise.all([
|
|
1073
|
+
readMCPConfig(projectPath, "project", log, {
|
|
1074
|
+
runtimeId: CLAUDE_CODE_RUNTIME_ID,
|
|
1075
|
+
sourceLabel: runtimeScopeLabel(CLAUDE_CODE_RUNTIME_ID, "Project"),
|
|
1076
|
+
sourcePath: ".claude/settings.json"
|
|
1077
|
+
}),
|
|
1078
|
+
readMCPConfig(localPath, "local", log, {
|
|
1079
|
+
runtimeId: CLAUDE_CODE_RUNTIME_ID,
|
|
1080
|
+
sourceLabel: runtimeScopeLabel(CLAUDE_CODE_RUNTIME_ID, "Local"),
|
|
1081
|
+
sourcePath: ".claude/settings.local.json"
|
|
1082
|
+
}),
|
|
1083
|
+
readCodexMCPConfig(codexProjectPath, "project", log, {
|
|
1084
|
+
runtimeId: CODEX_RUNTIME_ID,
|
|
1085
|
+
sourceLabel: "Codex Project",
|
|
1086
|
+
sourcePath: ".codex/config.toml"
|
|
1087
|
+
}),
|
|
1088
|
+
readMCPConfig(mcpJsonPath, "mcp-json", log, {
|
|
1089
|
+
sourceLabel: "Project",
|
|
1090
|
+
sourcePath: ".mcp.json"
|
|
1091
|
+
})
|
|
1092
|
+
]);
|
|
1093
|
+
}
|
|
1094
|
+
async function detectMCPServersInner(environments, tokenStore, log, activeRuntimeId, preferredAuth, changedCwd, lastKnown) {
|
|
1095
|
+
const allCandidates = [];
|
|
1096
|
+
if (changedCwd) seedLastKnownCandidates(allCandidates, lastKnown);
|
|
1097
|
+
const userSettingsPath = join2(homedir2(), ".claude", "settings.json");
|
|
1098
|
+
const userLocalSettingsPath = join2(homedir2(), ".claude", "settings.local.json");
|
|
1099
|
+
const userCodexConfigPath = join2(homedir2(), ".codex", "config.toml");
|
|
1100
|
+
const userMcpJsonPath = join2(homedir2(), ".mcp.json");
|
|
1101
|
+
const userClaudeJsonPath = join2(homedir2(), ".claude.json");
|
|
1102
|
+
const codexHome = join2(homedir2(), ".codex");
|
|
1103
|
+
const fetchClaudeAi = shouldFetchClaudeAiIntegrations(preferredAuth);
|
|
1104
|
+
const claudeAiPromise = fetchClaudeAi ? fetchClaudeAiIntegrations(log) : Promise.resolve([]);
|
|
1105
|
+
if (!fetchClaudeAi) {
|
|
1106
|
+
log({ event: "claudeai_integrations_skipped_by_method", method: preferredAuth ?? null });
|
|
1107
|
+
}
|
|
1108
|
+
const codexUserConfig = await readCodexTomlConfig(userCodexConfigPath);
|
|
1109
|
+
const [
|
|
1110
|
+
userServers,
|
|
1111
|
+
userLocalServers,
|
|
1112
|
+
userClaudeJsonServers,
|
|
1113
|
+
userCodexServers,
|
|
1114
|
+
userMcpJsonServers,
|
|
1115
|
+
pluginServers,
|
|
1116
|
+
codexPluginServers,
|
|
1117
|
+
claudeAiServers
|
|
1118
|
+
] = await Promise.all([
|
|
1119
|
+
readMCPConfig(userSettingsPath, "user", log, {
|
|
1120
|
+
runtimeId: CLAUDE_CODE_RUNTIME_ID,
|
|
1121
|
+
sourceLabel: runtimeScopeLabel(CLAUDE_CODE_RUNTIME_ID, "User"),
|
|
1122
|
+
sourcePath: "~/.claude/settings.json"
|
|
1123
|
+
}),
|
|
1124
|
+
readMCPConfig(userLocalSettingsPath, "user", log, {
|
|
1125
|
+
runtimeId: CLAUDE_CODE_RUNTIME_ID,
|
|
1126
|
+
sourceLabel: runtimeScopeLabel(CLAUDE_CODE_RUNTIME_ID, "Local"),
|
|
1127
|
+
sourcePath: "~/.claude/settings.local.json"
|
|
1128
|
+
}),
|
|
1129
|
+
readMCPConfig(userClaudeJsonPath, "user", log, {
|
|
1130
|
+
runtimeId: CLAUDE_CODE_RUNTIME_ID,
|
|
1131
|
+
sourceLabel: runtimeScopeLabel(CLAUDE_CODE_RUNTIME_ID, "User"),
|
|
1132
|
+
sourcePath: "~/.claude.json"
|
|
1133
|
+
}),
|
|
1134
|
+
readCodexMCPConfig(userCodexConfigPath, "user", log, {
|
|
1135
|
+
runtimeId: "codex",
|
|
1136
|
+
sourceLabel: "Codex User",
|
|
1137
|
+
sourcePath: "~/.codex/config.toml"
|
|
1138
|
+
}),
|
|
1139
|
+
readMCPConfig(userMcpJsonPath, "user", log, {
|
|
1140
|
+
sourceLabel: "User",
|
|
1141
|
+
sourcePath: "~/.mcp.json"
|
|
1142
|
+
}),
|
|
1143
|
+
readPluginMCPServers(log),
|
|
1144
|
+
codexUserConfig ? detectCodexPluginMcps(codexHome, codexUserConfig, log) : Promise.resolve([]),
|
|
1145
|
+
claudeAiPromise
|
|
1146
|
+
]);
|
|
1147
|
+
pushCandidates(allCandidates, claudeAiServers, 10);
|
|
1148
|
+
pushCandidates(allCandidates, pluginServers, 20);
|
|
1149
|
+
pushCandidates(allCandidates, codexPluginServers, 25);
|
|
1150
|
+
pushCandidates(allCandidates, userServers, 30);
|
|
1151
|
+
pushCandidates(allCandidates, userLocalServers, 31);
|
|
1152
|
+
pushCandidates(allCandidates, userClaudeJsonServers, 32);
|
|
1153
|
+
pushCandidates(allCandidates, userCodexServers, 33);
|
|
1154
|
+
pushCandidates(allCandidates, userMcpJsonServers, 40);
|
|
1155
|
+
const envsToScan = changedCwd ? environments.filter((env) => env.path === changedCwd) : environments;
|
|
1156
|
+
const envResults = await Promise.all(envsToScan.map((env) => readEnvProjectMcpFiles(env, log)));
|
|
1157
|
+
for (const [projectServers, localServers, codexProjectServers, mcpJsonServers] of envResults) {
|
|
1158
|
+
pushCandidates(allCandidates, projectServers, 50);
|
|
1159
|
+
pushCandidates(allCandidates, localServers, 60);
|
|
1160
|
+
pushCandidates(allCandidates, codexProjectServers, 65);
|
|
1161
|
+
pushCandidates(allCandidates, mcpJsonServers, 70);
|
|
1162
|
+
}
|
|
1163
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
1164
|
+
for (const candidate of allCandidates) {
|
|
1165
|
+
if (activeRuntimeId && candidate.server.runtimeId && candidate.server.runtimeId !== activeRuntimeId) {
|
|
1166
|
+
continue;
|
|
1167
|
+
}
|
|
1168
|
+
const existing = deduped.get(candidate.server.name);
|
|
1169
|
+
if (!existing || candidate.priority >= existing.priority) {
|
|
1170
|
+
deduped.set(candidate.server.name, candidate);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
if (tokenStore) {
|
|
1174
|
+
await resolveAuthStatuses(
|
|
1175
|
+
new Map([...deduped.entries()].map(([name, candidate]) => [name, candidate.server])),
|
|
1176
|
+
tokenStore
|
|
1177
|
+
);
|
|
1178
|
+
}
|
|
1179
|
+
return [...deduped.values()].map((candidate) => candidate.server);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
export {
|
|
1183
|
+
DiscoveryStateSchema,
|
|
1184
|
+
readPluginClientIds,
|
|
1185
|
+
readClaudeCodeCredentials,
|
|
1186
|
+
isMcpServerCompatibleWithRuntime,
|
|
1187
|
+
resolveStdioEnv,
|
|
1188
|
+
resolveEnabledMcpServers,
|
|
1189
|
+
readKeychainOAuthToken,
|
|
1190
|
+
CLAUDE_CODE_RUNTIME_ID,
|
|
1191
|
+
CODEX_RUNTIME_ID,
|
|
1192
|
+
CURSOR_RUNTIME_ID,
|
|
1193
|
+
getRuntimeDescriptor,
|
|
1194
|
+
getRuntimeDisplayName,
|
|
1195
|
+
shouldFetchClaudeAiIntegrations,
|
|
1196
|
+
redactArgs,
|
|
1197
|
+
redactEnv,
|
|
1198
|
+
resetMCPConfigCaches,
|
|
1199
|
+
readMCPConfig,
|
|
1200
|
+
detectCodexPluginMcps,
|
|
1201
|
+
detectMCPServers
|
|
1202
|
+
};
|
|
1203
|
+
//# sourceMappingURL=chunk-HQ43PHOH.js.map
|