@wuyax/mcps 0.1.0-beta.2 → 0.1.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.
@@ -0,0 +1,1898 @@
1
+ // src/agents.ts
2
+ import { existsSync } from "fs";
3
+ import { homedir, platform } from "os";
4
+ import { join } from "path";
5
+ var home = homedir();
6
+ var getPlatformPaths = () => {
7
+ const currentPlatform = platform();
8
+ if (currentPlatform === "win32") {
9
+ const appData = process.env.APPDATA || join(home, "AppData", "Roaming");
10
+ return {
11
+ appSupport: appData,
12
+ vscodePath: join(appData, "Code", "User"),
13
+ traePath: join(appData, "Trae", "User"),
14
+ gooseConfigPath: join(appData, "Block", "goose", "config", "config.yaml"),
15
+ zedConfigPath: join(appData, "Zed", "settings.json")
16
+ };
17
+ }
18
+ if (currentPlatform === "darwin") {
19
+ return {
20
+ appSupport: join(home, "Library", "Application Support"),
21
+ vscodePath: join(home, "Library", "Application Support", "Code", "User"),
22
+ traePath: join(home, "Library", "Application Support", "Trae", "User"),
23
+ gooseConfigPath: join(home, ".config", "goose", "config.yaml"),
24
+ zedConfigPath: join(home, ".config", "zed", "settings.json")
25
+ };
26
+ }
27
+ const configDir = process.env.XDG_CONFIG_HOME || join(home, ".config");
28
+ return {
29
+ appSupport: configDir,
30
+ vscodePath: join(configDir, "Code", "User"),
31
+ traePath: join(configDir, "Trae", "User"),
32
+ gooseConfigPath: join(configDir, "goose", "config.yaml"),
33
+ zedConfigPath: join(configDir, "zed", "settings.json")
34
+ };
35
+ };
36
+ var { appSupport, vscodePath, traePath, gooseConfigPath, zedConfigPath } = getPlatformPaths();
37
+ var ampConfigDir = process.env.AMP_HOME?.trim() || join(process.env.XDG_CONFIG_HOME || join(home, ".config"), "amp");
38
+ var ampGlobalConfigPath = existsSync(join(ampConfigDir, "settings.jsonc")) ? join(ampConfigDir, "settings.jsonc") : join(ampConfigDir, "settings.json");
39
+ var antigravityMcpConfigPath = join(home, ".gemini", "config", "mcp_config.json");
40
+ var augmentConfigDir = process.env.AUGMENT_HOME?.trim() || join(home, ".augment");
41
+ var augmentGlobalConfigPath = existsSync(join(augmentConfigDir, "settings.jsonc")) ? join(augmentConfigDir, "settings.jsonc") : join(augmentConfigDir, "settings.json");
42
+ var clineDir = process.env.CLINE_DIR || join(home, ".cline");
43
+ var clineCliConfigPath = join(clineDir, "mcp.json");
44
+ var clineExtensionConfigPath = join(
45
+ vscodePath,
46
+ "globalStorage",
47
+ "saoudrizwan.claude-dev",
48
+ "settings",
49
+ "cline_mcp_settings.json"
50
+ );
51
+ var copilotConfigPath = join(
52
+ process.env.COPILOT_HOME?.trim() || join(home, ".copilot"),
53
+ "mcp-config.json"
54
+ );
55
+ var grokConfigPath = join(
56
+ process.env.GROK_HOME?.trim() || join(home, ".grok"),
57
+ "config.toml"
58
+ );
59
+ var kimiCodeConfigPath = join(
60
+ process.env.KIMI_CODE_HOME?.trim() || join(home, ".kimi-code"),
61
+ "mcp.json"
62
+ );
63
+ var kiroConfigPath = join(
64
+ process.env.KIRO_HOME?.trim() || join(home, ".kiro"),
65
+ "settings",
66
+ "mcp.json"
67
+ );
68
+ var qoderConfigPath = join(
69
+ process.env.QODER_HOME?.trim() || join(home, ".qoder"),
70
+ "settings.json"
71
+ );
72
+ var qwenCodeConfigPath = join(
73
+ process.env.QWEN_CODE_HOME?.trim() || process.env.QWEN_HOME?.trim() || join(home, ".qwen"),
74
+ "settings.json"
75
+ );
76
+ var traeConfigPath = existsSync(join(home, ".trae", "mcp.json")) ? join(home, ".trae", "mcp.json") : join(traePath, "mcp.json");
77
+ var ALL_TRANSPORTS = ["stdio", "http", "sse"];
78
+ var mcpAgents = {
79
+ // https://ampcode.com/docs/markdown/customize/mcp
80
+ amp: {
81
+ name: "amp",
82
+ displayName: "Amp",
83
+ globalConfigPath: ampGlobalConfigPath,
84
+ projectConfigPath: ".amp/settings.json",
85
+ configKey: "amp.mcpServers",
86
+ format: "jsonc",
87
+ supportedTransports: ALL_TRANSPORTS,
88
+ detectGlobalInstall: () => existsSync(ampConfigDir) || existsSync(join(home, ".amp")),
89
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".amp", "settings.json")) || existsSync(join(cwd, ".amp", "settings.jsonc")) || existsSync(join(cwd, ".amp")),
90
+ resolveConfigPath: ({ global: isGlobal, cwd }) => {
91
+ if (isGlobal) {
92
+ if (existsSync(join(ampConfigDir, "settings.jsonc"))) {
93
+ return join(ampConfigDir, "settings.jsonc");
94
+ }
95
+ return ampGlobalConfigPath;
96
+ }
97
+ if (existsSync(join(cwd, ".amp", "settings.jsonc"))) {
98
+ return join(cwd, ".amp", "settings.jsonc");
99
+ }
100
+ return join(cwd, ".amp", "settings.json");
101
+ },
102
+ transformDialect: "amp"
103
+ },
104
+ antigravity: {
105
+ name: "antigravity",
106
+ displayName: "Antigravity",
107
+ globalConfigPath: antigravityMcpConfigPath,
108
+ projectConfigPath: ".agents/mcp_config.json",
109
+ configKey: "mcpServers",
110
+ format: "jsonc",
111
+ supportedTransports: ALL_TRANSPORTS,
112
+ detectGlobalInstall: () => existsSync(join(home, ".gemini", "antigravity")) || existsSync(join(home, ".gemini", "config")),
113
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".agents", "mcp_config.json")) || existsSync(join(cwd, ".agents"))
114
+ },
115
+ // https://antigravity.google/docs/cli/mcp/
116
+ "antigravity-cli": {
117
+ name: "antigravity-cli",
118
+ displayName: "Antigravity CLI",
119
+ globalConfigPath: antigravityMcpConfigPath,
120
+ projectConfigPath: ".agents/mcp_config.json",
121
+ configKey: "mcpServers",
122
+ format: "jsonc",
123
+ supportedTransports: ALL_TRANSPORTS,
124
+ detectGlobalInstall: () => existsSync(join(home, ".gemini", "antigravity-cli")),
125
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".agents", "mcp_config.json")) || existsSync(join(cwd, ".agents"))
126
+ },
127
+ // https://docs.augmentcode.com/cli/integrations.md
128
+ augment: {
129
+ name: "augment",
130
+ displayName: "Augment",
131
+ globalConfigPath: augmentGlobalConfigPath,
132
+ projectConfigPath: ".augment/settings.json",
133
+ configKey: "mcpServers",
134
+ format: "jsonc",
135
+ supportedTransports: ALL_TRANSPORTS,
136
+ detectGlobalInstall: () => existsSync(augmentConfigDir) || existsSync(join(home, ".augment")),
137
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".augment", "settings.json")) || existsSync(join(cwd, ".augment", "settings.jsonc")) || existsSync(join(cwd, ".augment")),
138
+ resolveConfigPath: ({ global: isGlobal, cwd }) => {
139
+ if (isGlobal) {
140
+ if (existsSync(join(augmentConfigDir, "settings.jsonc"))) {
141
+ return join(augmentConfigDir, "settings.jsonc");
142
+ }
143
+ return augmentGlobalConfigPath;
144
+ }
145
+ if (existsSync(join(cwd, ".augment", "settings.jsonc"))) {
146
+ return join(cwd, ".augment", "settings.jsonc");
147
+ }
148
+ return join(cwd, ".augment", "settings.json");
149
+ },
150
+ transformDialect: "augment"
151
+ },
152
+ cline: {
153
+ name: "cline",
154
+ displayName: "Cline (VSCode extension)",
155
+ globalConfigPath: clineExtensionConfigPath,
156
+ projectConfigPath: ".cline/mcp.json",
157
+ configKey: "mcpServers",
158
+ format: "jsonc",
159
+ supportedTransports: ALL_TRANSPORTS,
160
+ detectGlobalInstall: () => existsSync(clineExtensionConfigPath),
161
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".cline", "mcp.json")) || existsSync(join(cwd, ".cline")),
162
+ resolveConfigPath: ({ global: isGlobal, cwd }) => {
163
+ if (isGlobal) {
164
+ return clineExtensionConfigPath;
165
+ }
166
+ return join(cwd, ".cline", "mcp.json");
167
+ },
168
+ transformDialect: "cline"
169
+ },
170
+ // https://docs.cline.bot/mcp/mcp-overview.md
171
+ "cline-cli": {
172
+ name: "cline-cli",
173
+ displayName: "Cline CLI",
174
+ globalConfigPath: clineCliConfigPath,
175
+ projectConfigPath: ".cline/mcp.json",
176
+ configKey: "mcpServers",
177
+ format: "jsonc",
178
+ supportedTransports: ALL_TRANSPORTS,
179
+ detectGlobalInstall: () => existsSync(clineDir),
180
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".cline", "mcp.json")) || existsSync(join(cwd, ".cline")),
181
+ resolveConfigPath: ({ global: isGlobal, cwd }) => {
182
+ if (isGlobal) {
183
+ return clineCliConfigPath;
184
+ }
185
+ return join(cwd, ".cline", "mcp.json");
186
+ },
187
+ transformDialect: "cline"
188
+ },
189
+ // https://code.claude.com/docs/en/mcp-quickstart#edit-mcp-json-directly
190
+ "claude-code": {
191
+ name: "claude-code",
192
+ displayName: "Claude Code",
193
+ globalConfigPath: join(home, ".claude.json"),
194
+ projectConfigPath: ".mcp.json",
195
+ configKey: "mcpServers",
196
+ format: "jsonc",
197
+ supportedTransports: ALL_TRANSPORTS,
198
+ detectGlobalInstall: () => existsSync(join(home, ".claude.json")),
199
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".mcp.json"))
200
+ },
201
+ "claude-desktop": {
202
+ name: "claude-desktop",
203
+ displayName: "Claude Desktop",
204
+ globalConfigPath: join(appSupport, "Claude", "claude_desktop_config.json"),
205
+ configKey: "mcpServers",
206
+ format: "jsonc",
207
+ supportedTransports: ["stdio"],
208
+ unsupportedTransportMessage: "Claude Desktop currently supports only stdio MCP servers. Use a package name or command instead of a URL.",
209
+ detectGlobalInstall: () => existsSync(join(appSupport, "Claude", "claude_desktop_config.json"))
210
+ },
211
+ // https://learn.chatgpt.com/docs/extend/mcp?surface=app
212
+ codex: {
213
+ name: "codex",
214
+ displayName: "Codex",
215
+ globalConfigPath: join(process.env.CODEX_HOME?.trim() || join(home, ".codex"), "config.toml"),
216
+ projectConfigPath: ".codex/config.toml",
217
+ configKey: "mcp_servers",
218
+ format: "toml",
219
+ supportedTransports: ALL_TRANSPORTS,
220
+ detectGlobalInstall: () => existsSync(process.env.CODEX_HOME?.trim() || join(home, ".codex")),
221
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".codex", "config.toml")),
222
+ transformDialect: "augment"
223
+ },
224
+ // https://cursor.com/help/customization/mcp
225
+ cursor: {
226
+ name: "cursor",
227
+ displayName: "Cursor",
228
+ globalConfigPath: join(home, ".cursor", "mcp.json"),
229
+ projectConfigPath: ".cursor/mcp.json",
230
+ configKey: "mcpServers",
231
+ format: "jsonc",
232
+ supportedTransports: ALL_TRANSPORTS,
233
+ detectGlobalInstall: () => existsSync(join(home, ".cursor")),
234
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".cursor", "mcp.json"))
235
+ },
236
+ "gemini-cli": {
237
+ name: "gemini-cli",
238
+ displayName: "Gemini CLI",
239
+ globalConfigPath: join(home, ".gemini", "settings.json"),
240
+ projectConfigPath: ".gemini/settings.json",
241
+ configKey: "mcpServers",
242
+ format: "jsonc",
243
+ supportedTransports: ALL_TRANSPORTS,
244
+ detectGlobalInstall: () => existsSync(join(home, ".gemini")),
245
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".gemini", "settings.json"))
246
+ },
247
+ // https://docs.x.ai/build/features/mcp-servers.md
248
+ grok: {
249
+ name: "grok",
250
+ displayName: "Grok",
251
+ globalConfigPath: grokConfigPath,
252
+ projectConfigPath: ".grok/config.toml",
253
+ configKey: "mcp_servers",
254
+ format: "toml",
255
+ supportedTransports: ALL_TRANSPORTS,
256
+ detectGlobalInstall: () => existsSync(grokConfigPath) || existsSync(process.env.GROK_HOME?.trim() || join(home, ".grok")),
257
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".grok", "config.toml")) || existsSync(join(cwd, ".grok")),
258
+ transformDialect: "grok"
259
+ },
260
+ // https://goose-docs.ai/docs/guides/config-files/
261
+ goose: {
262
+ name: "goose",
263
+ displayName: "Goose",
264
+ globalConfigPath: gooseConfigPath,
265
+ projectConfigPath: ".goose/config.yaml",
266
+ configKey: "extensions",
267
+ format: "yaml",
268
+ supportedTransports: ALL_TRANSPORTS,
269
+ detectGlobalInstall: () => existsSync(gooseConfigPath),
270
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".goose", "config.yaml")),
271
+ transformDialect: "goose"
272
+ },
273
+ "github-copilot-cli": {
274
+ name: "github-copilot-cli",
275
+ displayName: "GitHub Copilot CLI",
276
+ globalConfigPath: copilotConfigPath,
277
+ projectConfigPath: ".mcp.json",
278
+ configKey: "mcpServers",
279
+ format: "jsonc",
280
+ supportedTransports: ALL_TRANSPORTS,
281
+ detectGlobalInstall: () => existsSync(copilotConfigPath) || existsSync(process.env.COPILOT_HOME?.trim() || join(home, ".copilot")),
282
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".mcp.json")) || existsSync(join(cwd, ".github", "mcp.json")),
283
+ transformDialect: "vscode"
284
+ },
285
+ // https://www.kimi.com/code/docs/en/kimi-code-cli/customization/mcp.html
286
+ "kimi-code-cli": {
287
+ name: "kimi-code-cli",
288
+ displayName: "Kimi Code CLI",
289
+ globalConfigPath: kimiCodeConfigPath,
290
+ projectConfigPath: ".kimi-code/mcp.json",
291
+ configKey: "mcpServers",
292
+ format: "jsonc",
293
+ supportedTransports: ALL_TRANSPORTS,
294
+ detectGlobalInstall: () => existsSync(kimiCodeConfigPath) || existsSync(process.env.KIMI_CODE_HOME?.trim() || join(home, ".kimi-code")),
295
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".kimi-code", "mcp.json")) || existsSync(join(cwd, ".kimi-code")),
296
+ transformDialect: "kimi-code"
297
+ },
298
+ // https://kiro.dev/docs/mcp/configuration.md
299
+ kiro: {
300
+ name: "kiro",
301
+ displayName: "Kiro",
302
+ globalConfigPath: kiroConfigPath,
303
+ projectConfigPath: ".kiro/settings/mcp.json",
304
+ configKey: "mcpServers",
305
+ format: "jsonc",
306
+ supportedTransports: ALL_TRANSPORTS,
307
+ detectGlobalInstall: () => existsSync(kiroConfigPath) || existsSync(process.env.KIRO_HOME?.trim() || join(home, ".kiro")),
308
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".kiro", "settings", "mcp.json")) || existsSync(join(cwd, ".kiro")),
309
+ transformDialect: "kiro"
310
+ },
311
+ // https://opencode.ai/docs/config/
312
+ opencode: {
313
+ name: "opencode",
314
+ displayName: "OpenCode",
315
+ globalConfigPath: join(
316
+ process.env.XDG_CONFIG_HOME || join(home, ".config"),
317
+ "opencode",
318
+ "opencode.json"
319
+ ),
320
+ projectConfigPath: "opencode.json",
321
+ configKey: "mcp",
322
+ format: "jsonc",
323
+ supportedTransports: ALL_TRANSPORTS,
324
+ detectGlobalInstall: () => existsSync(join(process.env.XDG_CONFIG_HOME || join(home, ".config"), "opencode")),
325
+ detectProjectInstall: (cwd) => existsSync(join(cwd, "opencode.json")),
326
+ transformDialect: "opencode"
327
+ },
328
+ // https://pi.dev/packages/pi-mcp-extension
329
+ pi: {
330
+ name: "pi",
331
+ displayName: "Pi",
332
+ globalConfigPath: join(home, ".pi", "agent", "mcp.json"),
333
+ projectConfigPath: ".pi/mcp.json",
334
+ configKey: "mcpServers",
335
+ format: "jsonc",
336
+ supportedTransports: ALL_TRANSPORTS,
337
+ detectGlobalInstall: () => existsSync(join(home, ".pi")),
338
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".pi", "mcp.json")) || existsSync(join(cwd, ".pi")),
339
+ transformDialect: "pi"
340
+ },
341
+ // https://docs.qoder.com/zh/cli/mcp-servers.md
342
+ qoder: {
343
+ name: "qoder",
344
+ displayName: "Qoder",
345
+ globalConfigPath: qoderConfigPath,
346
+ projectConfigPath: ".mcp.json",
347
+ configKey: "mcpServers",
348
+ format: "jsonc",
349
+ supportedTransports: ALL_TRANSPORTS,
350
+ detectGlobalInstall: () => existsSync(qoderConfigPath) || existsSync(process.env.QODER_HOME?.trim() || join(home, ".qoder")),
351
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".mcp.json")) || existsSync(join(cwd, ".qoder", "settings.json")) || existsSync(join(cwd, ".qoder")),
352
+ resolveConfigPath: ({ global: isGlobal, cwd }) => {
353
+ if (isGlobal) return qoderConfigPath;
354
+ if (existsSync(join(cwd, ".qoder", "settings.json"))) {
355
+ return join(cwd, ".qoder", "settings.json");
356
+ }
357
+ return join(cwd, ".mcp.json");
358
+ }
359
+ },
360
+ // https://qwenlm.github.io/qwen-code-docs/en/users/features/mcp/
361
+ "qwen-code": {
362
+ name: "qwen-code",
363
+ displayName: "Qwen Code",
364
+ globalConfigPath: qwenCodeConfigPath,
365
+ projectConfigPath: ".qwen/settings.json",
366
+ configKey: "mcpServers",
367
+ format: "jsonc",
368
+ supportedTransports: ALL_TRANSPORTS,
369
+ detectGlobalInstall: () => existsSync(qwenCodeConfigPath) || existsSync(
370
+ process.env.QWEN_CODE_HOME?.trim() || process.env.QWEN_HOME?.trim() || join(home, ".qwen")
371
+ ),
372
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".qwen", "settings.json")) || existsSync(join(cwd, ".qwen")),
373
+ transformDialect: "qwen-code"
374
+ },
375
+ // https://docs.trae.ai/ide/add-mcp-servers?_lang=en
376
+ trae: {
377
+ name: "trae",
378
+ displayName: "Trae",
379
+ globalConfigPath: traeConfigPath,
380
+ projectConfigPath: ".trae/mcp.json",
381
+ configKey: "mcpServers",
382
+ format: "jsonc",
383
+ supportedTransports: ALL_TRANSPORTS,
384
+ detectGlobalInstall: () => existsSync(traeConfigPath) || existsSync(join(home, ".trae", "mcp.json")) || existsSync(join(home, ".trae")) || existsSync(traePath) || existsSync(join(appSupport, "Trae")),
385
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".trae", "mcp.json")) || existsSync(join(cwd, ".trae")),
386
+ resolveConfigPath: ({ global: isGlobal, cwd }) => {
387
+ if (isGlobal) {
388
+ if (existsSync(join(home, ".trae", "mcp.json"))) {
389
+ return join(home, ".trae", "mcp.json");
390
+ }
391
+ return traeConfigPath;
392
+ }
393
+ return join(cwd, ".trae", "mcp.json");
394
+ },
395
+ transformDialect: "trae"
396
+ },
397
+ // https://code.visualstudio.com/raw/docs/agents/reference/mcp-configuration.md
398
+ vscode: {
399
+ name: "vscode",
400
+ displayName: "VS Code",
401
+ globalConfigPath: join(vscodePath, "mcp.json"),
402
+ projectConfigPath: ".vscode/mcp.json",
403
+ configKey: "servers",
404
+ format: "jsonc",
405
+ supportedTransports: ALL_TRANSPORTS,
406
+ detectGlobalInstall: () => existsSync(join(vscodePath, "mcp.json")) || existsSync(vscodePath),
407
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".vscode", "mcp.json")),
408
+ transformDialect: "vscode"
409
+ },
410
+ // https://zed.dev/docs/ai/mcp.md
411
+ zed: {
412
+ name: "zed",
413
+ displayName: "Zed",
414
+ globalConfigPath: zedConfigPath,
415
+ projectConfigPath: ".zed/settings.json",
416
+ configKey: "context_servers",
417
+ format: "jsonc",
418
+ supportedTransports: ALL_TRANSPORTS,
419
+ detectGlobalInstall: () => existsSync(zedConfigPath) || existsSync(join(process.env.XDG_CONFIG_HOME || join(home, ".config"), "zed")) || existsSync(join(appSupport, "Zed")),
420
+ detectProjectInstall: (cwd) => existsSync(join(cwd, ".zed", "settings.json")),
421
+ transformDialect: "zed"
422
+ }
423
+ };
424
+ var mcpAgentAliases = {
425
+ agy: "antigravity-cli",
426
+ "amp-cli": "amp",
427
+ "amp-code": "amp",
428
+ ampcode: "amp",
429
+ auggie: "augment",
430
+ "augment-code": "augment",
431
+ augmentcode: "augment",
432
+ "cline-vscode": "cline",
433
+ gemini: "gemini-cli",
434
+ "github-copilot": "vscode",
435
+ "grok-cli": "grok",
436
+ kimi: "kimi-code-cli",
437
+ "kimi-cli": "kimi-code-cli",
438
+ "kimi-code": "kimi-code-cli",
439
+ "kiro-cli": "kiro",
440
+ "kiro-ide": "kiro",
441
+ "pi-agent": "pi",
442
+ "qoder-cli": "qoder",
443
+ qwen: "qwen-code",
444
+ "qwen-cli": "qwen-code",
445
+ qwencode: "qwen-code",
446
+ "trae-code": "trae",
447
+ traecode: "trae",
448
+ "trae-ide": "trae",
449
+ xai: "grok",
450
+ "xai-grok": "grok"
451
+ };
452
+ var getMcpAgentConfig = (agentType) => mcpAgents[agentType];
453
+ var getMcpAgentTypes = () => Object.values(mcpAgents).map((config) => config.name);
454
+ var isMcpAgentType = (value) => value in mcpAgents;
455
+ var resolveMcpAgentAlias = (input) => {
456
+ if (isMcpAgentType(input)) return input;
457
+ return mcpAgentAliases[input] ?? null;
458
+ };
459
+ var isMcpTransportSupported = (agent, transport) => agent.supportedTransports.includes(transport);
460
+ var detectProjectInstalledMcpAgents = (cwd) => getMcpAgentTypes().filter(
461
+ (type) => mcpAgents[type].detectProjectInstall ? mcpAgents[type].detectProjectInstall(cwd) : false
462
+ );
463
+ var detectGloballyInstalledMcpAgents = () => getMcpAgentTypes().filter((type) => mcpAgents[type].detectGlobalInstall());
464
+ var getMcpAgentsSupportingProjectScope = () => getMcpAgentTypes().filter((type) => Boolean(mcpAgents[type].projectConfigPath));
465
+
466
+ // src/constants.ts
467
+ var DEFAULT_REMOTE_TRANSPORT = "http";
468
+ var NPX_COMMAND = "npx";
469
+ var NPX_DASH_Y = "-y";
470
+ var GOOSE_TIMEOUT_SECONDS = 300;
471
+ var DEFAULT_JSON_INDENT_SPACES = 2;
472
+ var MCP_DEFAULT_SERVER_NAME = "mcp-server";
473
+ var GENERIC_HOST_PREFIXES = /* @__PURE__ */ new Set([
474
+ "mcp",
475
+ "api",
476
+ "app",
477
+ "www",
478
+ "server",
479
+ "servers",
480
+ "remote"
481
+ ]);
482
+ var COMMON_TLD_LABELS = /* @__PURE__ */ new Set([
483
+ "com",
484
+ "org",
485
+ "net",
486
+ "io",
487
+ "dev",
488
+ "ai",
489
+ "tech",
490
+ "co",
491
+ "app",
492
+ "cloud",
493
+ "sh",
494
+ "run"
495
+ ]);
496
+ var PACKAGE_NAME_PREFIX_STRIP = ["mcp-server-", "server-"];
497
+ var PACKAGE_NAME_SUFFIX_STRIP = ["-mcp-server", "-mcp"];
498
+ var KNOWN_COMMAND_RUNNERS = /* @__PURE__ */ new Set([
499
+ "npx",
500
+ "node",
501
+ "python",
502
+ "python3",
503
+ "uvx",
504
+ "bunx",
505
+ "deno"
506
+ ]);
507
+ var SCRIPT_EXTENSION_REGEX = /\.(?:js|ts|mjs|cjs|py|sh|rb|go)$/i;
508
+
509
+ // src/server-config.ts
510
+ var isRemoteServerConfig = (config) => typeof config.url === "string" && config.url.length > 0;
511
+ var isStdioServerConfig = (config) => typeof config.command === "string" && config.command.length > 0;
512
+ var buildMcpServerConfig = (parsed, options = {}) => {
513
+ if (parsed.type === "remote") {
514
+ const config2 = {
515
+ type: options.transport ?? DEFAULT_REMOTE_TRANSPORT,
516
+ url: parsed.value
517
+ };
518
+ if (options.headers && Object.keys(options.headers).length > 0) {
519
+ config2.headers = options.headers;
520
+ }
521
+ return config2;
522
+ }
523
+ if (parsed.type === "command") {
524
+ const parts = parsed.value.split(/\s+/);
525
+ const command = parts[0] ?? "";
526
+ const args = parts.slice(1);
527
+ if (options.args && options.args.length > 0) {
528
+ args.push(...options.args);
529
+ }
530
+ const config2 = { command, args };
531
+ if (options.env && Object.keys(options.env).length > 0) {
532
+ config2.env = options.env;
533
+ }
534
+ return config2;
535
+ }
536
+ const packageArgs = [NPX_DASH_Y, parsed.value];
537
+ if (options.args && options.args.length > 0) {
538
+ packageArgs.push(...options.args);
539
+ }
540
+ const config = {
541
+ command: NPX_COMMAND,
542
+ args: packageArgs
543
+ };
544
+ if (options.env && Object.keys(options.env).length > 0) {
545
+ config.env = options.env;
546
+ }
547
+ return config;
548
+ };
549
+ var parseServerConfig = (raw) => {
550
+ if (!raw || typeof raw !== "object") return {};
551
+ const data = raw;
552
+ const rawUrl = typeof data.url === "string" && data.url.trim().length > 0 ? data.url.trim() : void 0;
553
+ const rawHttpUrl = typeof data.httpUrl === "string" && data.httpUrl.trim().length > 0 ? data.httpUrl.trim() : void 0;
554
+ const remoteUrl = rawHttpUrl ?? rawUrl;
555
+ if (remoteUrl) {
556
+ const transport = data.type === "sse" || data.transport === "sse" ? "sse" : "http";
557
+ const headers = data.headers && typeof data.headers === "object" ? data.headers : void 0;
558
+ return {
559
+ type: transport,
560
+ url: remoteUrl,
561
+ headers
562
+ };
563
+ }
564
+ if (typeof data.command === "string" && data.command.trim().length > 0) {
565
+ const args = Array.isArray(data.args) ? data.args.filter((item) => typeof item === "string") : void 0;
566
+ const env = data.env && typeof data.env === "object" ? data.env : void 0;
567
+ return {
568
+ command: data.command.trim(),
569
+ args,
570
+ env
571
+ };
572
+ }
573
+ return {};
574
+ };
575
+ var toRemoteServerConfig = (config, defaultTransport = "http") => {
576
+ const {
577
+ command: _droppedCommand,
578
+ args: _droppedArgs,
579
+ env: _droppedEnv,
580
+ ...remoteConfig
581
+ } = config;
582
+ return {
583
+ ...remoteConfig,
584
+ type: remoteConfig.type ?? defaultTransport
585
+ };
586
+ };
587
+ var toStdioServerConfig = (config) => {
588
+ const {
589
+ url: _droppedUrl,
590
+ type: _droppedType,
591
+ headers: _droppedHeaders,
592
+ ...stdioConfig
593
+ } = config;
594
+ return stdioConfig;
595
+ };
596
+ var detectUpdateTransition = (incoming, previous) => {
597
+ if (!previous) {
598
+ return incoming.url ? "switch-to-remote" : "switch-to-stdio";
599
+ }
600
+ const previousIsRemote = Boolean(previous.url && previous.url.length > 0);
601
+ if (previousIsRemote) {
602
+ if (incoming.command && !incoming.url) {
603
+ return "switch-to-stdio";
604
+ }
605
+ return "merge-remote";
606
+ }
607
+ if (incoming.url && !incoming.command) {
608
+ return "switch-to-remote";
609
+ }
610
+ return "merge-stdio";
611
+ };
612
+ var sanitizeUpdatedServerConfig = (incoming, previous) => {
613
+ const transition = detectUpdateTransition(incoming, previous);
614
+ const targetTransport = incoming.type ?? previous?.type ?? "http";
615
+ switch (transition) {
616
+ case "switch-to-remote": {
617
+ const cleanBase = previous ? toRemoteServerConfig(previous, targetTransport) : {};
618
+ return toRemoteServerConfig({ ...cleanBase, ...incoming }, targetTransport);
619
+ }
620
+ case "switch-to-stdio": {
621
+ const cleanBase = previous ? toStdioServerConfig(previous) : {};
622
+ return toStdioServerConfig({ ...cleanBase, ...incoming });
623
+ }
624
+ case "merge-remote": {
625
+ return toRemoteServerConfig({ ...previous, ...incoming }, targetTransport);
626
+ }
627
+ case "merge-stdio":
628
+ default: {
629
+ return toStdioServerConfig({ ...previous, ...incoming });
630
+ }
631
+ }
632
+ };
633
+ var applyServerConfigDelta = (previousConfig, options) => {
634
+ if (options.url !== void 0 && options.command !== void 0) {
635
+ throw new Error('Cannot specify both "--url" (remote) and "--command" (stdio) simultaneously.');
636
+ }
637
+ const isCurrentRemote = Boolean(previousConfig?.url && previousConfig.url.length > 0);
638
+ const willBeRemote = options.url !== void 0 ? true : options.command !== void 0 ? false : isCurrentRemote;
639
+ const protocol = willBeRemote ? "remote" : "stdio";
640
+ const ignoredFlags = [];
641
+ if (willBeRemote) {
642
+ if (options.env !== void 0) ignoredFlags.push("--env");
643
+ if (options.clearEnv) ignoredFlags.push("--clear-env");
644
+ if (options.args !== void 0) ignoredFlags.push("--args");
645
+ if (options.clearArgs) ignoredFlags.push("--clear-args");
646
+ } else {
647
+ if (options.headers !== void 0) ignoredFlags.push("--header");
648
+ if (options.clearHeaders) ignoredFlags.push("--clear-headers");
649
+ if (options.transport !== void 0) ignoredFlags.push("--transport");
650
+ }
651
+ const incomingDelta = {};
652
+ if (options.command !== void 0) {
653
+ incomingDelta.command = options.command;
654
+ }
655
+ if (options.clearArgs) {
656
+ incomingDelta.args = void 0;
657
+ }
658
+ if (options.args !== void 0) {
659
+ incomingDelta.args = options.args;
660
+ }
661
+ if (options.url !== void 0) {
662
+ incomingDelta.url = options.url;
663
+ }
664
+ if (options.transport !== void 0) {
665
+ incomingDelta.type = options.transport;
666
+ }
667
+ if (options.clearEnv) {
668
+ incomingDelta.env = void 0;
669
+ }
670
+ if (options.env !== void 0) {
671
+ const baseEnv = options.clearEnv ? {} : previousConfig?.env ?? {};
672
+ incomingDelta.env = { ...baseEnv, ...options.env };
673
+ }
674
+ if (options.clearHeaders) {
675
+ incomingDelta.headers = void 0;
676
+ }
677
+ if (options.headers !== void 0) {
678
+ const baseHeaders = options.clearHeaders ? {} : previousConfig?.headers ?? {};
679
+ incomingDelta.headers = { ...baseHeaders, ...options.headers };
680
+ }
681
+ const transition = detectUpdateTransition(incomingDelta, previousConfig);
682
+ const cleanConfig = sanitizeUpdatedServerConfig(incomingDelta, previousConfig);
683
+ return {
684
+ config: cleanConfig,
685
+ transition,
686
+ protocol,
687
+ ignoredFlags
688
+ };
689
+ };
690
+
691
+ // src/source-parser.ts
692
+ var REMOTE_URL_REGEX = /^https?:\/\//i;
693
+ var HAS_WHITESPACE_REGEX = /\s/;
694
+ var PACKAGE_NAME_REGEX = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*(?:@[^\s]+)?$/;
695
+ var PATH_SEPARATOR_REGEX = /[/\\]/;
696
+ var stripVersionSuffix = (input) => {
697
+ if (input.startsWith("@")) {
698
+ const secondAtIndex = input.indexOf("@", 1);
699
+ if (secondAtIndex > 0) return input.slice(0, secondAtIndex);
700
+ return input;
701
+ }
702
+ const atIndex = input.lastIndexOf("@");
703
+ if (atIndex > 0) return input.slice(0, atIndex);
704
+ return input;
705
+ };
706
+ var stripScopePrefix = (input) => {
707
+ if (!input.startsWith("@") || !input.includes("/")) return input;
708
+ const parts = input.split("/");
709
+ return parts[1] || input;
710
+ };
711
+ var stripPathPrefix = (input) => {
712
+ if (!PATH_SEPARATOR_REGEX.test(input)) return input;
713
+ const segments = input.split(PATH_SEPARATOR_REGEX);
714
+ const basename = segments[segments.length - 1];
715
+ return basename || input;
716
+ };
717
+ var extractPackageName = (input) => {
718
+ let name = stripVersionSuffix(input);
719
+ name = stripScopePrefix(name);
720
+ name = stripPathPrefix(name);
721
+ name = name.replace(SCRIPT_EXTENSION_REGEX, "");
722
+ for (const prefix of PACKAGE_NAME_PREFIX_STRIP) {
723
+ if (name.startsWith(prefix)) {
724
+ name = name.slice(prefix.length);
725
+ break;
726
+ }
727
+ }
728
+ for (const suffix of PACKAGE_NAME_SUFFIX_STRIP) {
729
+ if (name.endsWith(suffix)) {
730
+ name = name.slice(0, -suffix.length);
731
+ break;
732
+ }
733
+ }
734
+ return name || MCP_DEFAULT_SERVER_NAME;
735
+ };
736
+ var inferNameFromUrl = (input) => {
737
+ try {
738
+ const url = new URL(input);
739
+ const host = url.hostname;
740
+ const labels = host.split(".").filter((segment) => segment.length > 0);
741
+ if (labels.length === 0) return MCP_DEFAULT_SERVER_NAME;
742
+ const meaningfulLabels = labels.filter((label) => {
743
+ const lower = label.toLowerCase();
744
+ if (COMMON_TLD_LABELS.has(lower)) return false;
745
+ if (GENERIC_HOST_PREFIXES.has(lower)) return false;
746
+ return true;
747
+ });
748
+ if (meaningfulLabels.length > 0) return meaningfulLabels[0];
749
+ if (labels.length >= 2) return labels[labels.length - 2];
750
+ return labels[labels.length - 1] || MCP_DEFAULT_SERVER_NAME;
751
+ } catch {
752
+ return MCP_DEFAULT_SERVER_NAME;
753
+ }
754
+ };
755
+ var inferNameFromCommand = (command) => {
756
+ const tokens = command.trim().split(/\s+/);
757
+ const runnerBase = tokens[0]?.split(PATH_SEPARATOR_REGEX).pop() ?? "";
758
+ const startIndex = KNOWN_COMMAND_RUNNERS.has(runnerBase) ? 1 : 0;
759
+ for (let tokenIndex = startIndex; tokenIndex < tokens.length; tokenIndex += 1) {
760
+ const token = tokens[tokenIndex];
761
+ if (!token || token.startsWith("-")) continue;
762
+ return extractPackageName(token);
763
+ }
764
+ const firstNonFlag = tokens.find((token) => !token.startsWith("-"));
765
+ return firstNonFlag ? extractPackageName(firstNonFlag) : MCP_DEFAULT_SERVER_NAME;
766
+ };
767
+ var parseMcpSource = (input) => {
768
+ const trimmed = input.trim();
769
+ if (trimmed.length === 0) {
770
+ throw new Error(
771
+ "Invalid MCP source: input is empty. Expected a remote URL, an npm package, or a command line."
772
+ );
773
+ }
774
+ if (REMOTE_URL_REGEX.test(trimmed)) {
775
+ return {
776
+ type: "remote",
777
+ value: trimmed,
778
+ inferredName: inferNameFromUrl(trimmed)
779
+ };
780
+ }
781
+ if (HAS_WHITESPACE_REGEX.test(trimmed)) {
782
+ return {
783
+ type: "command",
784
+ value: trimmed,
785
+ inferredName: inferNameFromCommand(trimmed)
786
+ };
787
+ }
788
+ if (PACKAGE_NAME_REGEX.test(trimmed)) {
789
+ return {
790
+ type: "package",
791
+ value: trimmed,
792
+ inferredName: extractPackageName(trimmed)
793
+ };
794
+ }
795
+ return {
796
+ type: "command",
797
+ value: trimmed,
798
+ inferredName: inferNameFromCommand(trimmed)
799
+ };
800
+ };
801
+ var isRemoteMcpSource = (parsed) => parsed.type === "remote";
802
+
803
+ // src/config-store.ts
804
+ import { existsSync as existsSync6 } from "fs";
805
+ import { join as join2 } from "path";
806
+
807
+ // src/utils/is-plain-object.ts
808
+ var isPlainObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
809
+
810
+ // src/utils/get-nested-value.ts
811
+ var getNestedValue = (source, dottedKey) => {
812
+ if (!source) return void 0;
813
+ if (dottedKey in source) return source[dottedKey];
814
+ const segments = dottedKey.split(".");
815
+ let cursor = source;
816
+ for (const segment of segments) {
817
+ if (!isPlainObject(cursor)) return void 0;
818
+ cursor = cursor[segment];
819
+ }
820
+ return cursor;
821
+ };
822
+
823
+ // src/formats/json.ts
824
+ import { existsSync as existsSync3, readFileSync, writeFileSync } from "fs";
825
+ import { applyEdits, modify, parse as parseJsonc } from "jsonc-parser";
826
+
827
+ // src/utils/ensure-parent-dir.ts
828
+ import { existsSync as existsSync2, mkdirSync } from "fs";
829
+ import { dirname } from "path";
830
+ var ensureParentDir = (filePath) => {
831
+ const parentDir = dirname(filePath);
832
+ if (!existsSync2(parentDir)) mkdirSync(parentDir, { recursive: true });
833
+ };
834
+
835
+ // src/utils/set-nested-value.ts
836
+ var DANGEROUS_KEY_SEGMENTS = /* @__PURE__ */ new Set([
837
+ "__proto__",
838
+ "prototype",
839
+ "constructor"
840
+ ]);
841
+ var assertSafeSegment = (segment) => {
842
+ if (DANGEROUS_KEY_SEGMENTS.has(segment)) {
843
+ throw new Error(`Refusing to write to unsafe key segment "${segment}"`);
844
+ }
845
+ };
846
+ var setNestedValue = (target, dottedKey, value) => {
847
+ if (dottedKey in target) {
848
+ assertSafeSegment(dottedKey);
849
+ target[dottedKey] = value;
850
+ return;
851
+ }
852
+ const segments = dottedKey.split(".");
853
+ let cursor = target;
854
+ for (let segmentIndex = 0; segmentIndex < segments.length - 1; segmentIndex += 1) {
855
+ const segment = segments[segmentIndex];
856
+ assertSafeSegment(segment);
857
+ const existing = cursor[segment];
858
+ if (isPlainObject(existing)) {
859
+ cursor = existing;
860
+ continue;
861
+ }
862
+ const next = {};
863
+ cursor[segment] = next;
864
+ cursor = next;
865
+ }
866
+ const finalSegment = segments[segments.length - 1];
867
+ assertSafeSegment(finalSegment);
868
+ cursor[finalSegment] = value;
869
+ };
870
+
871
+ // src/utils/walk-nested-object.ts
872
+ var walkNestedObject = (root, segments) => {
873
+ let cursor = root;
874
+ for (const segment of segments) {
875
+ if (!isPlainObject(cursor)) return void 0;
876
+ cursor = cursor[segment];
877
+ }
878
+ return isPlainObject(cursor) ? cursor : void 0;
879
+ };
880
+
881
+ // src/formats/json.ts
882
+ var JSONC_FORMATTING = {
883
+ insertSpaces: true,
884
+ tabSize: DEFAULT_JSON_INDENT_SPACES,
885
+ eol: "\n"
886
+ };
887
+ var readFileOrEmpty = (filePath) => existsSync3(filePath) ? readFileSync(filePath, "utf-8") : "";
888
+ var writeWithTrailingNewline = (filePath, contents) => {
889
+ writeFileSync(filePath, contents.endsWith("\n") ? contents : `${contents}
890
+ `, "utf-8");
891
+ };
892
+ var readJsoncConfig = (filePath) => {
893
+ const raw = readFileOrEmpty(filePath);
894
+ if (!raw.trim()) return {};
895
+ const parsed = parseJsonc(raw);
896
+ return isPlainObject(parsed) ? parsed : {};
897
+ };
898
+ var resolvePathPrefix = (root, dottedKey) => {
899
+ if (dottedKey in root) return [dottedKey];
900
+ const segments = dottedKey.split(".");
901
+ if (segments.length > 1 && walkNestedObject(root, segments)) {
902
+ return segments;
903
+ }
904
+ return [dottedKey];
905
+ };
906
+ var setJsoncNestedValue = (filePath, dottedKey, serverName, serverConfig) => {
907
+ ensureParentDir(filePath);
908
+ const existingText = readFileOrEmpty(filePath);
909
+ const sourceText = existingText.trim() ? existingText : "{}";
910
+ const existing = readJsoncConfig(filePath);
911
+ const pathPrefix = resolvePathPrefix(existing, dottedKey);
912
+ const path = [...pathPrefix, serverName];
913
+ const edits = modify(sourceText, path, serverConfig, {
914
+ formattingOptions: JSONC_FORMATTING
915
+ });
916
+ writeWithTrailingNewline(filePath, applyEdits(sourceText, edits));
917
+ };
918
+ var writeJsonConfigAtKey = (filePath, dottedKey, serverName, serverConfig) => {
919
+ ensureParentDir(filePath);
920
+ const existing = readJsoncConfig(filePath);
921
+ const pathPrefix = resolvePathPrefix(existing, dottedKey);
922
+ const existingServers = walkNestedObject(existing, pathPrefix);
923
+ const servers = existingServers ? { ...existingServers } : {};
924
+ servers[serverName] = serverConfig;
925
+ if (pathPrefix.length === 1) {
926
+ existing[pathPrefix[0]] = servers;
927
+ } else {
928
+ setNestedValue(existing, dottedKey, servers);
929
+ }
930
+ writeFileSync(
931
+ filePath,
932
+ `${JSON.stringify(existing, null, DEFAULT_JSON_INDENT_SPACES)}
933
+ `,
934
+ "utf-8"
935
+ );
936
+ };
937
+ var removeJsoncConfigKey = (filePath, dottedKey, serverName) => {
938
+ if (!existsSync3(filePath)) return false;
939
+ const sourceText = readFileSync(filePath, "utf-8");
940
+ if (!sourceText.trim()) return false;
941
+ const existing = readJsoncConfig(filePath);
942
+ const pathPrefix = resolvePathPrefix(existing, dottedKey);
943
+ const parentObject = walkNestedObject(existing, pathPrefix);
944
+ if (!parentObject || !(serverName in parentObject)) return false;
945
+ const path = [...pathPrefix, serverName];
946
+ const edits = modify(sourceText, path, void 0, {
947
+ formattingOptions: JSONC_FORMATTING
948
+ });
949
+ if (edits.length === 0) return false;
950
+ writeWithTrailingNewline(filePath, applyEdits(sourceText, edits));
951
+ return true;
952
+ };
953
+
954
+ // src/formats/toml.ts
955
+ import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
956
+ import TOML from "@iarna/toml";
957
+
958
+ // src/utils/delete-nested-value.ts
959
+ var DANGEROUS_KEY_SEGMENTS2 = /* @__PURE__ */ new Set([
960
+ "__proto__",
961
+ "prototype",
962
+ "constructor"
963
+ ]);
964
+ var deleteNestedValue = (target, dottedKey) => {
965
+ if (!target) return false;
966
+ if (dottedKey in target) {
967
+ if (DANGEROUS_KEY_SEGMENTS2.has(dottedKey)) return false;
968
+ delete target[dottedKey];
969
+ return true;
970
+ }
971
+ const segments = dottedKey.split(".");
972
+ if (segments.some((segment) => DANGEROUS_KEY_SEGMENTS2.has(segment))) return false;
973
+ let cursor = target;
974
+ for (let segmentIndex = 0; segmentIndex < segments.length - 1; segmentIndex += 1) {
975
+ const segment = segments[segmentIndex];
976
+ const existing = cursor[segment];
977
+ if (!isPlainObject(existing)) return false;
978
+ cursor = existing;
979
+ }
980
+ const lastSegment = segments[segments.length - 1];
981
+ if (!(lastSegment in cursor)) return false;
982
+ delete cursor[lastSegment];
983
+ return true;
984
+ };
985
+
986
+ // src/formats/toml.ts
987
+ var toTomlJsonMap = (value) => JSON.parse(JSON.stringify(value));
988
+ var readTomlConfig = (filePath) => {
989
+ if (!existsSync4(filePath)) return {};
990
+ const raw = readFileSync2(filePath, "utf-8");
991
+ if (!raw.trim()) return {};
992
+ const parsed = TOML.parse(raw);
993
+ return isPlainObject(parsed) ? parsed : {};
994
+ };
995
+ var writeTomlConfigAtKey = (filePath, dottedKey, serverName, serverConfig) => {
996
+ ensureParentDir(filePath);
997
+ const existing = readTomlConfig(filePath);
998
+ const existingServers = walkNestedObject(existing, dottedKey.split("."));
999
+ const servers = existingServers ? { ...existingServers } : {};
1000
+ servers[serverName] = serverConfig;
1001
+ setNestedValue(existing, dottedKey, servers);
1002
+ writeFileSync2(filePath, TOML.stringify(toTomlJsonMap(existing)), "utf-8");
1003
+ };
1004
+ var removeTomlConfigKey = (filePath, dottedKey, serverName) => {
1005
+ if (!existsSync4(filePath)) return false;
1006
+ const existing = readTomlConfig(filePath);
1007
+ const didRemove = deleteNestedValue(existing, `${dottedKey}.${serverName}`);
1008
+ if (didRemove) writeFileSync2(filePath, TOML.stringify(toTomlJsonMap(existing)), "utf-8");
1009
+ return didRemove;
1010
+ };
1011
+
1012
+ // src/formats/yaml.ts
1013
+ import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
1014
+ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
1015
+ var readYamlConfig = (filePath) => {
1016
+ if (!existsSync5(filePath)) return {};
1017
+ const raw = readFileSync3(filePath, "utf-8");
1018
+ if (!raw.trim()) return {};
1019
+ const parsed = parseYaml(raw);
1020
+ return isPlainObject(parsed) ? parsed : {};
1021
+ };
1022
+ var writeYamlConfigAtKey = (filePath, dottedKey, serverName, serverConfig) => {
1023
+ ensureParentDir(filePath);
1024
+ const existing = readYamlConfig(filePath);
1025
+ const existingServers = walkNestedObject(existing, dottedKey.split("."));
1026
+ const servers = existingServers ? { ...existingServers } : {};
1027
+ servers[serverName] = serverConfig;
1028
+ setNestedValue(existing, dottedKey, servers);
1029
+ writeFileSync3(filePath, stringifyYaml(existing), "utf-8");
1030
+ };
1031
+ var removeYamlConfigKey = (filePath, dottedKey, serverName) => {
1032
+ if (!existsSync5(filePath)) return false;
1033
+ const existing = readYamlConfig(filePath);
1034
+ const didRemove = deleteNestedValue(existing, `${dottedKey}.${serverName}`);
1035
+ if (didRemove) writeFileSync3(filePath, stringifyYaml(existing), "utf-8");
1036
+ return didRemove;
1037
+ };
1038
+
1039
+ // src/formats/index.ts
1040
+ var readConfigFile = (filePath, format) => {
1041
+ switch (format) {
1042
+ case "json":
1043
+ case "jsonc":
1044
+ return readJsoncConfig(filePath);
1045
+ case "yaml":
1046
+ return readYamlConfig(filePath);
1047
+ case "toml":
1048
+ return readTomlConfig(filePath);
1049
+ default:
1050
+ throw new Error(`Unsupported config format: ${format}`);
1051
+ }
1052
+ };
1053
+ var writeServerToConfigFile = (filePath, format, dottedKey, serverName, serverConfig) => {
1054
+ switch (format) {
1055
+ case "jsonc":
1056
+ setJsoncNestedValue(filePath, dottedKey, serverName, serverConfig);
1057
+ return;
1058
+ case "json":
1059
+ writeJsonConfigAtKey(filePath, dottedKey, serverName, serverConfig);
1060
+ return;
1061
+ case "yaml":
1062
+ writeYamlConfigAtKey(filePath, dottedKey, serverName, serverConfig);
1063
+ return;
1064
+ case "toml":
1065
+ writeTomlConfigAtKey(filePath, dottedKey, serverName, serverConfig);
1066
+ return;
1067
+ default:
1068
+ throw new Error(`Unsupported config format: ${format}`);
1069
+ }
1070
+ };
1071
+ var removeServerFromConfigFile = (filePath, format, dottedKey, serverName) => {
1072
+ switch (format) {
1073
+ case "json":
1074
+ case "jsonc":
1075
+ return removeJsoncConfigKey(filePath, dottedKey, serverName);
1076
+ case "yaml":
1077
+ return removeYamlConfigKey(filePath, dottedKey, serverName);
1078
+ case "toml":
1079
+ return removeTomlConfigKey(filePath, dottedKey, serverName);
1080
+ default:
1081
+ throw new Error(`Unsupported config format: ${format}`);
1082
+ }
1083
+ };
1084
+ var listServersInConfigFile = (filePath, format, dottedKey) => {
1085
+ const config = readConfigFile(filePath, format);
1086
+ const entries = getNestedValue(config, dottedKey);
1087
+ return isPlainObject(entries) ? entries : {};
1088
+ };
1089
+
1090
+ // src/transforms/index.ts
1091
+ var DIALECT_PRESETS = {
1092
+ vscode: {
1093
+ stdioTransport: "type-stdio",
1094
+ remoteTransport: "type-http-sse"
1095
+ },
1096
+ augment: {
1097
+ stdioTransport: "none",
1098
+ remoteTransport: "type-http-sse"
1099
+ },
1100
+ amp: {
1101
+ stdioTransport: "none",
1102
+ remoteTransport: "none"
1103
+ },
1104
+ trae: {
1105
+ stdioTransport: "none",
1106
+ remoteTransport: "sse-only-type"
1107
+ },
1108
+ grok: {
1109
+ stdioTransport: "none",
1110
+ remoteTransport: "sse-only-type"
1111
+ },
1112
+ cline: {
1113
+ stdioTransport: "none",
1114
+ remoteTransport: "streamableHttp"
1115
+ },
1116
+ goose: {
1117
+ stdioTransport: "type-stdio",
1118
+ remoteTransport: "streamable_http",
1119
+ commandField: "cmd",
1120
+ envField: "envs",
1121
+ urlField: "uri",
1122
+ defaultEnvEmpty: true,
1123
+ defaultHeadersEmpty: true,
1124
+ includeServerName: true,
1125
+ timeoutSeconds: GOOSE_TIMEOUT_SECONDS,
1126
+ extraFields: {
1127
+ description: "",
1128
+ enabled: true
1129
+ }
1130
+ },
1131
+ "kimi-code": {
1132
+ stdioTransport: "none",
1133
+ remoteTransport: "sse-only-transport"
1134
+ },
1135
+ kiro: {
1136
+ stdioTransport: "none",
1137
+ remoteTransport: "none"
1138
+ },
1139
+ opencode: {
1140
+ stdioTransport: "type-local",
1141
+ remoteTransport: "remote-type",
1142
+ commandArray: true,
1143
+ envField: "environment",
1144
+ defaultEnvEmpty: true,
1145
+ extraFields: {
1146
+ enabled: true
1147
+ }
1148
+ },
1149
+ pi: {
1150
+ stdioTransport: "transport-stdio",
1151
+ remoteTransport: "streamable-http"
1152
+ },
1153
+ "qwen-code": {
1154
+ stdioTransport: "none",
1155
+ remoteTransport: "qwen"
1156
+ },
1157
+ zed: {
1158
+ stdioTransport: "none",
1159
+ remoteTransport: "none",
1160
+ defaultEnvEmpty: true
1161
+ }
1162
+ };
1163
+ var resolveDialectOptions = (dialect) => {
1164
+ if (typeof dialect === "string") {
1165
+ const preset = DIALECT_PRESETS[dialect];
1166
+ if (!preset) {
1167
+ throw new Error(`Unknown server config dialect: "${dialect}"`);
1168
+ }
1169
+ return preset;
1170
+ }
1171
+ return dialect;
1172
+ };
1173
+ var transformRemoteConfig = (serverName, config, options) => {
1174
+ const result = {};
1175
+ if (options.includeServerName) {
1176
+ result.name = serverName;
1177
+ }
1178
+ if (options.extraFields) {
1179
+ Object.assign(result, options.extraFields);
1180
+ }
1181
+ const remoteTransport = options.remoteTransport ?? "type-http-sse";
1182
+ const urlField = options.urlField ?? "url";
1183
+ switch (remoteTransport) {
1184
+ case "type-http-sse":
1185
+ result.type = config.type || "http";
1186
+ result[urlField] = config.url;
1187
+ break;
1188
+ case "sse-only-type":
1189
+ result[urlField] = config.url;
1190
+ if (config.type === "sse") {
1191
+ result.type = "sse";
1192
+ }
1193
+ break;
1194
+ case "sse-only-transport":
1195
+ result[urlField] = config.url;
1196
+ if (config.type === "sse") {
1197
+ result.transport = "sse";
1198
+ }
1199
+ break;
1200
+ case "none":
1201
+ result[urlField] = config.url;
1202
+ break;
1203
+ case "streamableHttp":
1204
+ result.type = config.type === "sse" ? "sse" : "streamableHttp";
1205
+ result[urlField] = config.url;
1206
+ break;
1207
+ case "streamable-http":
1208
+ result.transport = config.type === "sse" ? "sse" : "streamable-http";
1209
+ result[urlField] = config.url;
1210
+ break;
1211
+ case "streamable_http":
1212
+ result.type = config.type === "sse" ? "sse" : "streamable_http";
1213
+ result[urlField] = config.url;
1214
+ break;
1215
+ case "remote-type":
1216
+ result.type = "remote";
1217
+ result[urlField] = config.url;
1218
+ break;
1219
+ case "qwen":
1220
+ if (config.type === "sse") {
1221
+ result.url = config.url;
1222
+ } else {
1223
+ result.httpUrl = config.url;
1224
+ }
1225
+ break;
1226
+ }
1227
+ const hasHeaders = config.headers && Object.keys(config.headers).length > 0;
1228
+ if (hasHeaders) {
1229
+ result.headers = config.headers;
1230
+ } else if (options.defaultHeadersEmpty) {
1231
+ result.headers = {};
1232
+ }
1233
+ if (options.timeoutSeconds !== void 0) {
1234
+ result.timeout = options.timeoutSeconds;
1235
+ }
1236
+ return result;
1237
+ };
1238
+ var transformStdioConfig = (serverName, config, options) => {
1239
+ const result = {};
1240
+ if (options.includeServerName) {
1241
+ result.name = serverName;
1242
+ }
1243
+ if (options.extraFields) {
1244
+ Object.assign(result, options.extraFields);
1245
+ }
1246
+ const stdioTransport = options.stdioTransport ?? "none";
1247
+ const commandField = options.commandField ?? "command";
1248
+ const argsField = options.argsField ?? "args";
1249
+ const envField = options.envField ?? "env";
1250
+ switch (stdioTransport) {
1251
+ case "type-stdio":
1252
+ result.type = "stdio";
1253
+ break;
1254
+ case "transport-stdio":
1255
+ result.transport = "stdio";
1256
+ break;
1257
+ case "type-local":
1258
+ result.type = "local";
1259
+ break;
1260
+ case "none":
1261
+ break;
1262
+ }
1263
+ if (options.commandArray) {
1264
+ result[commandField] = [config.command, ...config.args || []];
1265
+ } else {
1266
+ result[commandField] = config.command;
1267
+ result[argsField] = config.args || [];
1268
+ }
1269
+ const hasEnv = config.env && Object.keys(config.env).length > 0;
1270
+ if (hasEnv) {
1271
+ result[envField] = config.env;
1272
+ } else if (options.defaultEnvEmpty) {
1273
+ result[envField] = {};
1274
+ }
1275
+ if (options.timeoutSeconds !== void 0) {
1276
+ result.timeout = options.timeoutSeconds;
1277
+ }
1278
+ return result;
1279
+ };
1280
+ var transformServerConfig = (serverName, config, dialect, _context) => {
1281
+ const options = resolveDialectOptions(dialect);
1282
+ if (config.url) {
1283
+ return transformRemoteConfig(serverName, config, options);
1284
+ }
1285
+ return transformStdioConfig(serverName, config, options);
1286
+ };
1287
+ var createAgentTransform = (dialect) => {
1288
+ return (serverName, config, context) => transformServerConfig(serverName, config, dialect, context);
1289
+ };
1290
+ var transformServerConfigForAgent = (agent, serverName, config, context = { global: false }) => {
1291
+ if (agent.transformConfig) {
1292
+ return agent.transformConfig(serverName, config, context);
1293
+ }
1294
+ if (agent.transformDialect) {
1295
+ return transformServerConfig(serverName, config, agent.transformDialect, context);
1296
+ }
1297
+ return config;
1298
+ };
1299
+
1300
+ // src/utils/to-error-message.ts
1301
+ var toErrorMessage = (error, fallback = "Unknown error") => error instanceof Error ? error.message : fallback;
1302
+
1303
+ // src/config-store.ts
1304
+ var resolveMcpConfigTarget = (agent, options = {}) => {
1305
+ const isGlobal = options.global ?? false;
1306
+ const cwd = options.cwd ?? process.cwd();
1307
+ const configPath = agent.resolveConfigPath ? agent.resolveConfigPath({ global: isGlobal, cwd }) : !isGlobal && agent.projectConfigPath ? join2(cwd, agent.projectConfigPath) : agent.globalConfigPath;
1308
+ const configKey = !isGlobal && agent.projectConfigKey ? agent.projectConfigKey : agent.configKey;
1309
+ return { configPath, configKey };
1310
+ };
1311
+ var getCandidateAgentsForScope = (options = {}) => {
1312
+ return options.global ? getMcpAgentTypes() : getMcpAgentsSupportingProjectScope();
1313
+ };
1314
+ var FsConfigStoreAdapter = class {
1315
+ exists(filePath) {
1316
+ return existsSync6(filePath);
1317
+ }
1318
+ read(target) {
1319
+ return readConfigFile(target.filePath, target.format);
1320
+ }
1321
+ writeServer(target, serverName, serverConfig) {
1322
+ if (!target.dottedKey) {
1323
+ throw new Error(`Cannot write server: missing dottedKey for ${target.filePath}`);
1324
+ }
1325
+ writeServerToConfigFile(
1326
+ target.filePath,
1327
+ target.format,
1328
+ target.dottedKey,
1329
+ serverName,
1330
+ serverConfig
1331
+ );
1332
+ }
1333
+ removeServer(target, serverName) {
1334
+ if (!target.dottedKey) return false;
1335
+ return removeServerFromConfigFile(
1336
+ target.filePath,
1337
+ target.format,
1338
+ target.dottedKey,
1339
+ serverName
1340
+ );
1341
+ }
1342
+ listServers(target) {
1343
+ if (!target.dottedKey) return {};
1344
+ return listServersInConfigFile(target.filePath, target.format, target.dottedKey);
1345
+ }
1346
+ };
1347
+ var AgentConfigStore = class {
1348
+ constructor(adapter = new FsConfigStoreAdapter()) {
1349
+ this.adapter = adapter;
1350
+ }
1351
+ adapter;
1352
+ getAdapter() {
1353
+ return this.adapter;
1354
+ }
1355
+ // ---- Single-agent primitives ----
1356
+ resolveTarget(agent, options = {}) {
1357
+ const agentConfig = typeof agent === "string" ? getMcpAgentConfig(agent) : agent;
1358
+ const target = resolveMcpConfigTarget(agentConfig, options);
1359
+ return { agent: agentConfig, target };
1360
+ }
1361
+ resolveDescriptor(agent, options = {}) {
1362
+ const { agent: agentConfig, target } = this.resolveTarget(agent, options);
1363
+ return {
1364
+ filePath: target.configPath,
1365
+ format: agentConfig.format,
1366
+ dottedKey: target.configKey
1367
+ };
1368
+ }
1369
+ writeServer(agent, serverName, serverConfig, options = {}) {
1370
+ const descriptor = this.resolveDescriptor(agent, options);
1371
+ this.adapter.writeServer(descriptor, serverName, serverConfig);
1372
+ return { path: descriptor.filePath };
1373
+ }
1374
+ removeServer(agent, serverName, options = {}) {
1375
+ const descriptor = this.resolveDescriptor(agent, options);
1376
+ if (!this.adapter.exists(descriptor.filePath)) {
1377
+ return { path: descriptor.filePath, removed: false };
1378
+ }
1379
+ const removed = this.adapter.removeServer(descriptor, serverName);
1380
+ return { path: descriptor.filePath, removed };
1381
+ }
1382
+ listServers(agent, options = {}) {
1383
+ const descriptor = this.resolveDescriptor(agent, options);
1384
+ if (!this.adapter.exists(descriptor.filePath)) {
1385
+ return { path: descriptor.filePath, exists: false, servers: {} };
1386
+ }
1387
+ const servers = this.adapter.listServers(descriptor);
1388
+ return { path: descriptor.filePath, exists: true, servers };
1389
+ }
1390
+ /**
1391
+ * Batch lists servers for multiple agents, caching adapter reads for co-hosted
1392
+ * agents that share the exact same physical configuration file and dotted key.
1393
+ */
1394
+ listServersForAgents(agents, options = {}) {
1395
+ const readCache = /* @__PURE__ */ new Map();
1396
+ return agents.map((agentType) => {
1397
+ const descriptor = this.resolveDescriptor(agentType, options);
1398
+ const cacheKey = `${descriptor.filePath}::${descriptor.dottedKey ?? ""}`;
1399
+ let cached = readCache.get(cacheKey);
1400
+ if (!cached) {
1401
+ cached = this.listServers(agentType, options);
1402
+ readCache.set(cacheKey, cached);
1403
+ }
1404
+ return {
1405
+ agent: agentType,
1406
+ path: cached.path,
1407
+ exists: cached.exists,
1408
+ servers: cached.servers
1409
+ };
1410
+ });
1411
+ }
1412
+ read(agent, options = {}) {
1413
+ const descriptor = this.resolveDescriptor(agent, options);
1414
+ if (!this.adapter.exists(descriptor.filePath)) {
1415
+ return {};
1416
+ }
1417
+ return this.adapter.read(descriptor);
1418
+ }
1419
+ readServer(agent, serverName, options = {}) {
1420
+ const { exists, servers } = this.listServers(agent, options);
1421
+ if (!exists) return void 0;
1422
+ return servers[serverName];
1423
+ }
1424
+ // ---- Cluster & co-hosted awareness ----
1425
+ /**
1426
+ * Returns all other agents sharing the exact same physical configuration target
1427
+ * (same configPath and configKey) for the given scope.
1428
+ */
1429
+ getCoHostedAgents(agentType, options = {}) {
1430
+ const currentAgent = getMcpAgentConfig(agentType);
1431
+ const currentTarget = resolveMcpConfigTarget(currentAgent, options);
1432
+ const candidates = getCandidateAgentsForScope(options);
1433
+ const coHosted = [];
1434
+ for (const candidateType of candidates) {
1435
+ if (candidateType === agentType) continue;
1436
+ const candidateConfig = getMcpAgentConfig(candidateType);
1437
+ const candidateTarget = resolveMcpConfigTarget(candidateConfig, options);
1438
+ if (candidateTarget.configPath === currentTarget.configPath && candidateTarget.configKey === currentTarget.configKey) {
1439
+ coHosted.push(candidateType);
1440
+ }
1441
+ }
1442
+ return coHosted;
1443
+ }
1444
+ /**
1445
+ * Resolves configuration clusters for a given list of requested agents.
1446
+ * Groups requested agents sharing the same physical config path, and tracks
1447
+ * any remaining co-hosted agents sharing the same target that were not in the request.
1448
+ */
1449
+ resolveConfigClusters(agentTypes, options = {}) {
1450
+ const clustersByPath = /* @__PURE__ */ new Map();
1451
+ for (const agentType of agentTypes) {
1452
+ const agentConfig = getMcpAgentConfig(agentType);
1453
+ const target = resolveMcpConfigTarget(agentConfig, options);
1454
+ let keyMap = clustersByPath.get(target.configPath);
1455
+ if (!keyMap) {
1456
+ keyMap = /* @__PURE__ */ new Map();
1457
+ clustersByPath.set(target.configPath, keyMap);
1458
+ }
1459
+ let cluster = keyMap.get(target.configKey);
1460
+ if (!cluster) {
1461
+ const allCoHosted = this.getCoHostedAgents(agentType, options);
1462
+ cluster = {
1463
+ configPath: target.configPath,
1464
+ configKey: target.configKey,
1465
+ targetAgents: [],
1466
+ coHostedAgents: allCoHosted
1467
+ };
1468
+ keyMap.set(target.configKey, cluster);
1469
+ }
1470
+ if (!cluster.targetAgents.includes(agentType)) {
1471
+ cluster.targetAgents.push(agentType);
1472
+ }
1473
+ }
1474
+ const clusters = [];
1475
+ for (const keyMap of clustersByPath.values()) {
1476
+ for (const cluster of keyMap.values()) {
1477
+ cluster.coHostedAgents = cluster.coHostedAgents.filter(
1478
+ (co) => !cluster.targetAgents.includes(co)
1479
+ );
1480
+ clusters.push(cluster);
1481
+ }
1482
+ }
1483
+ return clusters;
1484
+ }
1485
+ /**
1486
+ * Sorts agent types so that agents sharing the same physical config target
1487
+ * appear adjacent to each other in the returned array.
1488
+ */
1489
+ sortAgentsByClusters(agentTypes, options = {}) {
1490
+ const clusters = this.resolveConfigClusters(agentTypes, options);
1491
+ const sorted = [];
1492
+ for (const cluster of clusters) {
1493
+ for (const agent of cluster.targetAgents) {
1494
+ if (!sorted.includes(agent)) {
1495
+ sorted.push(agent);
1496
+ }
1497
+ }
1498
+ }
1499
+ return sorted;
1500
+ }
1501
+ /**
1502
+ * Alias for sortAgentsByClusters for backward compatibility.
1503
+ */
1504
+ sortAgentsWithClusters(agentTypes, options = {}) {
1505
+ return this.sortAgentsByClusters(agentTypes, options);
1506
+ }
1507
+ // ---- Batch operations with automatic clustering & dialect transforms ----
1508
+ /**
1509
+ * Batch write: transforms and writes a server config to multiple agents,
1510
+ * with automatic cluster deduplication, dialect transform, and co-hosted awareness.
1511
+ * Callers pass standard McpServerConfig; the Store applies per-Agent dialect transforms
1512
+ * internally before persisting.
1513
+ */
1514
+ writeServers(agents, serverName, serverConfig, options = {}) {
1515
+ const clusters = this.resolveConfigClusters(agents, options);
1516
+ const resultsByAgent = /* @__PURE__ */ new Map();
1517
+ const isGlobal = options.global ?? false;
1518
+ for (const cluster of clusters) {
1519
+ const primaryAgentType = cluster.targetAgents[0];
1520
+ const primaryAgent = getMcpAgentConfig(primaryAgentType);
1521
+ try {
1522
+ const transformed = transformServerConfigForAgent(primaryAgent, serverName, serverConfig, {
1523
+ global: isGlobal
1524
+ });
1525
+ const descriptor = this.resolveDescriptor(primaryAgent, options);
1526
+ this.adapter.writeServer(descriptor, serverName, transformed);
1527
+ for (const agentType of cluster.targetAgents) {
1528
+ resultsByAgent.set(agentType, {
1529
+ agent: agentType,
1530
+ success: true,
1531
+ path: cluster.configPath,
1532
+ coConfiguredAgents: cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
1533
+ });
1534
+ }
1535
+ } catch (error) {
1536
+ const errorMsg = toErrorMessage(error);
1537
+ for (const agentType of cluster.targetAgents) {
1538
+ resultsByAgent.set(agentType, {
1539
+ agent: agentType,
1540
+ success: false,
1541
+ path: cluster.configPath,
1542
+ error: errorMsg
1543
+ });
1544
+ }
1545
+ }
1546
+ }
1547
+ return agents.map((agentType) => resultsByAgent.get(agentType));
1548
+ }
1549
+ /**
1550
+ * Batch remove: removes a server from multiple agents' configs,
1551
+ * with automatic cluster deduplication and co-hosted awareness.
1552
+ */
1553
+ removeServers(agents, serverName, options = {}) {
1554
+ const clusters = this.resolveConfigClusters(agents, options);
1555
+ const resultsByAgent = /* @__PURE__ */ new Map();
1556
+ for (const cluster of clusters) {
1557
+ const primaryAgentType = cluster.targetAgents[0];
1558
+ const primaryAgent = getMcpAgentConfig(primaryAgentType);
1559
+ try {
1560
+ const { removed } = this.removeServer(primaryAgent, serverName, options);
1561
+ for (const agentType of cluster.targetAgents) {
1562
+ resultsByAgent.set(agentType, {
1563
+ agent: agentType,
1564
+ path: cluster.configPath,
1565
+ removed,
1566
+ coAffectedAgents: removed && cluster.coHostedAgents.length > 0 ? cluster.coHostedAgents : void 0
1567
+ });
1568
+ }
1569
+ } catch (error) {
1570
+ const errorMsg = toErrorMessage(error);
1571
+ for (const agentType of cluster.targetAgents) {
1572
+ resultsByAgent.set(agentType, {
1573
+ agent: agentType,
1574
+ path: cluster.configPath,
1575
+ removed: false,
1576
+ error: errorMsg
1577
+ });
1578
+ }
1579
+ }
1580
+ }
1581
+ return agents.map((agentType) => resultsByAgent.get(agentType));
1582
+ }
1583
+ };
1584
+ var agentConfigStore = new AgentConfigStore();
1585
+
1586
+ // src/resolve-config-clusters.ts
1587
+ var getCoHostedAgents = (agentType, options = {}) => agentConfigStore.getCoHostedAgents(agentType, options);
1588
+ var resolveConfigClusters = (agentTypes, options = {}) => agentConfigStore.resolveConfigClusters(agentTypes, options);
1589
+ var sortAgentsByClusters = (agentTypes, options = {}) => agentConfigStore.sortAgentsByClusters(agentTypes, options);
1590
+
1591
+ // src/utils/parse-mcp-agent-list.ts
1592
+ var parseMcpAgentList = (input) => {
1593
+ if (!input || input.length === 0) return void 0;
1594
+ if (input.includes("*")) return getMcpAgentTypes();
1595
+ const resolved = [];
1596
+ for (const value of input) {
1597
+ const agentType = resolveMcpAgentAlias(value);
1598
+ if (!agentType) throw new Error(`Unknown MCP agent "${value}"`);
1599
+ resolved.push(agentType);
1600
+ }
1601
+ return resolved;
1602
+ };
1603
+
1604
+ // src/resolve-target-agents.ts
1605
+ var normalizeRequestedAgents = (input) => {
1606
+ if (!input || input.length === 0) return void 0;
1607
+ const rawList = [...input];
1608
+ if (rawList.every((item) => isMcpAgentType(item))) {
1609
+ return rawList;
1610
+ }
1611
+ return parseMcpAgentList(rawList);
1612
+ };
1613
+ var resolveTargetAgents = (query = {}) => {
1614
+ const cwd = query.cwd ?? process.cwd();
1615
+ const isGlobal = query.global ?? false;
1616
+ let explicitAgents = normalizeRequestedAgents(query.requested);
1617
+ if (query.all) {
1618
+ explicitAgents = getMcpAgentTypes();
1619
+ }
1620
+ const isDetected = !explicitAgents || explicitAgents.length === 0;
1621
+ const detected = isGlobal ? detectGloballyInstalledMcpAgents() : detectProjectInstalledMcpAgents(cwd);
1622
+ const candidateAgents = isDetected ? detected : explicitAgents ?? [];
1623
+ const allAgents = candidateAgents.filter(
1624
+ (type, index) => candidateAgents.indexOf(type) === index
1625
+ );
1626
+ const incompatible = [];
1627
+ const compatibleAgents = [];
1628
+ for (const agentType of allAgents) {
1629
+ const config = getMcpAgentConfig(agentType);
1630
+ if (query.transport && !isMcpTransportSupported(config, query.transport)) {
1631
+ incompatible.push({
1632
+ agent: agentType,
1633
+ reason: config.unsupportedTransportMessage ?? `agent ${agentType} only supports ${config.supportedTransports.join(", ")} transport (attempted ${query.transport})`
1634
+ });
1635
+ } else {
1636
+ compatibleAgents.push(agentType);
1637
+ }
1638
+ }
1639
+ let diagnostic;
1640
+ if (compatibleAgents.length === 0) {
1641
+ if (isDetected) {
1642
+ diagnostic = `No ${isGlobal ? "global" : "project"}-installed MCP agents detected. Pass -a <agent> (e.g. -a cursor) or --all to install.`;
1643
+ } else if (allAgents.length > 0 && incompatible.length > 0 && query.transport) {
1644
+ const list = incompatible.map((item) => `${item.agent} (${item.reason})`).join(", ");
1645
+ diagnostic = `None of the selected agents support ${query.transport} transport: ${list}`;
1646
+ } else {
1647
+ diagnostic = "No valid target agents specified.";
1648
+ }
1649
+ }
1650
+ return {
1651
+ agents: compatibleAgents,
1652
+ compatibleAgents,
1653
+ allAgents,
1654
+ candidateAgents: allAgents,
1655
+ detected,
1656
+ isDetected,
1657
+ incompatible,
1658
+ diagnostic
1659
+ };
1660
+ };
1661
+
1662
+ // src/install-compat.ts
1663
+ var installMcpServerForAgent = (serverName, serverConfig, agentType, options = {}) => {
1664
+ const results = agentConfigStore.writeServers([agentType], serverName, serverConfig, options);
1665
+ return results[0];
1666
+ };
1667
+ var installMcpServerForAgents = (serverName, serverConfig, agentTypes, options = {}) => {
1668
+ return agentConfigStore.writeServers(agentTypes, serverName, serverConfig, options);
1669
+ };
1670
+ var installToCompatibleAgents = (serverName, serverConfig, options) => {
1671
+ const { allAgents, incompatible = [], global: isGlobal, cwd } = options;
1672
+ const incompatibleMap = new Map(incompatible.map((item) => [item.agent, item.reason]));
1673
+ const compatibleAgents = allAgents.filter((a) => !incompatibleMap.has(a));
1674
+ const installedResults = agentConfigStore.writeServers(
1675
+ compatibleAgents,
1676
+ serverName,
1677
+ serverConfig,
1678
+ {
1679
+ global: isGlobal,
1680
+ cwd
1681
+ }
1682
+ );
1683
+ const installedMap = new Map(installedResults.map((r) => [r.agent, r]));
1684
+ return allAgents.map((agentType) => {
1685
+ const incompatibleReason = incompatibleMap.get(agentType);
1686
+ if (incompatibleReason) {
1687
+ return {
1688
+ agent: agentType,
1689
+ success: false,
1690
+ path: "",
1691
+ error: incompatibleReason
1692
+ };
1693
+ }
1694
+ return installedMap.get(agentType);
1695
+ });
1696
+ };
1697
+
1698
+ // src/install-mcp-server.ts
1699
+ var installMcpServer = (options) => {
1700
+ const parsed = parseMcpSource(options.source);
1701
+ const isGlobal = options.global ?? false;
1702
+ const cwd = options.cwd ?? process.cwd();
1703
+ const serverName = options.name ?? parsed.inferredName;
1704
+ const serverConfig = buildMcpServerConfig(parsed, {
1705
+ transport: options.transport,
1706
+ headers: options.headers,
1707
+ env: options.env,
1708
+ args: options.args
1709
+ });
1710
+ const requestedTransport = parsed.type === "remote" ? serverConfig.type ?? "http" : "stdio";
1711
+ const { allAgents, incompatible } = resolveTargetAgents({
1712
+ requested: options.agents,
1713
+ global: isGlobal,
1714
+ cwd,
1715
+ transport: requestedTransport
1716
+ });
1717
+ const results = installToCompatibleAgents(serverName, serverConfig, {
1718
+ allAgents,
1719
+ incompatible,
1720
+ global: isGlobal,
1721
+ cwd
1722
+ });
1723
+ return { serverName, config: serverConfig, results };
1724
+ };
1725
+
1726
+ // src/list.ts
1727
+ var listInstalledMcpServers = (options = {}) => {
1728
+ const agentTypes = options.agents ?? getMcpAgentTypes();
1729
+ const collected = [];
1730
+ const results = agentConfigStore.listServersForAgents(agentTypes, options);
1731
+ for (const item of results) {
1732
+ if (!item.exists) continue;
1733
+ for (const [serverName, rawConfig] of Object.entries(item.servers)) {
1734
+ collected.push({
1735
+ serverName,
1736
+ agent: item.agent,
1737
+ path: item.path,
1738
+ config: rawConfig,
1739
+ serverConfig: parseServerConfig(rawConfig)
1740
+ });
1741
+ }
1742
+ }
1743
+ return collected;
1744
+ };
1745
+ var groupInstalledServersByName = (installed) => {
1746
+ const grouped = /* @__PURE__ */ new Map();
1747
+ for (const item of installed) {
1748
+ const itemConfig = item.serverConfig ?? parseServerConfig(item.config);
1749
+ let entry = grouped.get(item.serverName);
1750
+ if (!entry) {
1751
+ entry = {
1752
+ serverName: item.serverName,
1753
+ agents: [],
1754
+ paths: [],
1755
+ config: itemConfig,
1756
+ hasDivergence: false
1757
+ };
1758
+ grouped.set(item.serverName, entry);
1759
+ } else if (!entry.hasDivergence) {
1760
+ if (JSON.stringify(entry.config) !== JSON.stringify(itemConfig)) {
1761
+ entry.hasDivergence = true;
1762
+ }
1763
+ }
1764
+ if (!entry.agents.includes(item.agent)) {
1765
+ entry.agents.push(item.agent);
1766
+ }
1767
+ if (!entry.paths.includes(item.path)) {
1768
+ entry.paths.push(item.path);
1769
+ }
1770
+ }
1771
+ return grouped;
1772
+ };
1773
+ var queryGroupedInstalledServers = (options = {}) => {
1774
+ const installed = listInstalledMcpServers(options);
1775
+ return groupInstalledServersByName(installed);
1776
+ };
1777
+
1778
+ // src/update-mcp-server.ts
1779
+ var updateMcpServer = (options) => {
1780
+ const isGlobal = options.global ?? false;
1781
+ const cwd = options.cwd ?? process.cwd();
1782
+ let previousConfig = options.previousConfig;
1783
+ if (!previousConfig) {
1784
+ const existing = listInstalledMcpServers({
1785
+ global: isGlobal,
1786
+ cwd,
1787
+ agents: options.agents
1788
+ });
1789
+ const found = existing.find((s) => s.serverName === options.serverName && s.serverConfig);
1790
+ if (found) {
1791
+ previousConfig = found.serverConfig;
1792
+ }
1793
+ }
1794
+ const serverConfig = sanitizeUpdatedServerConfig(options.config, previousConfig);
1795
+ let targetAgents = options.agents;
1796
+ if (!targetAgents || targetAgents.length === 0) {
1797
+ const existing = listInstalledMcpServers({ global: isGlobal, cwd });
1798
+ targetAgents = existing.filter((s) => s.serverName === options.serverName).map((s) => s.agent);
1799
+ }
1800
+ const requestedTransport = serverConfig.url ? serverConfig.type ?? "http" : "stdio";
1801
+ const { allAgents, incompatible } = resolveTargetAgents({
1802
+ requested: targetAgents,
1803
+ global: isGlobal,
1804
+ cwd,
1805
+ transport: requestedTransport
1806
+ });
1807
+ const results = installToCompatibleAgents(options.serverName, serverConfig, {
1808
+ allAgents,
1809
+ incompatible,
1810
+ global: isGlobal,
1811
+ cwd
1812
+ });
1813
+ return {
1814
+ serverName: options.serverName,
1815
+ config: serverConfig,
1816
+ results,
1817
+ incompatible
1818
+ };
1819
+ };
1820
+
1821
+ // src/remove.ts
1822
+ var removeMcpServer = (options) => {
1823
+ const { allAgents } = resolveTargetAgents({
1824
+ requested: options.agents,
1825
+ all: !options.agents,
1826
+ global: options.global,
1827
+ cwd: options.cwd
1828
+ });
1829
+ const storeResults = agentConfigStore.removeServers(allAgents, options.name, {
1830
+ global: options.global,
1831
+ cwd: options.cwd
1832
+ });
1833
+ return storeResults.filter((r) => r.removed || Boolean(r.error));
1834
+ };
1835
+
1836
+ // src/utils/mask-secret.ts
1837
+ var SECRET_KEY_PATTERN = /(token|key|secret|password|passwd|auth|credential)/i;
1838
+ var maskSecretValue = (key, value) => {
1839
+ if (!SECRET_KEY_PATTERN.test(key) || value.length <= 4) {
1840
+ return value;
1841
+ }
1842
+ return `${value.slice(0, 2)}***${value.slice(-2)}`;
1843
+ };
1844
+ var SECRET_HEADER_PATTERN = /(authorization|token|key|secret|auth)/i;
1845
+ var maskSecretHeader = (key, value) => {
1846
+ if (!SECRET_HEADER_PATTERN.test(key) || value.length <= 8) {
1847
+ return value;
1848
+ }
1849
+ return `${value.slice(0, 4)}***${value.slice(-3)}`;
1850
+ };
1851
+
1852
+ export {
1853
+ getMcpAgentConfig,
1854
+ getMcpAgentTypes,
1855
+ isMcpAgentType,
1856
+ resolveMcpAgentAlias,
1857
+ isMcpTransportSupported,
1858
+ detectProjectInstalledMcpAgents,
1859
+ detectGloballyInstalledMcpAgents,
1860
+ getMcpAgentsSupportingProjectScope,
1861
+ DEFAULT_REMOTE_TRANSPORT,
1862
+ isRemoteServerConfig,
1863
+ isStdioServerConfig,
1864
+ buildMcpServerConfig,
1865
+ parseServerConfig,
1866
+ toRemoteServerConfig,
1867
+ toStdioServerConfig,
1868
+ detectUpdateTransition,
1869
+ sanitizeUpdatedServerConfig,
1870
+ applyServerConfigDelta,
1871
+ extractPackageName,
1872
+ parseMcpSource,
1873
+ isRemoteMcpSource,
1874
+ createAgentTransform,
1875
+ transformServerConfigForAgent,
1876
+ toErrorMessage,
1877
+ resolveMcpConfigTarget,
1878
+ getCandidateAgentsForScope,
1879
+ AgentConfigStore,
1880
+ agentConfigStore,
1881
+ getCoHostedAgents,
1882
+ resolveConfigClusters,
1883
+ sortAgentsByClusters,
1884
+ parseMcpAgentList,
1885
+ resolveTargetAgents,
1886
+ installMcpServerForAgent,
1887
+ installMcpServerForAgents,
1888
+ installToCompatibleAgents,
1889
+ installMcpServer,
1890
+ listInstalledMcpServers,
1891
+ queryGroupedInstalledServers,
1892
+ updateMcpServer,
1893
+ removeMcpServer,
1894
+ SECRET_KEY_PATTERN,
1895
+ maskSecretValue,
1896
+ SECRET_HEADER_PATTERN,
1897
+ maskSecretHeader
1898
+ };