@wuyax/mcps 0.1.0-beta.3 → 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.
- package/LICENSE +21 -0
- package/README.md +22 -14
- package/dist/chunk-AWD3VQ3S.js +1624 -0
- package/dist/chunk-O3BRJFEC.js +1898 -0
- package/dist/cli.cjs +1057 -979
- package/dist/cli.js +188 -17
- package/dist/index.cjs +702 -2478
- package/dist/index.d.cts +152 -452
- package/dist/index.d.ts +152 -452
- package/dist/index.js +9 -85
- package/dist/{chunk-BUKA3NPJ.js → interactive.cjs} +1555 -1779
- package/dist/interactive.d.cts +228 -0
- package/dist/interactive.d.ts +228 -0
- package/dist/interactive.js +53 -0
- package/dist/types-lEP3zGvf.d.cts +128 -0
- package/dist/types-lEP3zGvf.d.ts +128 -0
- package/package.json +31 -1
|
@@ -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
|
+
};
|