@mcp-abap-adt/configurator 0.0.1 → 0.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/bin/mcp-conf.js +630 -129
- package/docs/CLIENT_INSTALLERS.md +59 -28
- package/package.json +10 -2
package/bin/mcp-conf.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
const os = require("os");
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
const os = require("node:os");
|
|
6
6
|
|
|
7
7
|
let yaml;
|
|
8
8
|
try {
|
|
@@ -22,6 +22,10 @@ try {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
const args = process.argv.slice(2);
|
|
25
|
+
const action = args[0] && !args[0].startsWith("-") ? args[0] : null;
|
|
26
|
+
if (action && ["add", "rm", "ls", "enable", "disable", "where", "help"].includes(action)) {
|
|
27
|
+
args.shift();
|
|
28
|
+
}
|
|
25
29
|
const options = {
|
|
26
30
|
clients: [],
|
|
27
31
|
envPath: null,
|
|
@@ -29,18 +33,29 @@ const options = {
|
|
|
29
33
|
name: null,
|
|
30
34
|
transport: "stdio",
|
|
31
35
|
command: "mcp-abap-adt",
|
|
36
|
+
scope: null,
|
|
32
37
|
dryRun: false,
|
|
33
38
|
force: false,
|
|
34
39
|
disabled: false,
|
|
35
40
|
toggle: false,
|
|
36
41
|
remove: false,
|
|
42
|
+
list: false,
|
|
43
|
+
allProjects: false,
|
|
44
|
+
projectPath: null,
|
|
45
|
+
where: false,
|
|
37
46
|
url: null,
|
|
38
47
|
headers: {},
|
|
39
48
|
timeout: 60,
|
|
40
49
|
};
|
|
41
50
|
|
|
42
|
-
if (args.includes("--help") || args.includes("-h")) {
|
|
43
|
-
|
|
51
|
+
if (args.includes("--help") || args.includes("-h") || action === "help") {
|
|
52
|
+
const helpAction =
|
|
53
|
+
action === "help"
|
|
54
|
+
? args[0]
|
|
55
|
+
: action && ["add", "rm", "ls", "enable", "disable", "where"].includes(action)
|
|
56
|
+
? action
|
|
57
|
+
: null;
|
|
58
|
+
printHelp(helpAction);
|
|
44
59
|
process.exit(0);
|
|
45
60
|
}
|
|
46
61
|
|
|
@@ -64,6 +79,21 @@ for (let i = 0; i < args.length; i += 1) {
|
|
|
64
79
|
} else if (arg === "--command") {
|
|
65
80
|
options.command = args[i + 1];
|
|
66
81
|
i += 1;
|
|
82
|
+
} else if (arg === "--global") {
|
|
83
|
+
if (options.scope && options.scope !== "global") {
|
|
84
|
+
fail("Choose either --global or --local (not both).");
|
|
85
|
+
}
|
|
86
|
+
options.scope = "global";
|
|
87
|
+
} else if (arg === "--local") {
|
|
88
|
+
if (options.scope && options.scope !== "local") {
|
|
89
|
+
fail("Choose either --global or --local (not both).");
|
|
90
|
+
}
|
|
91
|
+
options.scope = "local";
|
|
92
|
+
} else if (arg === "--all-projects") {
|
|
93
|
+
options.allProjects = true;
|
|
94
|
+
} else if (arg === "--project") {
|
|
95
|
+
options.projectPath = args[i + 1];
|
|
96
|
+
i += 1;
|
|
67
97
|
} else if (arg === "--url") {
|
|
68
98
|
options.url = args[i + 1];
|
|
69
99
|
i += 1;
|
|
@@ -92,19 +122,62 @@ for (let i = 0; i < args.length; i += 1) {
|
|
|
92
122
|
}
|
|
93
123
|
}
|
|
94
124
|
|
|
125
|
+
if (!action || !["add", "rm", "ls", "enable", "disable", "where"].includes(action)) {
|
|
126
|
+
fail("Provide a command: add | rm | ls | enable | disable | where.");
|
|
127
|
+
}
|
|
128
|
+
|
|
95
129
|
if (options.clients.length === 0) {
|
|
96
130
|
fail("Provide at least one --client.");
|
|
97
131
|
}
|
|
98
132
|
|
|
99
|
-
if (!options.name) {
|
|
133
|
+
if (!["ls"].includes(action) && !options.name) {
|
|
100
134
|
fail("Provide --name <serverName> (required).");
|
|
101
135
|
}
|
|
102
136
|
|
|
103
|
-
const transportNormalized =
|
|
104
|
-
options.transport === "http" ? "streamableHttp" : options.transport;
|
|
137
|
+
const transportNormalized = options.transport === "http" ? "streamableHttp" : options.transport;
|
|
105
138
|
options.transport = transportNormalized;
|
|
106
139
|
|
|
107
|
-
|
|
140
|
+
if (action === "rm") {
|
|
141
|
+
options.remove = true;
|
|
142
|
+
}
|
|
143
|
+
if (action === "ls") {
|
|
144
|
+
options.list = true;
|
|
145
|
+
}
|
|
146
|
+
if (action === "enable") {
|
|
147
|
+
options.toggle = true;
|
|
148
|
+
options.disabled = false;
|
|
149
|
+
}
|
|
150
|
+
if (action === "disable") {
|
|
151
|
+
options.toggle = true;
|
|
152
|
+
options.disabled = true;
|
|
153
|
+
}
|
|
154
|
+
if (action === "where") {
|
|
155
|
+
options.where = true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (options.remove && action !== "rm") {
|
|
159
|
+
fail("Use the rm command instead of --remove.");
|
|
160
|
+
}
|
|
161
|
+
if (options.list && options.toggle) {
|
|
162
|
+
fail("The ls command does not support --enable/--disable.");
|
|
163
|
+
}
|
|
164
|
+
if (options.remove && options.toggle) {
|
|
165
|
+
fail("The rm command does not support --enable/--disable.");
|
|
166
|
+
}
|
|
167
|
+
if (options.allProjects && !options.list && !options.toggle && !options.remove && !options.where) {
|
|
168
|
+
fail("--all-projects is only supported for rm/enable/disable/ls/where.");
|
|
169
|
+
}
|
|
170
|
+
if (options.projectPath && options.allProjects) {
|
|
171
|
+
fail("Use either --project or --all-projects (not both).");
|
|
172
|
+
}
|
|
173
|
+
if (options.where && (options.list || options.remove || options.toggle)) {
|
|
174
|
+
fail("The where command does not support ls/rm/enable/disable flags.");
|
|
175
|
+
}
|
|
176
|
+
if (options.projectPath && action === "add" && options.scope !== "global") {
|
|
177
|
+
fail("--project is only supported for Claude global config.");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const requiresConnectionParams = !options.remove && !options.toggle && !options.list && !options.where;
|
|
108
181
|
|
|
109
182
|
if (requiresConnectionParams && options.transport === "stdio") {
|
|
110
183
|
if (!options.envPath && !options.mcpDestination) {
|
|
@@ -136,57 +209,135 @@ const serverArgsRaw = [
|
|
|
136
209
|
];
|
|
137
210
|
const serverArgs = serverArgsRaw.filter(Boolean);
|
|
138
211
|
|
|
139
|
-
for (const client of options.clients) {
|
|
140
|
-
|
|
212
|
+
for (const client of options.clients) {
|
|
213
|
+
const scope = options.scope || getDefaultScope(client);
|
|
214
|
+
if (options.projectPath && client !== "claude") {
|
|
215
|
+
fail("--project is only supported for Claude.");
|
|
216
|
+
}
|
|
217
|
+
switch (client) {
|
|
141
218
|
case "cline":
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
"cline"
|
|
147
|
-
|
|
219
|
+
requireScope("Cline", ["global"], scope);
|
|
220
|
+
if (options.list) {
|
|
221
|
+
listJsonConfig(getClinePath(platform, home, appData), "cline");
|
|
222
|
+
} else if (options.where) {
|
|
223
|
+
whereJsonConfig(getClinePath(platform, home, appData), "cline", options.name);
|
|
224
|
+
} else {
|
|
225
|
+
writeJsonConfig(getClinePath(platform, home, appData), options.name, serverArgs, "cline");
|
|
226
|
+
}
|
|
148
227
|
break;
|
|
149
228
|
case "codex":
|
|
150
229
|
if (options.transport === "sse") {
|
|
151
230
|
fail("Codex does not support SSE transport.");
|
|
152
231
|
}
|
|
153
|
-
|
|
232
|
+
requireScope("Codex", ["global"], scope);
|
|
233
|
+
if (options.list) {
|
|
234
|
+
listCodexConfig(getCodexPath(platform, home, userProfile));
|
|
235
|
+
} else if (options.where) {
|
|
236
|
+
whereCodexConfig(getCodexPath(platform, home, userProfile), options.name);
|
|
237
|
+
} else {
|
|
238
|
+
writeCodexConfig(getCodexPath(platform, home, userProfile), options.name, serverArgs);
|
|
239
|
+
}
|
|
154
240
|
break;
|
|
155
241
|
case "claude":
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
242
|
+
requireScope("Claude", ["global", "local"], scope);
|
|
243
|
+
const claudeToggleScope = options.toggle ? "global" : scope;
|
|
244
|
+
if (options.allProjects && claudeToggleScope !== "global") {
|
|
245
|
+
fail("--all-projects is only supported for Claude global config.");
|
|
246
|
+
}
|
|
247
|
+
if (options.projectPath && claudeToggleScope !== "global") {
|
|
248
|
+
fail("--project is only supported for Claude global config.");
|
|
249
|
+
}
|
|
250
|
+
if (options.toggle && scope === "local") {
|
|
251
|
+
const localPath = getClaudePath(platform, home, appData, "local");
|
|
252
|
+
if (!claudeLocalHasServer(localPath, options.name)) {
|
|
253
|
+
fail(`Server "${options.name}" not found in ${localPath}.`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (options.list) {
|
|
257
|
+
listClaudeConfig(
|
|
258
|
+
getClaudePath(platform, home, appData, claudeToggleScope),
|
|
259
|
+
options.allProjects,
|
|
260
|
+
options.projectPath,
|
|
261
|
+
);
|
|
262
|
+
} else if (options.where) {
|
|
263
|
+
whereClaudeConfig(
|
|
264
|
+
getClaudePath(platform, home, appData, claudeToggleScope),
|
|
265
|
+
options.name,
|
|
266
|
+
options.allProjects,
|
|
267
|
+
options.projectPath,
|
|
268
|
+
);
|
|
269
|
+
} else {
|
|
270
|
+
writeClaudeConfig(
|
|
271
|
+
getClaudePath(platform, home, appData, claudeToggleScope),
|
|
272
|
+
options.name,
|
|
273
|
+
serverArgs,
|
|
274
|
+
);
|
|
275
|
+
}
|
|
161
276
|
break;
|
|
162
277
|
case "goose":
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
278
|
+
requireScope("Goose", ["global"], scope);
|
|
279
|
+
if (options.list) {
|
|
280
|
+
listGooseConfig(getGoosePath(platform, home, appData));
|
|
281
|
+
} else if (options.where) {
|
|
282
|
+
whereGooseConfig(getGoosePath(platform, home, appData), options.name);
|
|
283
|
+
} else {
|
|
284
|
+
writeGooseConfig(getGoosePath(platform, home, appData), options.name, serverArgs);
|
|
285
|
+
}
|
|
168
286
|
break;
|
|
169
287
|
case "opencode":
|
|
170
|
-
|
|
288
|
+
requireScope("OpenCode", ["global", "local"], scope);
|
|
289
|
+
if (options.list) {
|
|
290
|
+
listJsonConfig(getOpenCodePath(platform, home, appData, scope), "opencode");
|
|
291
|
+
} else if (options.where) {
|
|
292
|
+
whereJsonConfig(getOpenCodePath(platform, home, appData, scope), "opencode", options.name);
|
|
293
|
+
} else {
|
|
294
|
+
writeJsonConfig(
|
|
295
|
+
getOpenCodePath(platform, home, appData, scope),
|
|
296
|
+
options.name,
|
|
297
|
+
serverArgs,
|
|
298
|
+
"opencode",
|
|
299
|
+
);
|
|
300
|
+
}
|
|
171
301
|
break;
|
|
172
302
|
case "copilot":
|
|
173
|
-
|
|
303
|
+
requireScope("GitHub Copilot", ["local"], scope);
|
|
304
|
+
if (options.list) {
|
|
305
|
+
listJsonConfig(getCopilotPath(), "copilot");
|
|
306
|
+
} else if (options.where) {
|
|
307
|
+
whereJsonConfig(getCopilotPath(), "copilot", options.name);
|
|
308
|
+
} else {
|
|
309
|
+
writeJsonConfig(getCopilotPath(), options.name, serverArgs, "copilot");
|
|
310
|
+
}
|
|
174
311
|
break;
|
|
175
312
|
case "cursor":
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
"cursor"
|
|
181
|
-
|
|
313
|
+
requireScope("Cursor", ["global", "local"], scope);
|
|
314
|
+
if (options.list) {
|
|
315
|
+
listJsonConfig(getCursorPath(platform, home, userProfile, scope), "cursor");
|
|
316
|
+
} else if (options.where) {
|
|
317
|
+
whereJsonConfig(getCursorPath(platform, home, userProfile, scope), "cursor", options.name);
|
|
318
|
+
} else {
|
|
319
|
+
writeJsonConfig(
|
|
320
|
+
getCursorPath(platform, home, userProfile, scope),
|
|
321
|
+
options.name,
|
|
322
|
+
serverArgs,
|
|
323
|
+
"cursor",
|
|
324
|
+
);
|
|
325
|
+
}
|
|
182
326
|
break;
|
|
183
327
|
case "windsurf":
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
"windsurf"
|
|
189
|
-
|
|
328
|
+
requireScope("Windsurf", ["global"], scope);
|
|
329
|
+
if (options.list) {
|
|
330
|
+
listJsonConfig(getWindsurfPath(platform, home, userProfile), "windsurf");
|
|
331
|
+
} else if (options.where) {
|
|
332
|
+
whereJsonConfig(getWindsurfPath(platform, home, userProfile), "windsurf", options.name);
|
|
333
|
+
} else {
|
|
334
|
+
writeJsonConfig(
|
|
335
|
+
getWindsurfPath(platform, home, userProfile),
|
|
336
|
+
options.name,
|
|
337
|
+
serverArgs,
|
|
338
|
+
"windsurf",
|
|
339
|
+
);
|
|
340
|
+
}
|
|
190
341
|
break;
|
|
191
342
|
default:
|
|
192
343
|
fail(`Unknown client: ${client}`);
|
|
@@ -202,7 +353,7 @@ function getClinePath(platformValue, homeDir, appDataDir) {
|
|
|
202
353
|
"globalStorage",
|
|
203
354
|
"saoudrizwan.claude-dev",
|
|
204
355
|
"settings",
|
|
205
|
-
"cline_mcp_settings.json"
|
|
356
|
+
"cline_mcp_settings.json",
|
|
206
357
|
);
|
|
207
358
|
}
|
|
208
359
|
return path.join(
|
|
@@ -213,7 +364,7 @@ function getClinePath(platformValue, homeDir, appDataDir) {
|
|
|
213
364
|
"globalStorage",
|
|
214
365
|
"saoudrizwan.claude-dev",
|
|
215
366
|
"settings",
|
|
216
|
-
"cline_mcp_settings.json"
|
|
367
|
+
"cline_mcp_settings.json",
|
|
217
368
|
);
|
|
218
369
|
}
|
|
219
370
|
|
|
@@ -224,14 +375,17 @@ function getCodexPath(platformValue, homeDir, userProfileDir) {
|
|
|
224
375
|
return path.join(homeDir, ".codex", "config.toml");
|
|
225
376
|
}
|
|
226
377
|
|
|
227
|
-
function getClaudePath(platformValue, homeDir, appDataDir) {
|
|
378
|
+
function getClaudePath(platformValue, homeDir, appDataDir, scopeValue) {
|
|
379
|
+
if (scopeValue === "local") {
|
|
380
|
+
return path.join(process.cwd(), ".mcp.json");
|
|
381
|
+
}
|
|
228
382
|
if (platformValue === "darwin") {
|
|
229
383
|
return path.join(
|
|
230
384
|
homeDir,
|
|
231
385
|
"Library",
|
|
232
386
|
"Application Support",
|
|
233
387
|
"Claude",
|
|
234
|
-
"claude_desktop_config.json"
|
|
388
|
+
"claude_desktop_config.json",
|
|
235
389
|
);
|
|
236
390
|
}
|
|
237
391
|
if (platformValue === "win32") {
|
|
@@ -247,7 +401,10 @@ function getGoosePath(platformValue, homeDir, appDataDir) {
|
|
|
247
401
|
return path.join(homeDir, ".config", "goose", "config.yaml");
|
|
248
402
|
}
|
|
249
403
|
|
|
250
|
-
function getCursorPath(platformValue, homeDir, userProfileDir) {
|
|
404
|
+
function getCursorPath(platformValue, homeDir, userProfileDir, scopeValue) {
|
|
405
|
+
if (scopeValue === "local") {
|
|
406
|
+
return path.join(process.cwd(), ".cursor", "mcp.json");
|
|
407
|
+
}
|
|
251
408
|
const base = platformValue === "win32" ? userProfileDir : homeDir;
|
|
252
409
|
return path.join(base, ".cursor", "mcp.json");
|
|
253
410
|
}
|
|
@@ -256,8 +413,14 @@ function getCopilotPath() {
|
|
|
256
413
|
return path.join(process.cwd(), ".vscode", "mcp.json");
|
|
257
414
|
}
|
|
258
415
|
|
|
259
|
-
function getOpenCodePath() {
|
|
260
|
-
|
|
416
|
+
function getOpenCodePath(platformValue, homeDir, appDataDir, scopeValue) {
|
|
417
|
+
if (scopeValue === "local") {
|
|
418
|
+
return path.join(process.cwd(), "opencode.json");
|
|
419
|
+
}
|
|
420
|
+
if (platformValue === "win32") {
|
|
421
|
+
return path.join(appDataDir, "opencode", "opencode.json");
|
|
422
|
+
}
|
|
423
|
+
return path.join(homeDir, ".config", "opencode", "opencode.json");
|
|
261
424
|
}
|
|
262
425
|
|
|
263
426
|
function getWindsurfPath(platformValue, homeDir, userProfileDir) {
|
|
@@ -267,6 +430,50 @@ function getWindsurfPath(platformValue, homeDir, userProfileDir) {
|
|
|
267
430
|
return path.join(homeDir, ".codeium", "windsurf", "mcp_config.json");
|
|
268
431
|
}
|
|
269
432
|
|
|
433
|
+
function requireScope(clientLabel, allowedScopes, requestedScope) {
|
|
434
|
+
if (!allowedScopes.includes(requestedScope)) {
|
|
435
|
+
fail(
|
|
436
|
+
`${clientLabel} supports ${allowedScopes.join("/")} configuration only. ` +
|
|
437
|
+
`Use --${allowedScopes[0]}.`,
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function getDefaultScope(clientType) {
|
|
443
|
+
if (clientType === "copilot") {
|
|
444
|
+
return "local";
|
|
445
|
+
}
|
|
446
|
+
return "global";
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function resolveProjectSelector(data, projectPath) {
|
|
450
|
+
if (!projectPath) {
|
|
451
|
+
return resolveClaudeProjectKey(data);
|
|
452
|
+
}
|
|
453
|
+
if (!data.projects) {
|
|
454
|
+
return projectPath;
|
|
455
|
+
}
|
|
456
|
+
if (data.projects[projectPath]) {
|
|
457
|
+
return projectPath;
|
|
458
|
+
}
|
|
459
|
+
let desiredReal;
|
|
460
|
+
try {
|
|
461
|
+
desiredReal = fs.realpathSync(projectPath);
|
|
462
|
+
} catch {
|
|
463
|
+
return projectPath;
|
|
464
|
+
}
|
|
465
|
+
for (const key of Object.keys(data.projects)) {
|
|
466
|
+
try {
|
|
467
|
+
if (fs.realpathSync(key) === desiredReal) {
|
|
468
|
+
return key;
|
|
469
|
+
}
|
|
470
|
+
} catch {
|
|
471
|
+
// ignore invalid paths
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return projectPath;
|
|
475
|
+
}
|
|
476
|
+
|
|
270
477
|
function getDefaultDisabled(clientType) {
|
|
271
478
|
return ["cline", "codex", "windsurf", "goose", "claude", "opencode"].includes(clientType);
|
|
272
479
|
}
|
|
@@ -285,7 +492,7 @@ function writeJsonConfig(filePath, serverName, argsArray, clientType) {
|
|
|
285
492
|
if (options.toggle) {
|
|
286
493
|
if (clientType === "cursor" || clientType === "copilot") {
|
|
287
494
|
fail(
|
|
288
|
-
`${clientType === "cursor" ? "Cursor" : "GitHub Copilot"} enable/disable is not implemented yet
|
|
495
|
+
`${clientType === "cursor" ? "Cursor" : "GitHub Copilot"} enable/disable is not implemented yet.`,
|
|
289
496
|
);
|
|
290
497
|
}
|
|
291
498
|
const store =
|
|
@@ -301,9 +508,9 @@ function writeJsonConfig(filePath, serverName, argsArray, clientType) {
|
|
|
301
508
|
...store[serverName],
|
|
302
509
|
};
|
|
303
510
|
if (clientType === "opencode") {
|
|
304
|
-
store[serverName].enabled = options.disabled
|
|
511
|
+
store[serverName].enabled = !options.disabled;
|
|
305
512
|
} else {
|
|
306
|
-
store[serverName].disabled = options.disabled
|
|
513
|
+
store[serverName].disabled = !!options.disabled;
|
|
307
514
|
}
|
|
308
515
|
writeFile(filePath, JSON.stringify(data, null, 2));
|
|
309
516
|
return;
|
|
@@ -329,9 +536,7 @@ function writeJsonConfig(filePath, serverName, argsArray, clientType) {
|
|
|
329
536
|
? data.servers
|
|
330
537
|
: data.mcpServers;
|
|
331
538
|
if (store[serverName] && !options.force) {
|
|
332
|
-
fail(
|
|
333
|
-
`Server "${serverName}" already exists in ${filePath}. Use --force to overwrite.`
|
|
334
|
-
);
|
|
539
|
+
fail(`Server "${serverName}" already exists in ${filePath}. Use --force to overwrite.`);
|
|
335
540
|
}
|
|
336
541
|
if (clientType === "opencode") {
|
|
337
542
|
const enabled = options.disabled ? false : !getDefaultDisabled("opencode");
|
|
@@ -378,8 +583,7 @@ function writeJsonConfig(filePath, serverName, argsArray, clientType) {
|
|
|
378
583
|
args: argsArray,
|
|
379
584
|
timeout: options.timeout,
|
|
380
585
|
};
|
|
381
|
-
data.mcpServers[serverName].disabled =
|
|
382
|
-
options.disabled || getDefaultDisabled(clientType) ? true : false;
|
|
586
|
+
data.mcpServers[serverName].disabled = !!(options.disabled || getDefaultDisabled(clientType));
|
|
383
587
|
} else {
|
|
384
588
|
const entry = {
|
|
385
589
|
type: options.transport,
|
|
@@ -390,7 +594,7 @@ function writeJsonConfig(filePath, serverName, argsArray, clientType) {
|
|
|
390
594
|
entry.headers = options.headers;
|
|
391
595
|
}
|
|
392
596
|
const defaultDisabled = getDefaultDisabled(clientType);
|
|
393
|
-
entry.disabled = options.disabled || defaultDisabled
|
|
597
|
+
entry.disabled = !!(options.disabled || defaultDisabled);
|
|
394
598
|
data.mcpServers[serverName] = entry;
|
|
395
599
|
}
|
|
396
600
|
writeFile(filePath, JSON.stringify(data, null, 2));
|
|
@@ -400,32 +604,8 @@ function writeClaudeConfig(filePath, serverName, argsArray) {
|
|
|
400
604
|
ensureDir(filePath);
|
|
401
605
|
const data = readJson(filePath);
|
|
402
606
|
const isDesktopConfig =
|
|
403
|
-
filePath.endsWith(".claude.json") ||
|
|
404
|
-
|
|
405
|
-
const projectPath = process.cwd();
|
|
406
|
-
const resolveProjectKey = () => {
|
|
407
|
-
const desired = projectPath;
|
|
408
|
-
if (!data.projects || data.projects[desired]) {
|
|
409
|
-
return desired;
|
|
410
|
-
}
|
|
411
|
-
let desiredReal;
|
|
412
|
-
try {
|
|
413
|
-
desiredReal = fs.realpathSync(desired);
|
|
414
|
-
} catch {
|
|
415
|
-
return desired;
|
|
416
|
-
}
|
|
417
|
-
const keys = Object.keys(data.projects || {});
|
|
418
|
-
for (const key of keys) {
|
|
419
|
-
try {
|
|
420
|
-
if (fs.realpathSync(key) === desiredReal) {
|
|
421
|
-
return key;
|
|
422
|
-
}
|
|
423
|
-
} catch {
|
|
424
|
-
// ignore invalid paths
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
return desired;
|
|
428
|
-
};
|
|
607
|
+
filePath.endsWith(".claude.json") || filePath.endsWith("claude_desktop_config.json");
|
|
608
|
+
const resolveProjectKey = () => resolveProjectSelector(data, options.projectPath);
|
|
429
609
|
const updateClaudeMcpLists = (projectNode) => {
|
|
430
610
|
projectNode.enabledMcpServers = projectNode.enabledMcpServers || [];
|
|
431
611
|
projectNode.disabledMcpServers = projectNode.disabledMcpServers || [];
|
|
@@ -468,15 +648,35 @@ function writeClaudeConfig(filePath, serverName, argsArray) {
|
|
|
468
648
|
};
|
|
469
649
|
if (options.remove) {
|
|
470
650
|
if (isDesktopConfig) {
|
|
471
|
-
if (
|
|
472
|
-
|
|
651
|
+
if (options.allProjects) {
|
|
652
|
+
const projects = Object.keys(data.projects || {});
|
|
653
|
+
let removed = false;
|
|
654
|
+
for (const key of projects) {
|
|
655
|
+
if (data.projects?.[key]?.mcpServers?.[serverName]) {
|
|
656
|
+
delete data.projects[key].mcpServers[serverName];
|
|
657
|
+
const projectNode = data.projects[key];
|
|
658
|
+
projectNode.enabledMcpServers =
|
|
659
|
+
projectNode.enabledMcpServers?.filter((name) => name !== serverName) || [];
|
|
660
|
+
projectNode.disabledMcpServers =
|
|
661
|
+
projectNode.disabledMcpServers?.filter((name) => name !== serverName) || [];
|
|
662
|
+
removed = true;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
if (!removed) {
|
|
666
|
+
fail(`Server "${serverName}" not found in any Claude projects.`);
|
|
667
|
+
}
|
|
668
|
+
} else {
|
|
669
|
+
const projectKey = resolveProjectKey();
|
|
670
|
+
if (!data.projects?.[projectKey]?.mcpServers?.[serverName]) {
|
|
671
|
+
fail(`Server "${serverName}" not found for ${projectKey}.`);
|
|
672
|
+
}
|
|
673
|
+
delete data.projects[projectKey].mcpServers[serverName];
|
|
674
|
+
const projectNode = data.projects[projectKey];
|
|
675
|
+
projectNode.enabledMcpServers =
|
|
676
|
+
projectNode.enabledMcpServers?.filter((name) => name !== serverName) || [];
|
|
677
|
+
projectNode.disabledMcpServers =
|
|
678
|
+
projectNode.disabledMcpServers?.filter((name) => name !== serverName) || [];
|
|
473
679
|
}
|
|
474
|
-
delete data.projects[projectPath].mcpServers[serverName];
|
|
475
|
-
const projectNode = data.projects[projectPath];
|
|
476
|
-
projectNode.enabledMcpServers =
|
|
477
|
-
projectNode.enabledMcpServers?.filter((name) => name !== serverName) || [];
|
|
478
|
-
projectNode.disabledMcpServers =
|
|
479
|
-
projectNode.disabledMcpServers?.filter((name) => name !== serverName) || [];
|
|
480
680
|
} else {
|
|
481
681
|
data.mcpServers = data.mcpServers || {};
|
|
482
682
|
if (!data.mcpServers[serverName]) {
|
|
@@ -492,6 +692,42 @@ function writeClaudeConfig(filePath, serverName, argsArray) {
|
|
|
492
692
|
}
|
|
493
693
|
if (isDesktopConfig) {
|
|
494
694
|
data.projects = data.projects || {};
|
|
695
|
+
if (options.toggle) {
|
|
696
|
+
if (options.allProjects) {
|
|
697
|
+
const projects = Object.keys(data.projects || {});
|
|
698
|
+
let toggled = false;
|
|
699
|
+
for (const key of projects) {
|
|
700
|
+
if (!data.projects[key]) {
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
data.projects[key].mcpServers = data.projects[key].mcpServers || {};
|
|
704
|
+
if (!data.projects[key].mcpServers[serverName]) {
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
updateClaudeMcpLists(data.projects[key]);
|
|
708
|
+
toggled = true;
|
|
709
|
+
}
|
|
710
|
+
if (!toggled) {
|
|
711
|
+
fail(`Server "${serverName}" not found in any Claude projects.`);
|
|
712
|
+
}
|
|
713
|
+
writeFile(filePath, JSON.stringify(data, null, 2));
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
const projectKey = resolveProjectKey();
|
|
717
|
+
if (!data.projects[projectKey]) {
|
|
718
|
+
data.projects[projectKey] = {
|
|
719
|
+
allowedTools: [],
|
|
720
|
+
mcpContextUris: [],
|
|
721
|
+
mcpServers: {},
|
|
722
|
+
enabledMcpServers: [],
|
|
723
|
+
disabledMcpServers: [],
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
data.projects[projectKey].mcpServers = data.projects[projectKey].mcpServers || {};
|
|
727
|
+
updateClaudeMcpLists(data.projects[projectKey]);
|
|
728
|
+
writeFile(filePath, JSON.stringify(data, null, 2));
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
495
731
|
const projectKey = resolveProjectKey();
|
|
496
732
|
if (!data.projects[projectKey]) {
|
|
497
733
|
data.projects[projectKey] = {
|
|
@@ -502,24 +738,12 @@ function writeClaudeConfig(filePath, serverName, argsArray) {
|
|
|
502
738
|
disabledMcpServers: [],
|
|
503
739
|
};
|
|
504
740
|
}
|
|
505
|
-
data.projects[projectKey].enabledMcpServers =
|
|
506
|
-
data.projects[projectKey].enabledMcpServers || [];
|
|
741
|
+
data.projects[projectKey].enabledMcpServers = data.projects[projectKey].enabledMcpServers || [];
|
|
507
742
|
data.projects[projectKey].disabledMcpServers =
|
|
508
743
|
data.projects[projectKey].disabledMcpServers || [];
|
|
509
|
-
data.projects[projectKey].mcpServers =
|
|
510
|
-
data.projects[projectKey].mcpServers || {};
|
|
511
|
-
if (options.toggle) {
|
|
512
|
-
if (!data.projects[projectKey].mcpServers[serverName]) {
|
|
513
|
-
fail(`Server "${serverName}" not found for ${projectKey}.`);
|
|
514
|
-
}
|
|
515
|
-
updateClaudeMcpLists(data.projects[projectKey]);
|
|
516
|
-
writeFile(filePath, JSON.stringify(data, null, 2));
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
744
|
+
data.projects[projectKey].mcpServers = data.projects[projectKey].mcpServers || {};
|
|
519
745
|
if (data.projects[projectKey].mcpServers[serverName] && !options.force) {
|
|
520
|
-
fail(
|
|
521
|
-
`Server "${serverName}" already exists for ${projectKey}. Use --force to overwrite.`
|
|
522
|
-
);
|
|
746
|
+
fail(`Server "${serverName}" already exists for ${projectKey}. Use --force to overwrite.`);
|
|
523
747
|
}
|
|
524
748
|
if (options.transport === "stdio") {
|
|
525
749
|
data.projects[projectKey].mcpServers[serverName] = {
|
|
@@ -544,9 +768,7 @@ function writeClaudeConfig(filePath, serverName, argsArray) {
|
|
|
544
768
|
} else {
|
|
545
769
|
data.mcpServers = data.mcpServers || {};
|
|
546
770
|
if (data.mcpServers[serverName] && !options.force) {
|
|
547
|
-
fail(
|
|
548
|
-
`Server "${serverName}" already exists in ${filePath}. Use --force to overwrite.`
|
|
549
|
-
);
|
|
771
|
+
fail(`Server "${serverName}" already exists in ${filePath}. Use --force to overwrite.`);
|
|
550
772
|
}
|
|
551
773
|
if (options.transport === "stdio") {
|
|
552
774
|
data.mcpServers[serverName] = {
|
|
@@ -556,7 +778,7 @@ function writeClaudeConfig(filePath, serverName, argsArray) {
|
|
|
556
778
|
};
|
|
557
779
|
} else {
|
|
558
780
|
const entry = {
|
|
559
|
-
type: options.transport,
|
|
781
|
+
type: options.transport === "streamableHttp" ? "http" : options.transport,
|
|
560
782
|
url: options.url,
|
|
561
783
|
timeout: options.timeout,
|
|
562
784
|
};
|
|
@@ -569,6 +791,30 @@ function writeClaudeConfig(filePath, serverName, argsArray) {
|
|
|
569
791
|
writeFile(filePath, JSON.stringify(data, null, 2));
|
|
570
792
|
}
|
|
571
793
|
|
|
794
|
+
function resolveClaudeProjectKey(data, projectPath = process.cwd()) {
|
|
795
|
+
const desired = projectPath;
|
|
796
|
+
if (!data.projects || data.projects[desired]) {
|
|
797
|
+
return desired;
|
|
798
|
+
}
|
|
799
|
+
let desiredReal;
|
|
800
|
+
try {
|
|
801
|
+
desiredReal = fs.realpathSync(desired);
|
|
802
|
+
} catch {
|
|
803
|
+
return desired;
|
|
804
|
+
}
|
|
805
|
+
const keys = Object.keys(data.projects || {});
|
|
806
|
+
for (const key of keys) {
|
|
807
|
+
try {
|
|
808
|
+
if (fs.realpathSync(key) === desiredReal) {
|
|
809
|
+
return key;
|
|
810
|
+
}
|
|
811
|
+
} catch {
|
|
812
|
+
// ignore invalid paths
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
return desired;
|
|
816
|
+
}
|
|
817
|
+
|
|
572
818
|
function writeCodexConfig(filePath, serverName, argsArray) {
|
|
573
819
|
ensureDir(filePath);
|
|
574
820
|
if (!toml) {
|
|
@@ -601,9 +847,7 @@ function writeCodexConfig(filePath, serverName, argsArray) {
|
|
|
601
847
|
}
|
|
602
848
|
|
|
603
849
|
if (data.mcp_servers[serverName] && !options.force) {
|
|
604
|
-
fail(
|
|
605
|
-
`Server "${serverName}" already exists in ${filePath}. Use --force to overwrite.`
|
|
606
|
-
);
|
|
850
|
+
fail(`Server "${serverName}" already exists in ${filePath}. Use --force to overwrite.`);
|
|
607
851
|
}
|
|
608
852
|
|
|
609
853
|
if (options.transport === "stdio") {
|
|
@@ -655,9 +899,7 @@ function writeGooseConfig(filePath, serverName, argsArray) {
|
|
|
655
899
|
return;
|
|
656
900
|
}
|
|
657
901
|
if (data.extensions[serverName] && !options.force) {
|
|
658
|
-
fail(
|
|
659
|
-
`Server "${serverName}" already exists in ${filePath}. Use --force to overwrite.`
|
|
660
|
-
);
|
|
902
|
+
fail(`Server "${serverName}" already exists in ${filePath}. Use --force to overwrite.`);
|
|
661
903
|
}
|
|
662
904
|
const enabled = false;
|
|
663
905
|
if (options.transport === "stdio") {
|
|
@@ -691,6 +933,137 @@ function writeGooseConfig(filePath, serverName, argsArray) {
|
|
|
691
933
|
writeFile(filePath, yaml.stringify(data));
|
|
692
934
|
}
|
|
693
935
|
|
|
936
|
+
function listJsonConfig(filePath, clientType) {
|
|
937
|
+
const data = readJson(filePath);
|
|
938
|
+
let store;
|
|
939
|
+
if (clientType === "opencode") {
|
|
940
|
+
store = data.mcp || {};
|
|
941
|
+
} else if (clientType === "copilot") {
|
|
942
|
+
store = data.servers || {};
|
|
943
|
+
} else {
|
|
944
|
+
store = data.mcpServers || {};
|
|
945
|
+
}
|
|
946
|
+
outputList(filePath, Object.keys(store));
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
function listCodexConfig(filePath) {
|
|
950
|
+
if (!toml) {
|
|
951
|
+
fail("TOML dependency not available. Install dependencies and retry.");
|
|
952
|
+
}
|
|
953
|
+
const data = readToml(filePath);
|
|
954
|
+
const store = data.mcp_servers || {};
|
|
955
|
+
outputList(filePath, Object.keys(store));
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
function listGooseConfig(filePath) {
|
|
959
|
+
if (!yaml) {
|
|
960
|
+
fail("YAML dependency not available. Install dependencies and retry.");
|
|
961
|
+
}
|
|
962
|
+
const data = readYaml(filePath);
|
|
963
|
+
const store = data.extensions || {};
|
|
964
|
+
outputList(filePath, Object.keys(store));
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
function listClaudeConfig(filePath, allProjects, projectPath) {
|
|
968
|
+
const data = readJson(filePath);
|
|
969
|
+
const isDesktopConfig =
|
|
970
|
+
filePath.endsWith(".claude.json") || filePath.endsWith("claude_desktop_config.json");
|
|
971
|
+
if (isDesktopConfig) {
|
|
972
|
+
const projects = Object.keys(data.projects || {});
|
|
973
|
+
if (allProjects) {
|
|
974
|
+
if (!projects.length) {
|
|
975
|
+
outputList(filePath, [], "no-projects");
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
for (const key of projects.sort()) {
|
|
979
|
+
const projectNode = data.projects?.[key];
|
|
980
|
+
const store = projectNode?.mcpServers || {};
|
|
981
|
+
outputList(filePath, Object.keys(store), key);
|
|
982
|
+
}
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
const projectKey = resolveProjectSelector(data, projectPath);
|
|
986
|
+
const projectNode = data.projects?.[projectKey];
|
|
987
|
+
const store = projectNode?.mcpServers || {};
|
|
988
|
+
outputList(filePath, Object.keys(store), projectKey);
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
const store = data.mcpServers || {};
|
|
992
|
+
outputList(filePath, Object.keys(store));
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
function claudeLocalHasServer(filePath, serverName) {
|
|
996
|
+
const data = readJson(filePath);
|
|
997
|
+
const store = data.mcpServers || {};
|
|
998
|
+
return Boolean(store[serverName]);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
function whereJsonConfig(filePath, clientType, serverName) {
|
|
1002
|
+
const data = readJson(filePath);
|
|
1003
|
+
let store;
|
|
1004
|
+
if (clientType === "opencode") {
|
|
1005
|
+
store = data.mcp || {};
|
|
1006
|
+
} else if (clientType === "copilot") {
|
|
1007
|
+
store = data.servers || {};
|
|
1008
|
+
} else {
|
|
1009
|
+
store = data.mcpServers || {};
|
|
1010
|
+
}
|
|
1011
|
+
outputWhere(filePath, serverName, Boolean(store[serverName]));
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
function whereCodexConfig(filePath, serverName) {
|
|
1015
|
+
if (!toml) {
|
|
1016
|
+
fail("TOML dependency not available. Install dependencies and retry.");
|
|
1017
|
+
}
|
|
1018
|
+
const data = readToml(filePath);
|
|
1019
|
+
const store = data.mcp_servers || {};
|
|
1020
|
+
outputWhere(filePath, serverName, Boolean(store[serverName]));
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
function whereGooseConfig(filePath, serverName) {
|
|
1024
|
+
if (!yaml) {
|
|
1025
|
+
fail("YAML dependency not available. Install dependencies and retry.");
|
|
1026
|
+
}
|
|
1027
|
+
const data = readYaml(filePath);
|
|
1028
|
+
const store = data.extensions || {};
|
|
1029
|
+
outputWhere(filePath, serverName, Boolean(store[serverName]));
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
function whereClaudeConfig(filePath, serverName, allProjects, projectPath) {
|
|
1033
|
+
const data = readJson(filePath);
|
|
1034
|
+
const isDesktopConfig =
|
|
1035
|
+
filePath.endsWith(".claude.json") || filePath.endsWith("claude_desktop_config.json");
|
|
1036
|
+
if (isDesktopConfig) {
|
|
1037
|
+
const projects = Object.keys(data.projects || {});
|
|
1038
|
+
if (allProjects) {
|
|
1039
|
+
if (!projects.length) {
|
|
1040
|
+
outputWhere(filePath, serverName, false, "no-projects");
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
let found = false;
|
|
1044
|
+
for (const key of projects.sort()) {
|
|
1045
|
+
const projectNode = data.projects?.[key];
|
|
1046
|
+
const store = projectNode?.mcpServers || {};
|
|
1047
|
+
if (store[serverName]) {
|
|
1048
|
+
outputWhere(filePath, serverName, true, key);
|
|
1049
|
+
found = true;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
if (!found) {
|
|
1053
|
+
outputWhere(filePath, serverName, false, "all-projects");
|
|
1054
|
+
}
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
const projectKey = resolveProjectSelector(data, projectPath);
|
|
1058
|
+
const projectNode = data.projects?.[projectKey];
|
|
1059
|
+
const store = projectNode?.mcpServers || {};
|
|
1060
|
+
outputWhere(filePath, serverName, Boolean(store[serverName]), projectKey);
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
const store = data.mcpServers || {};
|
|
1064
|
+
outputWhere(filePath, serverName, Boolean(store[serverName]));
|
|
1065
|
+
}
|
|
1066
|
+
|
|
694
1067
|
function readJson(filePath) {
|
|
695
1068
|
if (!fs.existsSync(filePath)) {
|
|
696
1069
|
return {};
|
|
@@ -736,6 +1109,24 @@ function readToml(filePath) {
|
|
|
736
1109
|
}
|
|
737
1110
|
}
|
|
738
1111
|
|
|
1112
|
+
function outputList(filePath, keys, projectKey) {
|
|
1113
|
+
const header = projectKey ? `# ${filePath} (${projectKey})` : `# ${filePath}`;
|
|
1114
|
+
process.stdout.write(`${header}\n`);
|
|
1115
|
+
if (!keys.length) {
|
|
1116
|
+
process.stdout.write("- (none)\n");
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
for (const name of keys.sort()) {
|
|
1120
|
+
process.stdout.write(`- ${name}\n`);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
function outputWhere(filePath, serverName, found, projectKey) {
|
|
1125
|
+
const header = projectKey ? `# ${filePath} (${projectKey})` : `# ${filePath}`;
|
|
1126
|
+
process.stdout.write(`${header}\n`);
|
|
1127
|
+
process.stdout.write(`- ${serverName}: ${found ? "found" : "not found"}\n`);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
739
1130
|
function ensureDir(filePath) {
|
|
740
1131
|
const dir = path.dirname(filePath);
|
|
741
1132
|
if (!fs.existsSync(dir)) {
|
|
@@ -757,30 +1148,140 @@ function fail(message) {
|
|
|
757
1148
|
process.exit(1);
|
|
758
1149
|
}
|
|
759
1150
|
|
|
760
|
-
function printHelp() {
|
|
761
|
-
|
|
1151
|
+
function printHelp(command) {
|
|
1152
|
+
const header = "mcp-conf";
|
|
1153
|
+
if (!command) {
|
|
1154
|
+
process.stdout.write(`${header}
|
|
1155
|
+
|
|
1156
|
+
Usage:
|
|
1157
|
+
mcp-conf <add|rm|ls|enable|disable|where> --client <name> [options]
|
|
1158
|
+
mcp-conf help <command>
|
|
1159
|
+
|
|
1160
|
+
Commands:
|
|
1161
|
+
add add or update an MCP server entry
|
|
1162
|
+
rm remove an MCP server entry
|
|
1163
|
+
ls list MCP server entries
|
|
1164
|
+
enable enable an existing entry
|
|
1165
|
+
disable disable an existing entry
|
|
1166
|
+
where show where a server name is defined
|
|
1167
|
+
|
|
1168
|
+
Run:
|
|
1169
|
+
mcp-conf <command> --help
|
|
1170
|
+
mcp-conf help <command>
|
|
1171
|
+
|
|
1172
|
+
Notes:
|
|
1173
|
+
Scope defaults to --global (Copilot uses --local only).
|
|
1174
|
+
For Claude, --local maps to the project scope file ./.mcp.json.
|
|
1175
|
+
`);
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
switch (command) {
|
|
1179
|
+
case "add":
|
|
1180
|
+
process.stdout.write(`${header} add
|
|
762
1181
|
|
|
763
1182
|
Usage:
|
|
764
|
-
mcp-conf --client <name> --name <serverName> [--env <path> | --mcp <dest>] [options]
|
|
1183
|
+
mcp-conf add --client <name> --name <serverName> [--env <path> | --mcp <dest>] [options]
|
|
765
1184
|
|
|
766
1185
|
Options:
|
|
767
1186
|
--client <name> cline | codex | claude | goose | cursor | windsurf | opencode | copilot (repeatable)
|
|
768
1187
|
--name <serverName> required MCP server name key
|
|
769
|
-
--env <path> .env path (
|
|
770
|
-
--mcp <dest> destination name (
|
|
1188
|
+
--env <path> .env path (stdio only)
|
|
1189
|
+
--mcp <dest> destination name (stdio only)
|
|
771
1190
|
--transport <type> stdio | sse | http (http => streamableHttp)
|
|
772
1191
|
--command <bin> command to run (default: mcp-abap-adt)
|
|
1192
|
+
--global write to global user config (default)
|
|
1193
|
+
--local write to project config (where supported)
|
|
1194
|
+
--project <path> Claude global: target a specific project path
|
|
773
1195
|
--url <http(s)://...> required for sse/http
|
|
774
1196
|
--header key=value add request header (repeatable)
|
|
775
1197
|
--timeout <seconds> entry timeout (default: 60)
|
|
776
|
-
--
|
|
777
|
-
--enable enable entry (Codex/OpenCode/Cline/Windsurf/Goose/Claude; not Cursor/Copilot)
|
|
778
|
-
--remove remove entry
|
|
779
|
-
--force overwrite existing entry (add/update)
|
|
1198
|
+
--force overwrite existing entry
|
|
780
1199
|
--dry-run print changes without writing files
|
|
781
|
-
|
|
1200
|
+
`);
|
|
1201
|
+
break;
|
|
1202
|
+
case "rm":
|
|
1203
|
+
process.stdout.write(`${header} rm
|
|
782
1204
|
|
|
783
|
-
|
|
784
|
-
|
|
1205
|
+
Usage:
|
|
1206
|
+
mcp-conf rm --client <name> --name <serverName> [options]
|
|
1207
|
+
|
|
1208
|
+
Options:
|
|
1209
|
+
--client <name> cline | codex | claude | goose | cursor | windsurf | opencode | copilot (repeatable)
|
|
1210
|
+
--name <serverName> required MCP server name key
|
|
1211
|
+
--global write to global user config (default)
|
|
1212
|
+
--local write to project config (where supported)
|
|
1213
|
+
--all-projects Claude global: remove across all projects
|
|
1214
|
+
--project <path> Claude global: target a specific project path
|
|
1215
|
+
--dry-run print changes without writing files
|
|
785
1216
|
`);
|
|
1217
|
+
break;
|
|
1218
|
+
case "ls":
|
|
1219
|
+
process.stdout.write(`${header} ls
|
|
1220
|
+
|
|
1221
|
+
Usage:
|
|
1222
|
+
mcp-conf ls --client <name> [options]
|
|
1223
|
+
|
|
1224
|
+
Options:
|
|
1225
|
+
--client <name> cline | codex | claude | goose | cursor | windsurf | opencode | copilot (repeatable)
|
|
1226
|
+
--global write to global user config (default)
|
|
1227
|
+
--local write to project config (where supported)
|
|
1228
|
+
--all-projects Claude global: list across all projects
|
|
1229
|
+
--project <path> Claude global: target a specific project path
|
|
1230
|
+
`);
|
|
1231
|
+
break;
|
|
1232
|
+
case "enable":
|
|
1233
|
+
process.stdout.write(`${header} enable
|
|
1234
|
+
|
|
1235
|
+
Usage:
|
|
1236
|
+
mcp-conf enable --client <name> --name <serverName> [options]
|
|
1237
|
+
|
|
1238
|
+
Options:
|
|
1239
|
+
--client <name> cline | codex | claude | goose | cursor | windsurf | opencode | copilot (repeatable)
|
|
1240
|
+
--name <serverName> required MCP server name key
|
|
1241
|
+
--global write to global user config (default)
|
|
1242
|
+
--local write to project config (where supported)
|
|
1243
|
+
--all-projects Claude global: enable across all projects
|
|
1244
|
+
--project <path> Claude global: target a specific project path
|
|
1245
|
+
--dry-run print changes without writing files
|
|
1246
|
+
`);
|
|
1247
|
+
break;
|
|
1248
|
+
case "disable":
|
|
1249
|
+
process.stdout.write(`${header} disable
|
|
1250
|
+
|
|
1251
|
+
Usage:
|
|
1252
|
+
mcp-conf disable --client <name> --name <serverName> [options]
|
|
1253
|
+
|
|
1254
|
+
Options:
|
|
1255
|
+
--client <name> cline | codex | claude | goose | cursor | windsurf | opencode | copilot (repeatable)
|
|
1256
|
+
--name <serverName> required MCP server name key
|
|
1257
|
+
--global write to global user config (default)
|
|
1258
|
+
--local write to project config (where supported)
|
|
1259
|
+
--all-projects Claude global: disable across all projects
|
|
1260
|
+
--project <path> Claude global: target a specific project path
|
|
1261
|
+
--dry-run print changes without writing files
|
|
1262
|
+
`);
|
|
1263
|
+
break;
|
|
1264
|
+
case "where":
|
|
1265
|
+
process.stdout.write(`${header} where
|
|
1266
|
+
|
|
1267
|
+
Usage:
|
|
1268
|
+
mcp-conf where --client <name> --name <serverName> [options]
|
|
1269
|
+
|
|
1270
|
+
Options:
|
|
1271
|
+
--client <name> cline | codex | claude | goose | cursor | windsurf | opencode | copilot (repeatable)
|
|
1272
|
+
--name <serverName> required MCP server name key
|
|
1273
|
+
--global write to global user config (default)
|
|
1274
|
+
--local write to project config (where supported)
|
|
1275
|
+
--all-projects Claude global: search across all projects
|
|
1276
|
+
--project <path> Claude global: target a specific project path
|
|
1277
|
+
`);
|
|
1278
|
+
break;
|
|
1279
|
+
default:
|
|
1280
|
+
process.stdout.write(`${header}
|
|
1281
|
+
|
|
1282
|
+
Unknown command "${command}".
|
|
1283
|
+
Run:
|
|
1284
|
+
mcp-conf help
|
|
1285
|
+
`);
|
|
1286
|
+
}
|
|
786
1287
|
}
|
|
@@ -11,79 +11,102 @@ npm install -g @mcp-abap-adt/configurator
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
mcp-conf --client cline --env /path/to/.env --name abap
|
|
15
|
-
mcp-conf --client cline --mcp TRIAL --name abap
|
|
16
|
-
mcp-conf --client cline --env /path/to/.env --name abap --transport stdio
|
|
17
|
-
mcp-conf --client claude --mcp TRIAL --name abap
|
|
18
|
-
mcp-conf --client codex --name abap
|
|
19
|
-
mcp-conf --client cline --name direct-jwt-test-001 --transport http --url http://localhost:4004/mcp/stream/http --header x-sap-url=https://... --header x-sap-client=210 --header x-sap-auth-type=jwt --header x-sap-jwt-token=...
|
|
20
|
-
mcp-conf --client cline --name local-mcp-sse --transport sse --url http://localhost:3001/sse
|
|
21
|
-
mcp-conf --client codex --name abap-http --transport http --url http://localhost:3000/mcp/stream/http
|
|
22
|
-
mcp-conf --client codex --name abap-http --transport http --url http://localhost:3000/mcp/stream/http --header x-mcp-destination=trial
|
|
23
|
-
mcp-conf --client opencode --name abap --transport http --url http://localhost:3000/mcp/stream/http
|
|
24
|
-
mcp-conf --client copilot --name abap --transport http --url http://localhost:3000/mcp/stream/http --header x-mcp-destination=trial
|
|
14
|
+
mcp-conf add --client cline --env /path/to/.env --name abap
|
|
15
|
+
mcp-conf add --client cline --mcp TRIAL --name abap
|
|
16
|
+
mcp-conf add --client cline --env /path/to/.env --name abap --transport stdio
|
|
17
|
+
mcp-conf add --client claude --mcp TRIAL --name abap
|
|
18
|
+
mcp-conf rm --client codex --name abap
|
|
19
|
+
mcp-conf add --client cline --name direct-jwt-test-001 --transport http --url http://localhost:4004/mcp/stream/http --header x-sap-url=https://... --header x-sap-client=210 --header x-sap-auth-type=jwt --header x-sap-jwt-token=...
|
|
20
|
+
mcp-conf add --client cline --name local-mcp-sse --transport sse --url http://localhost:3001/sse
|
|
21
|
+
mcp-conf add --client codex --name abap-http --transport http --url http://localhost:3000/mcp/stream/http
|
|
22
|
+
mcp-conf add --client codex --name abap-http --transport http --url http://localhost:3000/mcp/stream/http --header x-mcp-destination=trial
|
|
23
|
+
mcp-conf add --client opencode --name abap --transport http --url http://localhost:3000/mcp/stream/http
|
|
24
|
+
mcp-conf add --client copilot --name abap --transport http --url http://localhost:3000/mcp/stream/http --header x-mcp-destination=trial
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
## Common Tasks
|
|
28
28
|
|
|
29
29
|
Add MCP:
|
|
30
30
|
```bash
|
|
31
|
-
mcp-conf --client codex --mcp TRIAL --name abap
|
|
32
|
-
mcp-conf --client cline --env /path/to/.env --name abap
|
|
33
|
-
mcp-conf --client claude --mcp TRIAL --name abap
|
|
31
|
+
mcp-conf add --client codex --mcp TRIAL --name abap
|
|
32
|
+
mcp-conf add --client cline --env /path/to/.env --name abap
|
|
33
|
+
mcp-conf add --client claude --mcp TRIAL --name abap
|
|
34
|
+
mcp-conf add --client claude --name abap-http --transport http --url http://localhost:3000/mcp/stream/http --header x-mcp-destination=trial
|
|
35
|
+
mcp-conf add --client claude --name abap --project /path/to/project --mcp TRIAL
|
|
34
36
|
```
|
|
35
37
|
|
|
36
38
|
Disable MCP:
|
|
37
39
|
```bash
|
|
38
|
-
mcp-conf --client codex --name abap
|
|
39
|
-
mcp-conf --client cline --name abap
|
|
40
|
+
mcp-conf disable --client codex --name abap
|
|
41
|
+
mcp-conf disable --client cline --name abap
|
|
40
42
|
```
|
|
41
43
|
|
|
42
44
|
Enable MCP:
|
|
43
45
|
```bash
|
|
44
|
-
mcp-conf --client codex --name abap
|
|
45
|
-
mcp-conf --client cline --name abap
|
|
46
|
+
mcp-conf enable --client codex --name abap
|
|
47
|
+
mcp-conf enable --client cline --name abap
|
|
46
48
|
```
|
|
47
49
|
|
|
48
50
|
Remove MCP:
|
|
49
51
|
```bash
|
|
50
|
-
mcp-conf --client codex --name abap
|
|
51
|
-
mcp-conf --client cline --name abap
|
|
52
|
-
mcp-conf --client claude --name abap
|
|
52
|
+
mcp-conf rm --client codex --name abap
|
|
53
|
+
mcp-conf rm --client cline --name abap
|
|
54
|
+
mcp-conf rm --client claude --name abap
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
List MCP servers:
|
|
58
|
+
```bash
|
|
59
|
+
mcp-conf ls --client codex
|
|
60
|
+
mcp-conf ls --client cline
|
|
61
|
+
mcp-conf ls --client claude --local
|
|
62
|
+
mcp-conf ls --client claude --all-projects
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Find where a server is defined:
|
|
66
|
+
```bash
|
|
67
|
+
mcp-conf where --client codex --name abap
|
|
68
|
+
mcp-conf where --client claude --name goose --project /path/to/project
|
|
69
|
+
mcp-conf where --client claude --name goose --all-projects
|
|
53
70
|
```
|
|
54
71
|
|
|
55
72
|
Options:
|
|
73
|
+
- Commands: `add`, `rm`, `ls`, `enable`, `disable`, `where` (first argument)
|
|
56
74
|
- `--client <name>` (repeatable): `cline`, `codex`, `claude`, `goose`, `cursor`, `windsurf`, `opencode`, `copilot`
|
|
57
75
|
- `--env <path>`: use a specific `.env` file
|
|
58
76
|
- `--mcp <destination>`: use service key destination
|
|
59
77
|
- `--name <serverName>`: MCP server name (required)
|
|
60
78
|
- `--transport <type>`: `stdio`, `sse`, or `http` (`http` maps to `streamableHttp`)
|
|
61
79
|
- `--command <bin>`: command to run (default: `mcp-abap-adt`)
|
|
80
|
+
- `--global`: write to the global user config (default)
|
|
81
|
+
- `--local`: write to the project config (supported by `cursor`, `opencode`, `copilot`, `claude`)
|
|
82
|
+
- `--all-projects`: for Claude (global scope), apply `rm/enable/disable/ls/where` across all projects
|
|
83
|
+
- `--project <path>`: for Claude (global scope), target a specific project path
|
|
62
84
|
- `--url <http(s)://...>`: required for `sse` and `http`
|
|
63
85
|
- `--header key=value`: add request header (repeatable)
|
|
64
86
|
- `--timeout <seconds>`: timeout value for client entries (default: 60)
|
|
65
|
-
- `--disable`: disable server entry (Codex/OpenCode: `enabled = false`, Cline/Windsurf: `disabled = true`, Claude: moves name to `disabledMcpServers`; not Cursor/Copilot)
|
|
66
|
-
- `--enable`: enable server entry (Codex/OpenCode: `enabled = true`, Cline/Windsurf: `disabled = false`, Claude: moves name to `enabledMcpServers`; not Cursor/Copilot)
|
|
67
|
-
- `--remove`: remove server entry from client config
|
|
68
87
|
|
|
69
88
|
Notes:
|
|
70
|
-
-
|
|
89
|
+
- `disable` and `rm` do not require `--env` or `--mcp`.
|
|
71
90
|
- `--env`/`--mcp` are only valid for `stdio` transport. For `sse/http`, use `--url` and optional `--header`.
|
|
72
91
|
- Cursor/Copilot enable/disable are not implemented yet.
|
|
73
92
|
- Claude stores enable/disable state under `enabledMcpServers` and `disabledMcpServers` for each project.
|
|
74
|
-
-
|
|
93
|
+
- Claude enable/disable always updates `~/.claude.json` (global scope), even if you pass `--local`.
|
|
94
|
+
- New entries for Cline, Codex, Windsurf, Goose, Claude, and OpenCode are added **disabled by default**. Use `enable` to turn them on.
|
|
75
95
|
- Windsurf follows `disabled` like Cline. The configurator sets `disabled = true` for default-disabled entries.
|
|
76
|
-
-
|
|
96
|
+
- `enable`/`disable` only work if the server entry already exists. Use add commands with `--env` or `--mcp` first.
|
|
77
97
|
- Non-stdio transports are supported for Cline/Cursor/Windsurf/Claude/Goose. Codex supports `http` (streamable HTTP) but not `sse`.
|
|
78
98
|
- Codex writes custom headers under `http_headers` in `~/.codex/config.toml`.
|
|
79
99
|
- Codex HTTP entries include `startup_timeout_sec` (default: 60).
|
|
80
100
|
- `--dry-run`: print changes without writing files
|
|
81
101
|
- `--force`: overwrite existing server entry if it exists
|
|
102
|
+
- Scope defaults to `--global` (GitHub Copilot is `--local` only).
|
|
103
|
+
- For Claude, `--local` maps to the documented project scope file `./.mcp.json`.
|
|
82
104
|
|
|
83
105
|
## Config Locations
|
|
84
106
|
|
|
85
107
|
Paths are client-specific and OS-dependent. The installer writes config files in:
|
|
86
108
|
|
|
109
|
+
Global (default) locations:
|
|
87
110
|
- **Cline**:
|
|
88
111
|
- Linux/macOS: `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
|
|
89
112
|
- Windows: `%APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json`
|
|
@@ -104,8 +127,16 @@ Paths are client-specific and OS-dependent. The installer writes config files in
|
|
|
104
127
|
- **Windsurf**:
|
|
105
128
|
- Linux/macOS: `~/.codeium/windsurf/mcp_config.json`
|
|
106
129
|
- Windows: `%USERPROFILE%\.codeium\windsurf\mcp_config.json`
|
|
130
|
+
- **OpenCode**:
|
|
131
|
+
- Linux/macOS: `~/.config/opencode/opencode.json`
|
|
132
|
+
- Windows: `%APPDATA%\opencode\opencode.json`
|
|
133
|
+
|
|
134
|
+
Local (project) locations:
|
|
135
|
+
- **Claude Code**:
|
|
136
|
+
- Project: `./.mcp.json`
|
|
137
|
+
- **Cursor**:
|
|
138
|
+
- Project: `./.cursor/mcp.json`
|
|
107
139
|
- **OpenCode**:
|
|
108
140
|
- Project: `./opencode.json` (uses `mcp.<name>` entries with `enabled: true|false`)
|
|
109
141
|
- **GitHub Copilot**:
|
|
110
142
|
- Project: `./.vscode/mcp.json` (uses `servers.<name>` entries)
|
|
111
|
-
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-abap-adt/configurator",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "MCP client configurator for mcp-abap-adt and mcp-abap-adt-proxy",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -17,12 +17,20 @@
|
|
|
17
17
|
"README.md",
|
|
18
18
|
"LICENSE"
|
|
19
19
|
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"lint": "biome check .",
|
|
22
|
+
"format": "biome format . --write",
|
|
23
|
+
"build": "npm run lint"
|
|
24
|
+
},
|
|
20
25
|
"engines": {
|
|
21
26
|
"node": ">=18.0.0",
|
|
22
27
|
"npm": ">=9.0.0"
|
|
23
28
|
},
|
|
24
29
|
"dependencies": {
|
|
25
|
-
"@iarna/toml": "^
|
|
30
|
+
"@iarna/toml": "^3.0.0",
|
|
26
31
|
"yaml": "^2.8.1"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@biomejs/biome": "^2.3.14"
|
|
27
35
|
}
|
|
28
36
|
}
|