mcpcm 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +356 -0
- package/dist/cli.js +1383 -0
- package/package.json +63 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,1383 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
5
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
|
|
8
|
+
// src/commands/add.ts
|
|
9
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
10
|
+
|
|
11
|
+
// src/agents.ts
|
|
12
|
+
import { homedir, platform } from "os";
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
import { existsSync } from "fs";
|
|
15
|
+
var home = homedir();
|
|
16
|
+
var isMac = platform() === "darwin";
|
|
17
|
+
var isWindows = platform() === "win32";
|
|
18
|
+
function getVscodeGlobalPath() {
|
|
19
|
+
if (isMac) {
|
|
20
|
+
return join(home, "Library/Application Support/Code/User/mcp.json");
|
|
21
|
+
} else if (isWindows) {
|
|
22
|
+
return join(home, "AppData/Roaming/Code/User/mcp.json");
|
|
23
|
+
} else {
|
|
24
|
+
return join(home, ".config/Code/User/mcp.json");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
var agents = {
|
|
28
|
+
cursor: {
|
|
29
|
+
name: "cursor",
|
|
30
|
+
displayName: "Cursor",
|
|
31
|
+
projectConfigPath: ".cursor/mcp.json",
|
|
32
|
+
globalConfigPath: join(home, ".cursor/mcp.json"),
|
|
33
|
+
configFormat: "json",
|
|
34
|
+
mcpConfigKey: "mcpServers",
|
|
35
|
+
detectInstalled: async () => existsSync(join(home, ".cursor"))
|
|
36
|
+
},
|
|
37
|
+
"claude-code": {
|
|
38
|
+
name: "claude-code",
|
|
39
|
+
displayName: "Claude Code",
|
|
40
|
+
projectConfigPath: ".mcp.json",
|
|
41
|
+
globalConfigPath: join(home, ".claude.json"),
|
|
42
|
+
configFormat: "json",
|
|
43
|
+
mcpConfigKey: "mcpServers",
|
|
44
|
+
detectInstalled: async () => existsSync(join(home, ".claude.json"))
|
|
45
|
+
},
|
|
46
|
+
antigravity: {
|
|
47
|
+
name: "antigravity",
|
|
48
|
+
displayName: "Antigravity",
|
|
49
|
+
projectConfigPath: ".gemini/mcp_config.json",
|
|
50
|
+
globalConfigPath: join(home, ".gemini/antigravity/mcp_config.json"),
|
|
51
|
+
configFormat: "json",
|
|
52
|
+
mcpConfigKey: "mcpServers",
|
|
53
|
+
detectInstalled: async () => existsSync(join(home, ".gemini/antigravity")) || existsSync(join(home, ".gemini"))
|
|
54
|
+
},
|
|
55
|
+
windsurf: {
|
|
56
|
+
name: "windsurf",
|
|
57
|
+
displayName: "Windsurf",
|
|
58
|
+
projectConfigPath: ".windsurf/mcp_config.json",
|
|
59
|
+
globalConfigPath: join(home, ".codeium/windsurf/mcp_config.json"),
|
|
60
|
+
configFormat: "json",
|
|
61
|
+
mcpConfigKey: "mcpServers",
|
|
62
|
+
detectInstalled: async () => existsSync(join(home, ".codeium/windsurf"))
|
|
63
|
+
},
|
|
64
|
+
vscode: {
|
|
65
|
+
name: "vscode",
|
|
66
|
+
displayName: "VS Code / GitHub Copilot",
|
|
67
|
+
projectConfigPath: ".vscode/mcp.json",
|
|
68
|
+
globalConfigPath: getVscodeGlobalPath(),
|
|
69
|
+
configFormat: "json",
|
|
70
|
+
mcpConfigKey: "servers",
|
|
71
|
+
detectInstalled: async () => {
|
|
72
|
+
if (isMac) {
|
|
73
|
+
return existsSync(join(home, "Library/Application Support/Code"));
|
|
74
|
+
} else if (isWindows) {
|
|
75
|
+
return existsSync(join(home, "AppData/Roaming/Code"));
|
|
76
|
+
}
|
|
77
|
+
return existsSync(join(home, ".config/Code"));
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
codex: {
|
|
81
|
+
name: "codex",
|
|
82
|
+
displayName: "Codex",
|
|
83
|
+
projectConfigPath: ".codex/config.toml",
|
|
84
|
+
globalConfigPath: join(home, ".codex/config.toml"),
|
|
85
|
+
configFormat: "toml",
|
|
86
|
+
mcpConfigKey: "mcp_servers",
|
|
87
|
+
detectInstalled: async () => existsSync(join(home, ".codex"))
|
|
88
|
+
},
|
|
89
|
+
opencode: {
|
|
90
|
+
name: "opencode",
|
|
91
|
+
displayName: "OpenCode",
|
|
92
|
+
projectConfigPath: null,
|
|
93
|
+
globalConfigPath: join(home, ".config/opencode/opencode.json"),
|
|
94
|
+
configFormat: "json",
|
|
95
|
+
mcpConfigKey: "mcpServers",
|
|
96
|
+
detectInstalled: async () => existsSync(join(home, ".config/opencode"))
|
|
97
|
+
},
|
|
98
|
+
"gemini-cli": {
|
|
99
|
+
name: "gemini-cli",
|
|
100
|
+
displayName: "Gemini CLI",
|
|
101
|
+
projectConfigPath: ".gemini/settings.json",
|
|
102
|
+
globalConfigPath: join(home, ".gemini/settings.json"),
|
|
103
|
+
configFormat: "json",
|
|
104
|
+
mcpConfigKey: "mcpServers",
|
|
105
|
+
detectInstalled: async () => existsSync(join(home, ".gemini"))
|
|
106
|
+
},
|
|
107
|
+
qoder: {
|
|
108
|
+
name: "qoder",
|
|
109
|
+
displayName: "Qoder",
|
|
110
|
+
projectConfigPath: null,
|
|
111
|
+
globalConfigPath: null,
|
|
112
|
+
// Managed via qodercli
|
|
113
|
+
configFormat: "json",
|
|
114
|
+
mcpConfigKey: null,
|
|
115
|
+
detectInstalled: async () => false
|
|
116
|
+
// Cannot detect programmatically
|
|
117
|
+
},
|
|
118
|
+
"qwen-code": {
|
|
119
|
+
name: "qwen-code",
|
|
120
|
+
displayName: "Qwen Code",
|
|
121
|
+
projectConfigPath: ".qwen/settings.json",
|
|
122
|
+
globalConfigPath: join(home, ".qwen/settings.json"),
|
|
123
|
+
configFormat: "json",
|
|
124
|
+
mcpConfigKey: "mcpServers",
|
|
125
|
+
detectInstalled: async () => existsSync(join(home, ".qwen"))
|
|
126
|
+
},
|
|
127
|
+
trae: {
|
|
128
|
+
name: "trae",
|
|
129
|
+
displayName: "Trae",
|
|
130
|
+
projectConfigPath: ".trae/mcp.json",
|
|
131
|
+
globalConfigPath: null,
|
|
132
|
+
configFormat: "json",
|
|
133
|
+
mcpConfigKey: "mcpServers",
|
|
134
|
+
detectInstalled: async () => existsSync(join(process.cwd(), ".trae"))
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
function getAllAgentTypes() {
|
|
138
|
+
return Object.keys(agents);
|
|
139
|
+
}
|
|
140
|
+
async function detectInstalledAgents() {
|
|
141
|
+
const installed = [];
|
|
142
|
+
for (const [type, config] of Object.entries(agents)) {
|
|
143
|
+
if (await config.detectInstalled()) {
|
|
144
|
+
installed.push(type);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return installed;
|
|
148
|
+
}
|
|
149
|
+
function getAgentConfig(type) {
|
|
150
|
+
return agents[type];
|
|
151
|
+
}
|
|
152
|
+
function isValidAgentType(type) {
|
|
153
|
+
return type in agents;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/config-reader.ts
|
|
157
|
+
import { readFileSync, existsSync as existsSync3 } from "fs";
|
|
158
|
+
import { join as join3 } from "path";
|
|
159
|
+
import { parse as parseToml } from "@iarna/toml";
|
|
160
|
+
|
|
161
|
+
// src/utils.ts
|
|
162
|
+
import { homedir as homedir2 } from "os";
|
|
163
|
+
import { join as join2, dirname } from "path";
|
|
164
|
+
import { existsSync as existsSync2, mkdirSync } from "fs";
|
|
165
|
+
var RESET = "\x1B[0m";
|
|
166
|
+
var BOLD = "\x1B[1m";
|
|
167
|
+
var DIM = "\x1B[38;5;102m";
|
|
168
|
+
var TEXT = "\x1B[38;5;145m";
|
|
169
|
+
var GREEN = "\x1B[38;5;78m";
|
|
170
|
+
var YELLOW = "\x1B[38;5;220m";
|
|
171
|
+
var RED = "\x1B[38;5;196m";
|
|
172
|
+
var CYAN = "\x1B[38;5;87m";
|
|
173
|
+
function expandPath(path) {
|
|
174
|
+
if (path.startsWith("~")) {
|
|
175
|
+
return join2(homedir2(), path.slice(1));
|
|
176
|
+
}
|
|
177
|
+
return path;
|
|
178
|
+
}
|
|
179
|
+
function ensureDir(filePath) {
|
|
180
|
+
const dir = dirname(filePath);
|
|
181
|
+
if (!existsSync2(dir)) {
|
|
182
|
+
mkdirSync(dir, { recursive: true });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function safeParseJson(content) {
|
|
186
|
+
try {
|
|
187
|
+
return JSON.parse(content);
|
|
188
|
+
} catch {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function formatServerConfig(name, config) {
|
|
193
|
+
const command = config["command"] || "unknown";
|
|
194
|
+
const args = config["args"] || [];
|
|
195
|
+
const argsStr = args.length > 0 ? ` ${args.join(" ")}` : "";
|
|
196
|
+
return `${CYAN}${name}${RESET}: ${DIM}${command}${argsStr}${RESET}`;
|
|
197
|
+
}
|
|
198
|
+
function success(message) {
|
|
199
|
+
console.log(`${GREEN}\u2713${RESET} ${message}`);
|
|
200
|
+
}
|
|
201
|
+
function error(message) {
|
|
202
|
+
console.log(`${RED}\u2717${RESET} ${message}`);
|
|
203
|
+
}
|
|
204
|
+
function warn(message) {
|
|
205
|
+
console.log(`${YELLOW}!${RESET} ${message}`);
|
|
206
|
+
}
|
|
207
|
+
function info(message) {
|
|
208
|
+
console.log(`${DIM}${message}${RESET}`);
|
|
209
|
+
}
|
|
210
|
+
function verbose(message, isVerbose) {
|
|
211
|
+
if (isVerbose) {
|
|
212
|
+
console.log(`${DIM}[verbose] ${message}${RESET}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// src/config-reader.ts
|
|
217
|
+
function readConfigFile(filePath, format, mcpConfigKey, verboseMode = false) {
|
|
218
|
+
const expandedPath = expandPath(filePath);
|
|
219
|
+
if (!existsSync3(expandedPath)) {
|
|
220
|
+
verbose(`Config file not found: ${expandedPath}`, verboseMode);
|
|
221
|
+
return { config: null, rawContent: null, path: expandedPath, exists: false };
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
const content = readFileSync(expandedPath, "utf-8");
|
|
225
|
+
let parsed;
|
|
226
|
+
if (format === "toml") {
|
|
227
|
+
parsed = parseToml(content);
|
|
228
|
+
} else {
|
|
229
|
+
const jsonParsed = safeParseJson(content);
|
|
230
|
+
if (!jsonParsed) {
|
|
231
|
+
return {
|
|
232
|
+
config: null,
|
|
233
|
+
rawContent: null,
|
|
234
|
+
path: expandedPath,
|
|
235
|
+
exists: true,
|
|
236
|
+
error: "Invalid JSON"
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
parsed = jsonParsed;
|
|
240
|
+
}
|
|
241
|
+
let mcpServers = {};
|
|
242
|
+
if (mcpConfigKey) {
|
|
243
|
+
const servers = parsed[mcpConfigKey];
|
|
244
|
+
if (servers && typeof servers === "object") {
|
|
245
|
+
mcpServers = servers;
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
if (parsed["mcpServers"] && typeof parsed["mcpServers"] === "object") {
|
|
249
|
+
mcpServers = parsed["mcpServers"];
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
verbose(`Read config from ${expandedPath}: ${Object.keys(mcpServers).length} servers`, verboseMode);
|
|
253
|
+
return {
|
|
254
|
+
config: { mcpServers },
|
|
255
|
+
rawContent: parsed,
|
|
256
|
+
path: expandedPath,
|
|
257
|
+
exists: true
|
|
258
|
+
};
|
|
259
|
+
} catch (err) {
|
|
260
|
+
return {
|
|
261
|
+
config: null,
|
|
262
|
+
rawContent: null,
|
|
263
|
+
path: expandedPath,
|
|
264
|
+
exists: true,
|
|
265
|
+
error: err instanceof Error ? err.message : "Unknown error"
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function readGlobalConfig(agentType, verboseMode = false) {
|
|
270
|
+
const agent = getAgentConfig(agentType);
|
|
271
|
+
if (!agent.globalConfigPath) {
|
|
272
|
+
return {
|
|
273
|
+
config: null,
|
|
274
|
+
rawContent: null,
|
|
275
|
+
path: "",
|
|
276
|
+
exists: false,
|
|
277
|
+
error: "No global config path"
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
return readConfigFile(
|
|
281
|
+
agent.globalConfigPath,
|
|
282
|
+
agent.configFormat,
|
|
283
|
+
agent.mcpConfigKey,
|
|
284
|
+
verboseMode
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
function readProjectConfig(agentType, projectDir = process.cwd(), verboseMode = false) {
|
|
288
|
+
const agent = getAgentConfig(agentType);
|
|
289
|
+
if (!agent.projectConfigPath) {
|
|
290
|
+
return {
|
|
291
|
+
config: null,
|
|
292
|
+
rawContent: null,
|
|
293
|
+
path: "",
|
|
294
|
+
exists: false,
|
|
295
|
+
error: "No project config path"
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
const fullPath = join3(projectDir, agent.projectConfigPath);
|
|
299
|
+
return readConfigFile(fullPath, agent.configFormat, agent.mcpConfigKey, verboseMode);
|
|
300
|
+
}
|
|
301
|
+
function parseMcpConfigString(jsonString) {
|
|
302
|
+
const parsed = safeParseJson(jsonString);
|
|
303
|
+
if (!parsed) return null;
|
|
304
|
+
if (parsed["mcpServers"] && typeof parsed["mcpServers"] === "object") {
|
|
305
|
+
return { mcpServers: parsed["mcpServers"] };
|
|
306
|
+
}
|
|
307
|
+
const firstValue = Object.values(parsed)[0];
|
|
308
|
+
if (firstValue && typeof firstValue === "object" && "command" in firstValue) {
|
|
309
|
+
return { mcpServers: parsed };
|
|
310
|
+
}
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// src/config-writer.ts
|
|
315
|
+
import { writeFileSync, readFileSync as readFileSync2, existsSync as existsSync4 } from "fs";
|
|
316
|
+
import { join as join4 } from "path";
|
|
317
|
+
import { parse as parseToml2, stringify as stringifyToml } from "@iarna/toml";
|
|
318
|
+
function mergeMcpConfigs(existing, incoming, replace = false) {
|
|
319
|
+
if (replace) {
|
|
320
|
+
return {
|
|
321
|
+
mcpServers: {
|
|
322
|
+
...existing.mcpServers,
|
|
323
|
+
...incoming.mcpServers
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
const merged = { ...existing.mcpServers };
|
|
328
|
+
for (const [name, config] of Object.entries(incoming.mcpServers)) {
|
|
329
|
+
if (merged[name]) {
|
|
330
|
+
merged[name] = { ...merged[name], ...config };
|
|
331
|
+
} else {
|
|
332
|
+
merged[name] = config;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return { mcpServers: merged };
|
|
336
|
+
}
|
|
337
|
+
function removeServersFromConfig(config, serverNames) {
|
|
338
|
+
const filtered = { ...config.mcpServers };
|
|
339
|
+
for (const name of serverNames) {
|
|
340
|
+
delete filtered[name];
|
|
341
|
+
}
|
|
342
|
+
return { mcpServers: filtered };
|
|
343
|
+
}
|
|
344
|
+
function writeConfigFile(filePath, config, format, mcpConfigKey, existingContent = null, verboseMode = false) {
|
|
345
|
+
const expandedPath = expandPath(filePath);
|
|
346
|
+
try {
|
|
347
|
+
ensureDir(expandedPath);
|
|
348
|
+
let content;
|
|
349
|
+
if (existingContent) {
|
|
350
|
+
content = { ...existingContent };
|
|
351
|
+
} else if (existsSync4(expandedPath)) {
|
|
352
|
+
const existing = readFileSync2(expandedPath, "utf-8");
|
|
353
|
+
if (format === "toml") {
|
|
354
|
+
content = parseToml2(existing);
|
|
355
|
+
} else {
|
|
356
|
+
content = safeParseJson(existing) || {};
|
|
357
|
+
}
|
|
358
|
+
} else {
|
|
359
|
+
content = {};
|
|
360
|
+
}
|
|
361
|
+
const key = mcpConfigKey || "mcpServers";
|
|
362
|
+
content[key] = config.mcpServers;
|
|
363
|
+
let output;
|
|
364
|
+
if (format === "toml") {
|
|
365
|
+
output = stringifyToml(content);
|
|
366
|
+
} else {
|
|
367
|
+
output = JSON.stringify(content, null, 2);
|
|
368
|
+
}
|
|
369
|
+
writeFileSync(expandedPath, output, "utf-8");
|
|
370
|
+
verbose(`Wrote config to ${expandedPath}`, verboseMode);
|
|
371
|
+
return { success: true, path: expandedPath };
|
|
372
|
+
} catch (err) {
|
|
373
|
+
return {
|
|
374
|
+
success: false,
|
|
375
|
+
path: expandedPath,
|
|
376
|
+
error: err instanceof Error ? err.message : "Unknown error"
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function writeGlobalConfig(agentType, config, existingContent = null, verboseMode = false) {
|
|
381
|
+
const agent = getAgentConfig(agentType);
|
|
382
|
+
if (!agent.globalConfigPath) {
|
|
383
|
+
return { success: false, path: "", error: "No global config path" };
|
|
384
|
+
}
|
|
385
|
+
return writeConfigFile(
|
|
386
|
+
agent.globalConfigPath,
|
|
387
|
+
config,
|
|
388
|
+
agent.configFormat,
|
|
389
|
+
agent.mcpConfigKey,
|
|
390
|
+
existingContent,
|
|
391
|
+
verboseMode
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
function writeProjectConfig(agentType, config, existingContent = null, projectDir = process.cwd(), verboseMode = false) {
|
|
395
|
+
const agent = getAgentConfig(agentType);
|
|
396
|
+
if (!agent.projectConfigPath) {
|
|
397
|
+
return { success: false, path: "", error: "No project config path" };
|
|
398
|
+
}
|
|
399
|
+
const fullPath = join4(projectDir, agent.projectConfigPath);
|
|
400
|
+
return writeConfigFile(
|
|
401
|
+
fullPath,
|
|
402
|
+
config,
|
|
403
|
+
agent.configFormat,
|
|
404
|
+
agent.mcpConfigKey,
|
|
405
|
+
existingContent,
|
|
406
|
+
verboseMode
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// src/commands/add.ts
|
|
411
|
+
function parseAddOptions(args) {
|
|
412
|
+
const options = {};
|
|
413
|
+
let input = null;
|
|
414
|
+
for (let i = 0; i < args.length; i++) {
|
|
415
|
+
const arg = args[i];
|
|
416
|
+
if (arg === "--agent" || arg === "-a") {
|
|
417
|
+
options.agents = [];
|
|
418
|
+
while (i + 1 < args.length && !args[i + 1]?.startsWith("-")) {
|
|
419
|
+
i++;
|
|
420
|
+
const agentName = args[i];
|
|
421
|
+
if (agentName && isValidAgentType(agentName)) {
|
|
422
|
+
options.agents.push(agentName);
|
|
423
|
+
} else if (agentName) {
|
|
424
|
+
warn(`Unknown agent: ${agentName}`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
} else if (arg === "--global" || arg === "-g") {
|
|
428
|
+
options.global = true;
|
|
429
|
+
} else if (arg === "--workspace" || arg === "-w") {
|
|
430
|
+
options.workspace = true;
|
|
431
|
+
} else if (arg === "--replace" || arg === "-r") {
|
|
432
|
+
options.replace = true;
|
|
433
|
+
} else if (arg === "--file" || arg === "-f") {
|
|
434
|
+
options.file = args[++i];
|
|
435
|
+
} else if (arg === "--verbose" || arg === "-v") {
|
|
436
|
+
options.verbose = true;
|
|
437
|
+
} else if (!arg?.startsWith("-") && !input) {
|
|
438
|
+
input = arg ?? null;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return { input, options };
|
|
442
|
+
}
|
|
443
|
+
async function runAdd(input, options) {
|
|
444
|
+
verbose("Running add command", options.verbose ?? false);
|
|
445
|
+
let mcpConfig = null;
|
|
446
|
+
if (options.file) {
|
|
447
|
+
try {
|
|
448
|
+
const content = readFileSync3(options.file, "utf-8");
|
|
449
|
+
mcpConfig = parseMcpConfigString(content);
|
|
450
|
+
if (!mcpConfig) {
|
|
451
|
+
error(`Invalid MCP config in file: ${options.file}`);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
verbose(`Read config from file: ${options.file}`, options.verbose ?? false);
|
|
455
|
+
} catch (err) {
|
|
456
|
+
error(`Failed to read file: ${options.file}`);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
} else if (input) {
|
|
460
|
+
mcpConfig = parseMcpConfigString(input);
|
|
461
|
+
if (!mcpConfig) {
|
|
462
|
+
error("Invalid MCP config JSON");
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
} else {
|
|
466
|
+
error("No MCP config provided. Use JSON string or --file option.");
|
|
467
|
+
console.log(`
|
|
468
|
+
${DIM}Example:${RESET}`);
|
|
469
|
+
console.log(
|
|
470
|
+
` mcpcm add '{"mcpServers":{"my-server":{"command":"node","args":["/path/to/server.js"]}}}' --agent cursor`
|
|
471
|
+
);
|
|
472
|
+
console.log(` mcpcm add --file mcp.json --global`);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const serverNames = Object.keys(mcpConfig.mcpServers);
|
|
476
|
+
info(`Adding ${serverNames.length} MCP server(s): ${serverNames.join(", ")}`);
|
|
477
|
+
let targetAgents = [];
|
|
478
|
+
if (options.agents && options.agents.length > 0) {
|
|
479
|
+
targetAgents = options.agents;
|
|
480
|
+
} else if (options.global) {
|
|
481
|
+
targetAgents = await detectInstalledAgents();
|
|
482
|
+
if (targetAgents.length === 0) {
|
|
483
|
+
warn("No installed agents detected. Specify agents with --agent.");
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
} else if (options.workspace) {
|
|
487
|
+
targetAgents = getAllAgentTypes().filter((t) => agents[t].projectConfigPath !== null);
|
|
488
|
+
} else {
|
|
489
|
+
error("Specify target with --agent, --global, or --workspace");
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
verbose(`Target agents: ${targetAgents.join(", ")}`, options.verbose ?? false);
|
|
493
|
+
let successCount = 0;
|
|
494
|
+
let failCount = 0;
|
|
495
|
+
const allSkippedServers = /* @__PURE__ */ new Map();
|
|
496
|
+
for (const agentType of targetAgents) {
|
|
497
|
+
const agent = agents[agentType];
|
|
498
|
+
if (options.workspace) {
|
|
499
|
+
if (!agent.projectConfigPath) {
|
|
500
|
+
verbose(`${agent.displayName} doesn't support project config`, options.verbose ?? false);
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
const existing = readProjectConfig(agentType, process.cwd(), options.verbose);
|
|
504
|
+
const existingServers = Object.keys(existing.config?.mcpServers || {});
|
|
505
|
+
const incomingServers = Object.keys(mcpConfig.mcpServers);
|
|
506
|
+
const duplicates = incomingServers.filter((s) => existingServers.includes(s));
|
|
507
|
+
const newServers = incomingServers.filter((s) => !existingServers.includes(s));
|
|
508
|
+
for (const dup of duplicates) {
|
|
509
|
+
const agents2 = allSkippedServers.get(dup) || [];
|
|
510
|
+
agents2.push(agent.displayName);
|
|
511
|
+
allSkippedServers.set(dup, agents2);
|
|
512
|
+
}
|
|
513
|
+
if (newServers.length === 0) {
|
|
514
|
+
warn(`${agent.displayName}: all servers already exist (skipped)`);
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
const filteredConfig = {
|
|
518
|
+
mcpServers: Object.fromEntries(
|
|
519
|
+
Object.entries(mcpConfig.mcpServers).filter(([name]) => newServers.includes(name))
|
|
520
|
+
)
|
|
521
|
+
};
|
|
522
|
+
const merged = mergeMcpConfigs(
|
|
523
|
+
existing.config || { mcpServers: {} },
|
|
524
|
+
filteredConfig,
|
|
525
|
+
options.replace
|
|
526
|
+
);
|
|
527
|
+
const result = writeProjectConfig(
|
|
528
|
+
agentType,
|
|
529
|
+
merged,
|
|
530
|
+
existing.rawContent,
|
|
531
|
+
process.cwd(),
|
|
532
|
+
options.verbose
|
|
533
|
+
);
|
|
534
|
+
if (result.success) {
|
|
535
|
+
if (duplicates.length > 0) {
|
|
536
|
+
success(
|
|
537
|
+
`${agent.displayName}: added ${newServers.join(", ")} (skipped: ${duplicates.join(", ")})`
|
|
538
|
+
);
|
|
539
|
+
} else {
|
|
540
|
+
success(`${agent.displayName}: ${result.path}`);
|
|
541
|
+
}
|
|
542
|
+
successCount++;
|
|
543
|
+
} else {
|
|
544
|
+
error(`${agent.displayName}: ${result.error}`);
|
|
545
|
+
failCount++;
|
|
546
|
+
}
|
|
547
|
+
} else {
|
|
548
|
+
if (!agent.globalConfigPath) {
|
|
549
|
+
verbose(`${agent.displayName} doesn't support global config`, options.verbose ?? false);
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
const existing = readGlobalConfig(agentType, options.verbose);
|
|
553
|
+
const existingServers = Object.keys(existing.config?.mcpServers || {});
|
|
554
|
+
const incomingServers = Object.keys(mcpConfig.mcpServers);
|
|
555
|
+
const duplicates = incomingServers.filter((s) => existingServers.includes(s));
|
|
556
|
+
const newServers = incomingServers.filter((s) => !existingServers.includes(s));
|
|
557
|
+
for (const dup of duplicates) {
|
|
558
|
+
const agents2 = allSkippedServers.get(dup) || [];
|
|
559
|
+
agents2.push(agent.displayName);
|
|
560
|
+
allSkippedServers.set(dup, agents2);
|
|
561
|
+
}
|
|
562
|
+
if (newServers.length === 0) {
|
|
563
|
+
warn(`${agent.displayName}: all servers already exist (skipped)`);
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
const filteredConfig = {
|
|
567
|
+
mcpServers: Object.fromEntries(
|
|
568
|
+
Object.entries(mcpConfig.mcpServers).filter(([name]) => newServers.includes(name))
|
|
569
|
+
)
|
|
570
|
+
};
|
|
571
|
+
const merged = mergeMcpConfigs(
|
|
572
|
+
existing.config || { mcpServers: {} },
|
|
573
|
+
filteredConfig,
|
|
574
|
+
options.replace
|
|
575
|
+
);
|
|
576
|
+
const result = writeGlobalConfig(agentType, merged, existing.rawContent, options.verbose);
|
|
577
|
+
if (result.success) {
|
|
578
|
+
if (duplicates.length > 0) {
|
|
579
|
+
success(
|
|
580
|
+
`${agent.displayName}: added ${newServers.join(", ")} (skipped: ${duplicates.join(", ")})`
|
|
581
|
+
);
|
|
582
|
+
} else {
|
|
583
|
+
success(`${agent.displayName}: ${result.path}`);
|
|
584
|
+
}
|
|
585
|
+
successCount++;
|
|
586
|
+
} else {
|
|
587
|
+
error(`${agent.displayName}: ${result.error}`);
|
|
588
|
+
failCount++;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
console.log();
|
|
593
|
+
if (successCount > 0) {
|
|
594
|
+
success(`Added to ${successCount} agent(s)`);
|
|
595
|
+
}
|
|
596
|
+
if (failCount > 0) {
|
|
597
|
+
error(`Failed for ${failCount} agent(s)`);
|
|
598
|
+
}
|
|
599
|
+
if (allSkippedServers.size > 0) {
|
|
600
|
+
console.log();
|
|
601
|
+
warn(`Skipped ${allSkippedServers.size} existing server(s):`);
|
|
602
|
+
for (const [serverName, agentNames] of allSkippedServers) {
|
|
603
|
+
console.log(` ${DIM}${serverName}${RESET} \u2192 ${agentNames.join(", ")}`);
|
|
604
|
+
}
|
|
605
|
+
console.log(`
|
|
606
|
+
${DIM}Use 'mcpcm update' to modify existing servers.${RESET}`);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// src/commands/del.ts
|
|
611
|
+
function parseDelOptions(args) {
|
|
612
|
+
const options = {};
|
|
613
|
+
const serverNames = [];
|
|
614
|
+
for (let i = 0; i < args.length; i++) {
|
|
615
|
+
const arg = args[i];
|
|
616
|
+
if (arg === "--agent" || arg === "-a") {
|
|
617
|
+
options.agents = [];
|
|
618
|
+
while (i + 1 < args.length && !args[i + 1]?.startsWith("-")) {
|
|
619
|
+
i++;
|
|
620
|
+
const agentName = args[i];
|
|
621
|
+
if (agentName && isValidAgentType(agentName)) {
|
|
622
|
+
options.agents.push(agentName);
|
|
623
|
+
} else if (agentName) {
|
|
624
|
+
warn(`Unknown agent: ${agentName}`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
} else if (arg === "--global" || arg === "-g") {
|
|
628
|
+
options.global = true;
|
|
629
|
+
} else if (arg === "--workspace" || arg === "-w") {
|
|
630
|
+
options.workspace = true;
|
|
631
|
+
} else if (arg === "--verbose" || arg === "-v") {
|
|
632
|
+
options.verbose = true;
|
|
633
|
+
} else if (!arg?.startsWith("-")) {
|
|
634
|
+
if (typeof arg === "string") {
|
|
635
|
+
serverNames.push(arg);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return { serverNames, options };
|
|
640
|
+
}
|
|
641
|
+
async function runDel(serverNames, options) {
|
|
642
|
+
verbose("Running del command", options.verbose ?? false);
|
|
643
|
+
if (serverNames.length === 0) {
|
|
644
|
+
error("No server name provided");
|
|
645
|
+
console.log(`
|
|
646
|
+
${DIM}Example:${RESET}`);
|
|
647
|
+
console.log(` mcpcm del my-server --agent cursor`);
|
|
648
|
+
console.log(` mcpcm del my-server --global`);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
info(`Deleting MCP server(s): ${serverNames.join(", ")}`);
|
|
652
|
+
let targetAgents = [];
|
|
653
|
+
if (options.agents && options.agents.length > 0) {
|
|
654
|
+
targetAgents = options.agents;
|
|
655
|
+
} else if (options.global) {
|
|
656
|
+
targetAgents = await detectInstalledAgents();
|
|
657
|
+
if (targetAgents.length === 0) {
|
|
658
|
+
warn("No installed agents detected.");
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
} else if (options.workspace) {
|
|
662
|
+
targetAgents = getAllAgentTypes().filter((t) => agents[t].projectConfigPath !== null);
|
|
663
|
+
} else {
|
|
664
|
+
error("Specify target with --agent, --global, or --workspace");
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
let successCount = 0;
|
|
668
|
+
let failCount = 0;
|
|
669
|
+
const allNotFoundServers = /* @__PURE__ */ new Map();
|
|
670
|
+
for (const agentType of targetAgents) {
|
|
671
|
+
const agent = agents[agentType];
|
|
672
|
+
if (options.workspace) {
|
|
673
|
+
if (!agent.projectConfigPath) continue;
|
|
674
|
+
const existing = readProjectConfig(agentType, process.cwd(), options.verbose);
|
|
675
|
+
if (!existing.config) continue;
|
|
676
|
+
const existingServers = Object.keys(existing.config.mcpServers || {});
|
|
677
|
+
const toDelete = serverNames.filter((s) => existingServers.includes(s));
|
|
678
|
+
const notFound = serverNames.filter((s) => !existingServers.includes(s));
|
|
679
|
+
for (const nf of notFound) {
|
|
680
|
+
const agents2 = allNotFoundServers.get(nf) || [];
|
|
681
|
+
agents2.push(agent.displayName);
|
|
682
|
+
allNotFoundServers.set(nf, agents2);
|
|
683
|
+
}
|
|
684
|
+
if (toDelete.length === 0) {
|
|
685
|
+
warn(`${agent.displayName}: no matching servers found (skipped)`);
|
|
686
|
+
continue;
|
|
687
|
+
}
|
|
688
|
+
const updated = removeServersFromConfig(existing.config, toDelete);
|
|
689
|
+
const result = writeProjectConfig(
|
|
690
|
+
agentType,
|
|
691
|
+
updated,
|
|
692
|
+
existing.rawContent,
|
|
693
|
+
process.cwd(),
|
|
694
|
+
options.verbose
|
|
695
|
+
);
|
|
696
|
+
if (result.success) {
|
|
697
|
+
if (notFound.length > 0) {
|
|
698
|
+
success(
|
|
699
|
+
`${agent.displayName}: deleted ${toDelete.join(", ")} (not found: ${notFound.join(", ")})`
|
|
700
|
+
);
|
|
701
|
+
} else {
|
|
702
|
+
success(`${agent.displayName}: deleted ${toDelete.join(", ")}`);
|
|
703
|
+
}
|
|
704
|
+
successCount++;
|
|
705
|
+
} else {
|
|
706
|
+
error(`${agent.displayName}: ${result.error}`);
|
|
707
|
+
failCount++;
|
|
708
|
+
}
|
|
709
|
+
} else {
|
|
710
|
+
if (!agent.globalConfigPath) continue;
|
|
711
|
+
const existing = readGlobalConfig(agentType, options.verbose);
|
|
712
|
+
if (!existing.config) continue;
|
|
713
|
+
const existingServers = Object.keys(existing.config.mcpServers || {});
|
|
714
|
+
const toDelete = serverNames.filter((s) => existingServers.includes(s));
|
|
715
|
+
const notFound = serverNames.filter((s) => !existingServers.includes(s));
|
|
716
|
+
for (const nf of notFound) {
|
|
717
|
+
const agents2 = allNotFoundServers.get(nf) || [];
|
|
718
|
+
agents2.push(agent.displayName);
|
|
719
|
+
allNotFoundServers.set(nf, agents2);
|
|
720
|
+
}
|
|
721
|
+
if (toDelete.length === 0) {
|
|
722
|
+
warn(`${agent.displayName}: no matching servers found (skipped)`);
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
const updated = removeServersFromConfig(existing.config, toDelete);
|
|
726
|
+
const result = writeGlobalConfig(agentType, updated, existing.rawContent, options.verbose);
|
|
727
|
+
if (result.success) {
|
|
728
|
+
if (notFound.length > 0) {
|
|
729
|
+
success(
|
|
730
|
+
`${agent.displayName}: deleted ${toDelete.join(", ")} (not found: ${notFound.join(", ")})`
|
|
731
|
+
);
|
|
732
|
+
} else {
|
|
733
|
+
success(`${agent.displayName}: deleted ${toDelete.join(", ")}`);
|
|
734
|
+
}
|
|
735
|
+
successCount++;
|
|
736
|
+
} else {
|
|
737
|
+
error(`${agent.displayName}: ${result.error}`);
|
|
738
|
+
failCount++;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
console.log();
|
|
743
|
+
if (successCount > 0) {
|
|
744
|
+
success(`Deleted from ${successCount} agent(s)`);
|
|
745
|
+
}
|
|
746
|
+
if (failCount > 0) {
|
|
747
|
+
error(`Failed for ${failCount} agent(s)`);
|
|
748
|
+
}
|
|
749
|
+
if (allNotFoundServers.size > 0) {
|
|
750
|
+
console.log();
|
|
751
|
+
warn(`${allNotFoundServers.size} server(s) not found:`);
|
|
752
|
+
for (const [serverName, agentNames] of allNotFoundServers) {
|
|
753
|
+
console.log(` ${DIM}${serverName}${RESET} \u2192 ${agentNames.join(", ")}`);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// src/commands/list.ts
|
|
759
|
+
function parseListOptions(args) {
|
|
760
|
+
const options = {};
|
|
761
|
+
for (let i = 0; i < args.length; i++) {
|
|
762
|
+
const arg = args[i];
|
|
763
|
+
if (arg === "--agent" || arg === "-a") {
|
|
764
|
+
options.agents = [];
|
|
765
|
+
while (i + 1 < args.length && !args[i + 1]?.startsWith("-")) {
|
|
766
|
+
i++;
|
|
767
|
+
const agentName = args[i];
|
|
768
|
+
if (agentName && isValidAgentType(agentName)) {
|
|
769
|
+
options.agents.push(agentName);
|
|
770
|
+
} else if (agentName) {
|
|
771
|
+
warn(`Unknown agent: ${agentName}`);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
} else if (arg === "--global" || arg === "-g") {
|
|
775
|
+
options.global = true;
|
|
776
|
+
} else if (arg === "--workspace" || arg === "-w") {
|
|
777
|
+
options.workspace = true;
|
|
778
|
+
} else if (arg === "--verbose" || arg === "-v") {
|
|
779
|
+
options.verbose = true;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
return options;
|
|
783
|
+
}
|
|
784
|
+
async function runList(options) {
|
|
785
|
+
let targetAgents = [];
|
|
786
|
+
if (options.agents && options.agents.length > 0) {
|
|
787
|
+
targetAgents = options.agents;
|
|
788
|
+
} else {
|
|
789
|
+
targetAgents = await detectInstalledAgents();
|
|
790
|
+
if (targetAgents.length === 0) {
|
|
791
|
+
info("No installed agents detected.");
|
|
792
|
+
console.log(`
|
|
793
|
+
${DIM}Supported agents:${RESET}`);
|
|
794
|
+
for (const type of getAllAgentTypes()) {
|
|
795
|
+
console.log(` ${agents[type].displayName} (--agent ${type})`);
|
|
796
|
+
}
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
let totalServers = 0;
|
|
801
|
+
for (const agentType of targetAgents) {
|
|
802
|
+
const agent = agents[agentType];
|
|
803
|
+
let hasOutput = false;
|
|
804
|
+
if (!options.workspace) {
|
|
805
|
+
const globalConfig = readGlobalConfig(agentType, options.verbose);
|
|
806
|
+
if (globalConfig.config && Object.keys(globalConfig.config.mcpServers).length > 0) {
|
|
807
|
+
if (!hasOutput) {
|
|
808
|
+
console.log(`
|
|
809
|
+
${BOLD}${agent.displayName}${RESET}`);
|
|
810
|
+
hasOutput = true;
|
|
811
|
+
}
|
|
812
|
+
console.log(` ${DIM}Global:${RESET} ${globalConfig.path}`);
|
|
813
|
+
for (const [name, config] of Object.entries(globalConfig.config.mcpServers)) {
|
|
814
|
+
console.log(` ${formatServerConfig(name, config)}`);
|
|
815
|
+
totalServers++;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
if (!options.global) {
|
|
820
|
+
const projectConfig = readProjectConfig(agentType, process.cwd(), options.verbose);
|
|
821
|
+
if (projectConfig.config && Object.keys(projectConfig.config.mcpServers).length > 0) {
|
|
822
|
+
if (!hasOutput) {
|
|
823
|
+
console.log(`
|
|
824
|
+
${BOLD}${agent.displayName}${RESET}`);
|
|
825
|
+
hasOutput = true;
|
|
826
|
+
}
|
|
827
|
+
console.log(` ${DIM}Project:${RESET} ${projectConfig.path}`);
|
|
828
|
+
for (const [name, config] of Object.entries(projectConfig.config.mcpServers)) {
|
|
829
|
+
console.log(` ${formatServerConfig(name, config)}`);
|
|
830
|
+
totalServers++;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
console.log();
|
|
836
|
+
if (totalServers > 0) {
|
|
837
|
+
console.log(`${GREEN}Total:${RESET} ${totalServers} MCP server(s)`);
|
|
838
|
+
} else {
|
|
839
|
+
info("No MCP servers configured");
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// src/commands/find.ts
|
|
844
|
+
async function runFind(args) {
|
|
845
|
+
const serverName = args[0];
|
|
846
|
+
const verbose2 = args.includes("--verbose") || args.includes("-v");
|
|
847
|
+
if (!serverName) {
|
|
848
|
+
info("Usage: mcpcm find <server-name>");
|
|
849
|
+
console.log(`
|
|
850
|
+
${DIM}Example:${RESET}`);
|
|
851
|
+
console.log(` mcpcm find easeim`);
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
console.log(`
|
|
855
|
+
${DIM}Searching for "${serverName}"...${RESET}
|
|
856
|
+
`);
|
|
857
|
+
const results = [];
|
|
858
|
+
for (const agentType of getAllAgentTypes()) {
|
|
859
|
+
const agent = agents[agentType];
|
|
860
|
+
if (agent.globalConfigPath) {
|
|
861
|
+
const globalConfig = readGlobalConfig(agentType, verbose2);
|
|
862
|
+
if (globalConfig.config?.mcpServers[serverName]) {
|
|
863
|
+
results.push({
|
|
864
|
+
agent: agentType,
|
|
865
|
+
scope: "global",
|
|
866
|
+
path: globalConfig.path,
|
|
867
|
+
config: globalConfig.config.mcpServers[serverName]
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
if (agent.projectConfigPath) {
|
|
872
|
+
const projectConfig = readProjectConfig(agentType, process.cwd(), verbose2);
|
|
873
|
+
if (projectConfig.config?.mcpServers[serverName]) {
|
|
874
|
+
results.push({
|
|
875
|
+
agent: agentType,
|
|
876
|
+
scope: "project",
|
|
877
|
+
path: projectConfig.path,
|
|
878
|
+
config: projectConfig.config.mcpServers[serverName]
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
if (results.length === 0) {
|
|
884
|
+
info(`"${serverName}" not found in any MCP configuration`);
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
console.log(`${GREEN}\u2713${RESET} ${BOLD}${serverName}${RESET} found in:
|
|
888
|
+
`);
|
|
889
|
+
for (const result of results) {
|
|
890
|
+
const agent = agents[result.agent];
|
|
891
|
+
const scopeLabel = result.scope === "global" ? "global" : "project";
|
|
892
|
+
console.log(
|
|
893
|
+
` ${CYAN}${agent.displayName}${RESET} (${scopeLabel}): ${DIM}${result.path}${RESET}`
|
|
894
|
+
);
|
|
895
|
+
if (verbose2) {
|
|
896
|
+
const config = result.config;
|
|
897
|
+
console.log(` ${DIM}command:${RESET} ${config.command}`);
|
|
898
|
+
if (config.args && config.args.length > 0) {
|
|
899
|
+
console.log(` ${DIM}args:${RESET} ${config.args.join(" ")}`);
|
|
900
|
+
}
|
|
901
|
+
if (config.env) {
|
|
902
|
+
console.log(` ${DIM}env:${RESET} ${JSON.stringify(config.env)}`);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
console.log();
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// src/commands/sync.ts
|
|
910
|
+
function parseSyncOptions(args) {
|
|
911
|
+
let from = null;
|
|
912
|
+
const to = [];
|
|
913
|
+
let toAll = false;
|
|
914
|
+
let verboseMode = false;
|
|
915
|
+
for (let i = 0; i < args.length; i++) {
|
|
916
|
+
const arg = args[i];
|
|
917
|
+
if (arg === "--from" || arg === "-f") {
|
|
918
|
+
const agentName = args[++i];
|
|
919
|
+
if (agentName && isValidAgentType(agentName)) {
|
|
920
|
+
from = agentName;
|
|
921
|
+
} else {
|
|
922
|
+
warn(`Unknown agent: ${agentName}`);
|
|
923
|
+
}
|
|
924
|
+
} else if (arg === "--to" || arg === "-t") {
|
|
925
|
+
while (i + 1 < args.length && !args[i + 1]?.startsWith("-")) {
|
|
926
|
+
i++;
|
|
927
|
+
const agentName = args[i];
|
|
928
|
+
if (agentName && isValidAgentType(agentName)) {
|
|
929
|
+
to.push(agentName);
|
|
930
|
+
} else if (agentName) {
|
|
931
|
+
warn(`Unknown agent: ${agentName}`);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
} else if (arg === "--to-all") {
|
|
935
|
+
toAll = true;
|
|
936
|
+
} else if (arg === "--verbose" || arg === "-v") {
|
|
937
|
+
verboseMode = true;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
if (!from) {
|
|
941
|
+
error("Missing --from agent");
|
|
942
|
+
return null;
|
|
943
|
+
}
|
|
944
|
+
return { from, to: to.length > 0 ? to : void 0, toAll, verbose: verboseMode };
|
|
945
|
+
}
|
|
946
|
+
async function runSync(options) {
|
|
947
|
+
verbose("Running sync command", options.verbose ?? false);
|
|
948
|
+
const sourceConfig = readGlobalConfig(options.from, options.verbose);
|
|
949
|
+
if (!sourceConfig.config || Object.keys(sourceConfig.config.mcpServers).length === 0) {
|
|
950
|
+
error(`No MCP servers found in ${agents[options.from].displayName}'s global config`);
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
info(
|
|
954
|
+
`Syncing ${Object.keys(sourceConfig.config.mcpServers).length} server(s) from ${agents[options.from].displayName}`
|
|
955
|
+
);
|
|
956
|
+
let targetAgents = [];
|
|
957
|
+
if (options.toAll) {
|
|
958
|
+
targetAgents = await detectInstalledAgents();
|
|
959
|
+
targetAgents = targetAgents.filter((a) => a !== options.from);
|
|
960
|
+
} else if (options.to && options.to.length > 0) {
|
|
961
|
+
targetAgents = options.to.filter((a) => a !== options.from);
|
|
962
|
+
} else {
|
|
963
|
+
error("Specify target with --to or --to-all");
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
if (targetAgents.length === 0) {
|
|
967
|
+
warn("No target agents to sync to");
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
let successCount = 0;
|
|
971
|
+
let failCount = 0;
|
|
972
|
+
for (const agentType of targetAgents) {
|
|
973
|
+
const agent = agents[agentType];
|
|
974
|
+
if (!agent.globalConfigPath) {
|
|
975
|
+
verbose(`${agent.displayName} doesn't support global config`, options.verbose ?? false);
|
|
976
|
+
continue;
|
|
977
|
+
}
|
|
978
|
+
const existing = readGlobalConfig(agentType, options.verbose);
|
|
979
|
+
const merged = mergeMcpConfigs(
|
|
980
|
+
existing.config || { mcpServers: {} },
|
|
981
|
+
sourceConfig.config,
|
|
982
|
+
false
|
|
983
|
+
// Always merge, don't replace
|
|
984
|
+
);
|
|
985
|
+
const result = writeGlobalConfig(agentType, merged, existing.rawContent, options.verbose);
|
|
986
|
+
if (result.success) {
|
|
987
|
+
success(`${agent.displayName}: ${result.path}`);
|
|
988
|
+
successCount++;
|
|
989
|
+
} else {
|
|
990
|
+
error(`${agent.displayName}: ${result.error}`);
|
|
991
|
+
failCount++;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
console.log();
|
|
995
|
+
if (successCount > 0) {
|
|
996
|
+
success(`Synced to ${successCount} agent(s)`);
|
|
997
|
+
}
|
|
998
|
+
if (failCount > 0) {
|
|
999
|
+
error(`Failed for ${failCount} agent(s)`);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// src/commands/update.ts
|
|
1004
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
1005
|
+
function parseUpdateOptions(args) {
|
|
1006
|
+
const options = {};
|
|
1007
|
+
let input = null;
|
|
1008
|
+
for (let i = 0; i < args.length; i++) {
|
|
1009
|
+
const arg = args[i];
|
|
1010
|
+
if (arg === "--agent" || arg === "-a") {
|
|
1011
|
+
options.agents = [];
|
|
1012
|
+
while (i + 1 < args.length && !args[i + 1]?.startsWith("-")) {
|
|
1013
|
+
i++;
|
|
1014
|
+
const agentName = args[i];
|
|
1015
|
+
if (agentName && isValidAgentType(agentName)) {
|
|
1016
|
+
options.agents.push(agentName);
|
|
1017
|
+
} else if (agentName) {
|
|
1018
|
+
warn(`Unknown agent: ${agentName}`);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
} else if (arg === "--global" || arg === "-g") {
|
|
1022
|
+
options.global = true;
|
|
1023
|
+
} else if (arg === "--workspace" || arg === "-w") {
|
|
1024
|
+
options.workspace = true;
|
|
1025
|
+
} else if (arg === "--replace" || arg === "-r") {
|
|
1026
|
+
options.replace = true;
|
|
1027
|
+
} else if (arg === "--file" || arg === "-f") {
|
|
1028
|
+
options.file = args[++i];
|
|
1029
|
+
} else if (arg === "--verbose" || arg === "-v") {
|
|
1030
|
+
options.verbose = true;
|
|
1031
|
+
} else if (!arg?.startsWith("-") && !input) {
|
|
1032
|
+
input = arg ?? null;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
return { input, options };
|
|
1036
|
+
}
|
|
1037
|
+
async function runUpdate(input, options) {
|
|
1038
|
+
verbose("Running update command", options.verbose ?? false);
|
|
1039
|
+
let mcpConfig = null;
|
|
1040
|
+
if (options.file) {
|
|
1041
|
+
try {
|
|
1042
|
+
const content = readFileSync4(options.file, "utf-8");
|
|
1043
|
+
mcpConfig = parseMcpConfigString(content);
|
|
1044
|
+
if (!mcpConfig) {
|
|
1045
|
+
error(`Invalid MCP config in file: ${options.file}`);
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
verbose(`Read config from file: ${options.file}`, options.verbose ?? false);
|
|
1049
|
+
} catch (err) {
|
|
1050
|
+
error(`Failed to read file: ${options.file}`);
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
} else if (input) {
|
|
1054
|
+
mcpConfig = parseMcpConfigString(input);
|
|
1055
|
+
if (!mcpConfig) {
|
|
1056
|
+
error("Invalid MCP config JSON");
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
} else {
|
|
1060
|
+
error("No MCP config provided. Use JSON string or --file option.");
|
|
1061
|
+
console.log(`
|
|
1062
|
+
${DIM}Example:${RESET}`);
|
|
1063
|
+
console.log(
|
|
1064
|
+
` mcpcm update '{"mcpServers":{"my-server":{"command":"node","args":["/path/to/server.js"]}}}' --agent cursor`
|
|
1065
|
+
);
|
|
1066
|
+
console.log(` mcpcm update --file mcp.json --global`);
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
const serverNames = Object.keys(mcpConfig.mcpServers);
|
|
1070
|
+
info(`Updating ${serverNames.length} MCP server(s): ${serverNames.join(", ")}`);
|
|
1071
|
+
let targetAgents = [];
|
|
1072
|
+
if (options.agents && options.agents.length > 0) {
|
|
1073
|
+
targetAgents = options.agents;
|
|
1074
|
+
} else if (options.global) {
|
|
1075
|
+
targetAgents = await detectInstalledAgents();
|
|
1076
|
+
if (targetAgents.length === 0) {
|
|
1077
|
+
warn("No installed agents detected. Specify agents with --agent.");
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
} else if (options.workspace) {
|
|
1081
|
+
targetAgents = getAllAgentTypes().filter((t) => agents[t].projectConfigPath !== null);
|
|
1082
|
+
} else {
|
|
1083
|
+
error("Specify target with --agent, --global, or --workspace");
|
|
1084
|
+
return;
|
|
1085
|
+
}
|
|
1086
|
+
verbose(`Target agents: ${targetAgents.join(", ")}`, options.verbose ?? false);
|
|
1087
|
+
let successCount = 0;
|
|
1088
|
+
let failCount = 0;
|
|
1089
|
+
const allSkippedServers = /* @__PURE__ */ new Map();
|
|
1090
|
+
for (const agentType of targetAgents) {
|
|
1091
|
+
const agent = agents[agentType];
|
|
1092
|
+
if (options.workspace) {
|
|
1093
|
+
if (!agent.projectConfigPath) {
|
|
1094
|
+
verbose(`${agent.displayName} doesn't support project config`, options.verbose ?? false);
|
|
1095
|
+
continue;
|
|
1096
|
+
}
|
|
1097
|
+
const existing = readProjectConfig(agentType, process.cwd(), options.verbose);
|
|
1098
|
+
const existingServers = Object.keys(existing.config?.mcpServers || {});
|
|
1099
|
+
const incomingServers = Object.keys(mcpConfig.mcpServers);
|
|
1100
|
+
const toUpdate = incomingServers.filter((s) => existingServers.includes(s));
|
|
1101
|
+
const notFound = incomingServers.filter((s) => !existingServers.includes(s));
|
|
1102
|
+
for (const nf of notFound) {
|
|
1103
|
+
const agents2 = allSkippedServers.get(nf) || [];
|
|
1104
|
+
agents2.push(agent.displayName);
|
|
1105
|
+
allSkippedServers.set(nf, agents2);
|
|
1106
|
+
}
|
|
1107
|
+
if (toUpdate.length === 0) {
|
|
1108
|
+
warn(`${agent.displayName}: no matching servers found (skipped)`);
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
const filteredConfig = {
|
|
1112
|
+
mcpServers: Object.fromEntries(
|
|
1113
|
+
Object.entries(mcpConfig.mcpServers).filter(([name]) => toUpdate.includes(name))
|
|
1114
|
+
)
|
|
1115
|
+
};
|
|
1116
|
+
const merged = mergeMcpConfigs(
|
|
1117
|
+
existing.config || { mcpServers: {} },
|
|
1118
|
+
filteredConfig,
|
|
1119
|
+
true
|
|
1120
|
+
// Always replace for update
|
|
1121
|
+
);
|
|
1122
|
+
const result = writeProjectConfig(
|
|
1123
|
+
agentType,
|
|
1124
|
+
merged,
|
|
1125
|
+
existing.rawContent,
|
|
1126
|
+
process.cwd(),
|
|
1127
|
+
options.verbose
|
|
1128
|
+
);
|
|
1129
|
+
if (result.success) {
|
|
1130
|
+
if (notFound.length > 0) {
|
|
1131
|
+
success(
|
|
1132
|
+
`${agent.displayName}: updated ${toUpdate.join(", ")} (skipped: ${notFound.join(", ")})`
|
|
1133
|
+
);
|
|
1134
|
+
} else {
|
|
1135
|
+
success(`${agent.displayName}: updated ${toUpdate.join(", ")}`);
|
|
1136
|
+
}
|
|
1137
|
+
successCount++;
|
|
1138
|
+
} else {
|
|
1139
|
+
error(`${agent.displayName}: ${result.error}`);
|
|
1140
|
+
failCount++;
|
|
1141
|
+
}
|
|
1142
|
+
} else {
|
|
1143
|
+
if (!agent.globalConfigPath) {
|
|
1144
|
+
verbose(`${agent.displayName} doesn't support global config`, options.verbose ?? false);
|
|
1145
|
+
continue;
|
|
1146
|
+
}
|
|
1147
|
+
const existing = readGlobalConfig(agentType, options.verbose);
|
|
1148
|
+
const existingServers = Object.keys(existing.config?.mcpServers || {});
|
|
1149
|
+
const incomingServers = Object.keys(mcpConfig.mcpServers);
|
|
1150
|
+
const toUpdate = incomingServers.filter((s) => existingServers.includes(s));
|
|
1151
|
+
const notFound = incomingServers.filter((s) => !existingServers.includes(s));
|
|
1152
|
+
for (const nf of notFound) {
|
|
1153
|
+
const agents2 = allSkippedServers.get(nf) || [];
|
|
1154
|
+
agents2.push(agent.displayName);
|
|
1155
|
+
allSkippedServers.set(nf, agents2);
|
|
1156
|
+
}
|
|
1157
|
+
if (toUpdate.length === 0) {
|
|
1158
|
+
warn(`${agent.displayName}: no matching servers found (skipped)`);
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
const filteredConfig = {
|
|
1162
|
+
mcpServers: Object.fromEntries(
|
|
1163
|
+
Object.entries(mcpConfig.mcpServers).filter(([name]) => toUpdate.includes(name))
|
|
1164
|
+
)
|
|
1165
|
+
};
|
|
1166
|
+
const merged = mergeMcpConfigs(
|
|
1167
|
+
existing.config || { mcpServers: {} },
|
|
1168
|
+
filteredConfig,
|
|
1169
|
+
true
|
|
1170
|
+
// Always replace for update
|
|
1171
|
+
);
|
|
1172
|
+
const result = writeGlobalConfig(agentType, merged, existing.rawContent, options.verbose);
|
|
1173
|
+
if (result.success) {
|
|
1174
|
+
if (notFound.length > 0) {
|
|
1175
|
+
success(
|
|
1176
|
+
`${agent.displayName}: updated ${toUpdate.join(", ")} (skipped: ${notFound.join(", ")})`
|
|
1177
|
+
);
|
|
1178
|
+
} else {
|
|
1179
|
+
success(`${agent.displayName}: updated ${toUpdate.join(", ")}`);
|
|
1180
|
+
}
|
|
1181
|
+
successCount++;
|
|
1182
|
+
} else {
|
|
1183
|
+
error(`${agent.displayName}: ${result.error}`);
|
|
1184
|
+
failCount++;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
console.log();
|
|
1189
|
+
if (successCount > 0) {
|
|
1190
|
+
success(`Updated ${successCount} agent(s)`);
|
|
1191
|
+
}
|
|
1192
|
+
if (failCount > 0) {
|
|
1193
|
+
error(`Failed for ${failCount} agent(s)`);
|
|
1194
|
+
}
|
|
1195
|
+
if (allSkippedServers.size > 0) {
|
|
1196
|
+
console.log();
|
|
1197
|
+
warn(`Skipped ${allSkippedServers.size} server(s) not found:`);
|
|
1198
|
+
for (const [serverName, agentNames] of allSkippedServers) {
|
|
1199
|
+
console.log(` ${DIM}${serverName}${RESET} \u2192 ${agentNames.join(", ")}`);
|
|
1200
|
+
}
|
|
1201
|
+
console.log(`
|
|
1202
|
+
${DIM}Use 'mcpcm add' to add new servers.${RESET}`);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
// src/cli.ts
|
|
1207
|
+
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
1208
|
+
function getVersion() {
|
|
1209
|
+
try {
|
|
1210
|
+
const pkgPath = join5(__dirname, "..", "package.json");
|
|
1211
|
+
const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
1212
|
+
return pkg.version;
|
|
1213
|
+
} catch {
|
|
1214
|
+
return "1.0.0";
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
var VERSION = getVersion();
|
|
1218
|
+
var LOGO_LINES = [
|
|
1219
|
+
"\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557",
|
|
1220
|
+
"\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551",
|
|
1221
|
+
"\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551",
|
|
1222
|
+
"\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551",
|
|
1223
|
+
"\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551",
|
|
1224
|
+
"\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D"
|
|
1225
|
+
];
|
|
1226
|
+
var GRAYS = [
|
|
1227
|
+
"\x1B[38;5;250m",
|
|
1228
|
+
"\x1B[38;5;248m",
|
|
1229
|
+
"\x1B[38;5;245m",
|
|
1230
|
+
"\x1B[38;5;243m",
|
|
1231
|
+
"\x1B[38;5;240m",
|
|
1232
|
+
"\x1B[38;5;238m"
|
|
1233
|
+
];
|
|
1234
|
+
function showLogo() {
|
|
1235
|
+
console.log();
|
|
1236
|
+
LOGO_LINES.forEach((line, i) => {
|
|
1237
|
+
console.log(`${GRAYS[i]}${line}${RESET}`);
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
function showBanner() {
|
|
1241
|
+
showLogo();
|
|
1242
|
+
console.log();
|
|
1243
|
+
console.log(
|
|
1244
|
+
`${DIM}MCP Configuration Manager - Manage MCP server configuration across AI Agents${RESET}`
|
|
1245
|
+
);
|
|
1246
|
+
console.log();
|
|
1247
|
+
console.log(` ${DIM}$${RESET} ${TEXT}mcpcm add${RESET} ${DIM}Add MCP server config${RESET}`);
|
|
1248
|
+
console.log(
|
|
1249
|
+
` ${DIM}$${RESET} ${TEXT}mcpcm del${RESET} ${DIM}Delete MCP server config${RESET}`
|
|
1250
|
+
);
|
|
1251
|
+
console.log(` ${DIM}$${RESET} ${TEXT}mcpcm list${RESET} ${DIM}List all MCP configs${RESET}`);
|
|
1252
|
+
console.log(` ${DIM}$${RESET} ${TEXT}mcpcm find${RESET} ${DIM}Find MCP server${RESET}`);
|
|
1253
|
+
console.log(
|
|
1254
|
+
` ${DIM}$${RESET} ${TEXT}mcpcm sync${RESET} ${DIM}Sync configs between agents${RESET}`
|
|
1255
|
+
);
|
|
1256
|
+
console.log();
|
|
1257
|
+
console.log(`${DIM}Run${RESET} mcpcm --help ${DIM}for more info${RESET}`);
|
|
1258
|
+
console.log();
|
|
1259
|
+
}
|
|
1260
|
+
function showHelp() {
|
|
1261
|
+
const agentList = getAllAgentTypes().join(", ");
|
|
1262
|
+
console.log(`
|
|
1263
|
+
${BOLD}Usage:${RESET} mcpcm <command> [options]
|
|
1264
|
+
|
|
1265
|
+
${BOLD}Commands:${RESET}
|
|
1266
|
+
add <json> Add MCP server config from JSON string
|
|
1267
|
+
add --file <path> Add MCP server config from file
|
|
1268
|
+
update Update MCP server config (same as add)
|
|
1269
|
+
del <name> Delete MCP server by name
|
|
1270
|
+
list List all MCP configurations
|
|
1271
|
+
find <name> Find where an MCP server is configured
|
|
1272
|
+
sync Sync configs from one agent to others
|
|
1273
|
+
|
|
1274
|
+
${BOLD}Target Options:${RESET}
|
|
1275
|
+
-a, --agent <name> Target specific agent(s)
|
|
1276
|
+
-g, --global Target all installed agents (global configs)
|
|
1277
|
+
-w, --workspace Target project-level configs
|
|
1278
|
+
|
|
1279
|
+
${BOLD}Add/Update Options:${RESET}
|
|
1280
|
+
-f, --file <path> Read config from JSON/TOML file
|
|
1281
|
+
-r, --replace Replace configs instead of merging
|
|
1282
|
+
|
|
1283
|
+
${BOLD}Sync Options:${RESET}
|
|
1284
|
+
--from <agent> Source agent to sync from
|
|
1285
|
+
--to <agent> Target agent(s) to sync to
|
|
1286
|
+
--to-all Sync to all installed agents
|
|
1287
|
+
|
|
1288
|
+
${BOLD}Other Options:${RESET}
|
|
1289
|
+
-v, --verbose Show detailed output
|
|
1290
|
+
-h, --help Show this help message
|
|
1291
|
+
--version Show version number
|
|
1292
|
+
|
|
1293
|
+
${BOLD}Supported Agents:${RESET}
|
|
1294
|
+
${DIM}${agentList}${RESET}
|
|
1295
|
+
|
|
1296
|
+
${BOLD}Examples:${RESET}
|
|
1297
|
+
${DIM}$${RESET} mcpcm add '{"mcpServers":{"my-server":{"command":"node","args":["/path/to/server"]}}}' --agent cursor
|
|
1298
|
+
${DIM}$${RESET} mcpcm add --file mcp.json --global
|
|
1299
|
+
${DIM}$${RESET} mcpcm del my-server --agent cursor
|
|
1300
|
+
${DIM}$${RESET} mcpcm list --global
|
|
1301
|
+
${DIM}$${RESET} mcpcm find my-server
|
|
1302
|
+
${DIM}$${RESET} mcpcm sync --from cursor --to antigravity claude-code
|
|
1303
|
+
${DIM}$${RESET} mcpcm sync --from cursor --to-all
|
|
1304
|
+
`);
|
|
1305
|
+
}
|
|
1306
|
+
async function main() {
|
|
1307
|
+
const args = process.argv.slice(2);
|
|
1308
|
+
if (args.length === 0) {
|
|
1309
|
+
showBanner();
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
const command = args[0];
|
|
1313
|
+
const restArgs = args.slice(1);
|
|
1314
|
+
switch (command) {
|
|
1315
|
+
case "add":
|
|
1316
|
+
case "a": {
|
|
1317
|
+
showLogo();
|
|
1318
|
+
console.log();
|
|
1319
|
+
const { input, options } = parseAddOptions(restArgs);
|
|
1320
|
+
await runAdd(input, options);
|
|
1321
|
+
break;
|
|
1322
|
+
}
|
|
1323
|
+
case "update":
|
|
1324
|
+
case "u": {
|
|
1325
|
+
showLogo();
|
|
1326
|
+
console.log();
|
|
1327
|
+
const { input, options } = parseUpdateOptions(restArgs);
|
|
1328
|
+
await runUpdate(input, options);
|
|
1329
|
+
break;
|
|
1330
|
+
}
|
|
1331
|
+
case "del":
|
|
1332
|
+
case "delete":
|
|
1333
|
+
case "d": {
|
|
1334
|
+
showLogo();
|
|
1335
|
+
console.log();
|
|
1336
|
+
const { serverNames, options } = parseDelOptions(restArgs);
|
|
1337
|
+
await runDel(serverNames, options);
|
|
1338
|
+
break;
|
|
1339
|
+
}
|
|
1340
|
+
case "list":
|
|
1341
|
+
case "ls":
|
|
1342
|
+
case "l": {
|
|
1343
|
+
showLogo();
|
|
1344
|
+
console.log();
|
|
1345
|
+
const options = parseListOptions(restArgs);
|
|
1346
|
+
await runList(options);
|
|
1347
|
+
break;
|
|
1348
|
+
}
|
|
1349
|
+
case "find":
|
|
1350
|
+
case "search":
|
|
1351
|
+
case "f": {
|
|
1352
|
+
showLogo();
|
|
1353
|
+
await runFind(restArgs);
|
|
1354
|
+
break;
|
|
1355
|
+
}
|
|
1356
|
+
case "sync":
|
|
1357
|
+
case "s": {
|
|
1358
|
+
showLogo();
|
|
1359
|
+
console.log();
|
|
1360
|
+
const options = parseSyncOptions(restArgs);
|
|
1361
|
+
if (options) {
|
|
1362
|
+
await runSync(options);
|
|
1363
|
+
}
|
|
1364
|
+
break;
|
|
1365
|
+
}
|
|
1366
|
+
case "--help":
|
|
1367
|
+
case "-h":
|
|
1368
|
+
case "help":
|
|
1369
|
+
showHelp();
|
|
1370
|
+
break;
|
|
1371
|
+
case "--version":
|
|
1372
|
+
case "-v":
|
|
1373
|
+
console.log(VERSION);
|
|
1374
|
+
break;
|
|
1375
|
+
default:
|
|
1376
|
+
console.log(`Unknown command: ${command}`);
|
|
1377
|
+
console.log(`Run ${BOLD}mcpcm --help${RESET} for usage.`);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
main().catch((err) => {
|
|
1381
|
+
console.error("Error:", err.message);
|
|
1382
|
+
process.exit(1);
|
|
1383
|
+
});
|