@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 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
- printHelp();
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
- const requiresConnectionParams = !options.remove && !options.toggle;
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
- switch (client) {
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
- writeJsonConfig(
143
- getClinePath(platform, home, appData),
144
- options.name,
145
- serverArgs,
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
- writeCodexConfig(getCodexPath(platform, home, userProfile), options.name, serverArgs);
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
- writeClaudeConfig(
157
- getClaudePath(platform, home, appData),
158
- options.name,
159
- serverArgs
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
- writeGooseConfig(
164
- getGoosePath(platform, home, appData),
165
- options.name,
166
- serverArgs
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
- writeJsonConfig(getOpenCodePath(), options.name, serverArgs, "opencode");
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
- writeJsonConfig(getCopilotPath(), options.name, serverArgs, "copilot");
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
- writeJsonConfig(
177
- getCursorPath(platform, home, userProfile),
178
- options.name,
179
- serverArgs,
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
- writeJsonConfig(
185
- getWindsurfPath(platform, home, userProfile),
186
- options.name,
187
- serverArgs,
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
- return path.join(process.cwd(), "opencode.json");
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 ? false : true;
511
+ store[serverName].enabled = !options.disabled;
305
512
  } else {
306
- store[serverName].disabled = options.disabled ? true : false;
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 ? true : false;
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
- filePath.endsWith("claude_desktop_config.json");
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 (!data.projects?.[projectPath]?.mcpServers?.[serverName]) {
472
- fail(`Server "${serverName}" not found for ${projectPath}.`);
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
- process.stdout.write(`mcp-conf
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 (add/update only)
770
- --mcp <dest> destination name (add/update only)
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
- --disable disable entry (Codex/OpenCode/Cline/Windsurf/Goose/Claude; not Cursor/Copilot)
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
- -h, --help show this help
1200
+ `);
1201
+ break;
1202
+ case "rm":
1203
+ process.stdout.write(`${header} rm
782
1204
 
783
- Notes:
784
- New entries for Cline/Codex/Windsurf/Goose/Claude/OpenCode are added disabled by default.
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 --remove
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 --disable
39
- mcp-conf --client cline --name abap --disable
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 --enable
45
- mcp-conf --client cline --name abap --enable
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 --remove
51
- mcp-conf --client cline --name abap --remove
52
- mcp-conf --client claude --name abap --remove
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
- - `--disable` and `--remove` do not require `--env` or `--mcp`.
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
- - New entries for Cline, Codex, Windsurf, Goose, Claude, and OpenCode are added **disabled by default**. Use `--enable` to turn them on.
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
- - `--enable`/`--disable` only work if the server entry already exists. Use add commands with `--env` or `--mcp` first.
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.1",
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": "^2.2.5",
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
  }