@mcp-abap-adt/configurator 0.0.7 → 0.0.8

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/README.md CHANGED
@@ -25,7 +25,7 @@ mcp-conf tui
25
25
 
26
26
  ## TUI
27
27
 
28
- `mcp-conf tui` starts an interactive wizard.
28
+ `mcp-conf tui` starts an interactive wizard (`ls`/`add`/`show`/`update`/`rm`/`enable`/`disable`).
29
29
  - Step order: `operation` -> `client` -> `scope` (auto-skipped if only one scope is supported).
30
30
  - For `add` + `sse/http`: prompts for URL, timeout, and repeatable headers.
31
31
  - For `rm`/`enable`/`disable`: server name is selected from existing servers in chosen client/scope.
@@ -33,7 +33,15 @@ async function main() {
33
33
  timeout: 60,
34
34
  };
35
35
 
36
- result.tuiAction = await askSelect("Operation", ["ls", "add", "rm", "enable", "disable"]);
36
+ result.tuiAction = await askSelect("Operation", [
37
+ "ls",
38
+ "add",
39
+ "show",
40
+ "update",
41
+ "rm",
42
+ "enable",
43
+ "disable",
44
+ ]);
37
45
  const client = await askSelect(
38
46
  "Client",
39
47
  CLIENTS.map((item) => item.name),
@@ -44,7 +52,7 @@ async function main() {
44
52
  const scopes = getSupportedScopes(client);
45
53
  result.scope = scopes.length === 1 ? scopes[0] : await askSelect("Scope", ["global", "local"]);
46
54
 
47
- if (["rm", "enable", "disable"].includes(result.tuiAction)) {
55
+ if (["rm", "enable", "disable", "show", "update"].includes(result.tuiAction)) {
48
56
  const serverNames = listExistingServers(client, result.scope);
49
57
  if (serverNames.length === 0) {
50
58
  throw new Error(`No existing MCP servers found for ${client} (${result.scope})`);
@@ -57,6 +65,9 @@ async function main() {
57
65
  }
58
66
 
59
67
  if (result.tuiAction !== "add") {
68
+ if (result.tuiAction === "update") {
69
+ await configureUpdate(result, client);
70
+ }
60
71
  emitResult(result);
61
72
  return;
62
73
  }
@@ -105,11 +116,62 @@ async function main() {
105
116
  emitResult(result);
106
117
  }
107
118
 
119
+ async function configureUpdate(result, client) {
120
+ const current = getServerConfig(client, result.scope, result.name);
121
+ const currentTransport = current.transport || "stdio";
122
+ result.transport = currentTransport;
123
+ result.command = current.command || "mcp-abap-adt";
124
+ result.timeout = Number(current.timeout) > 0 ? Number(current.timeout) : 60;
125
+ result.url = current.url || null;
126
+ result.headers = current.headers || {};
127
+
128
+ if (currentTransport === "stdio") {
129
+ const authChoices = [
130
+ "service key destination (--mcp)",
131
+ "session environment (--env)",
132
+ "specific env file (--env-path)",
133
+ ];
134
+ const authType = current.auth?.type || "unknown";
135
+ const authInitial =
136
+ authType === "mcp" ? 0 : authType === "env" ? 1 : authType === "env-path" ? 2 : 0;
137
+ const authSource = await askSelect("Auth source for stdio", authChoices, null, authInitial);
138
+ if (authSource.startsWith("service key")) {
139
+ result.mcpDestination = await askInput("Destination name", current.auth?.value || "TRIAL");
140
+ result.useSessionEnv = false;
141
+ result.envPath = null;
142
+ result.url = null;
143
+ result.headers = {};
144
+ return;
145
+ }
146
+ if (authSource.startsWith("specific env file")) {
147
+ result.envPath = await askInput("Path to .env file", current.auth?.value || undefined);
148
+ result.useSessionEnv = false;
149
+ result.mcpDestination = null;
150
+ result.url = null;
151
+ result.headers = {};
152
+ return;
153
+ }
154
+ result.useSessionEnv = true;
155
+ result.envPath = null;
156
+ result.mcpDestination = null;
157
+ result.url = null;
158
+ result.headers = {};
159
+ return;
160
+ }
161
+
162
+ result.url = await askInput("Server URL (http/https)", current.url || undefined);
163
+ result.timeout = await askPositiveNumber("Timeout seconds", result.timeout);
164
+ result.headers = await askHeaders(current.headers || {});
165
+ result.useSessionEnv = false;
166
+ result.envPath = null;
167
+ result.mcpDestination = null;
168
+ }
169
+
108
170
  function emitResult(result) {
109
171
  fs.writeFileSync(3, JSON.stringify(result), "utf8");
110
172
  }
111
173
 
112
- async function askSelect(message, choices, choiceLabels) {
174
+ async function askSelect(message, choices, choiceLabels, initial = 0) {
113
175
  const promptChoices = choices.map((value, index) => ({
114
176
  name: value,
115
177
  message: choiceLabels?.[index] || value,
@@ -118,6 +180,7 @@ async function askSelect(message, choices, choiceLabels) {
118
180
  name: "value",
119
181
  message,
120
182
  choices: promptChoices,
183
+ initial,
121
184
  footer: "Use arrow keys + Enter. Ctrl+C to cancel.",
122
185
  });
123
186
  return select.run();
@@ -146,14 +209,14 @@ async function askPositiveNumber(message, initialValue) {
146
209
  }
147
210
  }
148
211
 
149
- async function askHeaders() {
150
- const headers = {};
212
+ async function askHeaders(initialHeaders = {}) {
213
+ const headers = { ...initialHeaders };
151
214
  while (true) {
152
215
  const key = await askSelect("Header key", [...HEADER_KEYS, "done"]);
153
216
  if (key === "done") {
154
217
  return headers;
155
218
  }
156
- headers[key] = await askInput(`Value for ${key}`);
219
+ headers[key] = await askInput(`Value for ${key}`, headers[key] || undefined);
157
220
  const addMore = await new Confirm({
158
221
  name: "value",
159
222
  message: "Add another header?",
@@ -207,6 +270,27 @@ function listExistingServers(clientName, scope) {
207
270
  return [...new Set(names)].sort((a, b) => a.localeCompare(b));
208
271
  }
209
272
 
273
+ function getServerConfig(clientName, scope, serverName) {
274
+ const cliPath = path.join(__dirname, "mcp-conf.js");
275
+ const scopeArg = scope === "local" ? "--local" : "--global";
276
+ const run = spawnSync(
277
+ process.execPath,
278
+ [cliPath, "show", "--client", clientName, "--name", serverName, scopeArg, "--json"],
279
+ {
280
+ encoding: "utf8",
281
+ },
282
+ );
283
+ if (run.status !== 0) {
284
+ const stderr = String(run.stderr || "").trim();
285
+ throw new Error(stderr || "Failed to read existing server config");
286
+ }
287
+ try {
288
+ return JSON.parse(String(run.stdout || "{}"));
289
+ } catch {
290
+ throw new Error("Failed to parse existing server config");
291
+ }
292
+ }
293
+
210
294
  main().catch((error) => {
211
295
  if (error === "") {
212
296
  process.exit(0);
package/bin/mcp-conf.js CHANGED
@@ -24,7 +24,12 @@ try {
24
24
 
25
25
  const args = process.argv.slice(2);
26
26
  const action = args[0] && !args[0].startsWith("-") ? args[0] : null;
27
- if (action && ["add", "rm", "ls", "enable", "disable", "where", "tui", "help"].includes(action)) {
27
+ if (
28
+ action &&
29
+ ["add", "rm", "ls", "enable", "disable", "where", "show", "update", "tui", "help"].includes(
30
+ action,
31
+ )
32
+ ) {
28
33
  args.shift();
29
34
  }
30
35
  const options = {
@@ -45,6 +50,8 @@ const options = {
45
50
  allProjects: false,
46
51
  projectPath: null,
47
52
  where: false,
53
+ show: false,
54
+ outputJson: false,
48
55
  url: null,
49
56
  headers: {},
50
57
  timeout: 60,
@@ -54,7 +61,10 @@ if (args.includes("--help") || args.includes("-h") || action === "help") {
54
61
  const helpAction =
55
62
  action === "help"
56
63
  ? args[0]
57
- : action && ["add", "rm", "ls", "enable", "disable", "where", "tui"].includes(action)
64
+ : action &&
65
+ ["add", "rm", "ls", "enable", "disable", "where", "show", "update", "tui"].includes(
66
+ action,
67
+ )
58
68
  ? action
59
69
  : null;
60
70
  printHelp(helpAction);
@@ -142,11 +152,16 @@ for (let i = 0; i < args.length; i += 1) {
142
152
  options.dryRun = true;
143
153
  } else if (arg === "--force") {
144
154
  options.force = true;
155
+ } else if (arg === "--json") {
156
+ options.outputJson = true;
145
157
  }
146
158
  }
147
159
 
148
- if (!action || !["add", "rm", "ls", "enable", "disable", "where", "tui"].includes(action)) {
149
- fail("Provide a command: add | rm | ls | enable | disable | where | tui.");
160
+ if (
161
+ !action ||
162
+ !["add", "rm", "ls", "enable", "disable", "where", "show", "update", "tui"].includes(action)
163
+ ) {
164
+ fail("Provide a command: add | rm | ls | enable | disable | where | show | update | tui.");
150
165
  }
151
166
 
152
167
  let effectiveAction = action;
@@ -183,6 +198,12 @@ if (effectiveAction === "disable") {
183
198
  if (effectiveAction === "where") {
184
199
  options.where = true;
185
200
  }
201
+ if (effectiveAction === "show") {
202
+ options.show = true;
203
+ }
204
+ if (effectiveAction === "update") {
205
+ options.force = true;
206
+ }
186
207
 
187
208
  if (options.remove && effectiveAction !== "rm") {
188
209
  fail("Use the rm command instead of --remove.");
@@ -193,21 +214,32 @@ if (options.list && options.toggle) {
193
214
  if (options.remove && options.toggle) {
194
215
  fail("The rm command does not support --enable/--disable.");
195
216
  }
196
- if (options.allProjects && !options.list && !options.toggle && !options.remove && !options.where) {
197
- fail("--all-projects is only supported for rm/enable/disable/ls/where.");
217
+ if (
218
+ options.allProjects &&
219
+ !options.list &&
220
+ !options.toggle &&
221
+ !options.remove &&
222
+ !options.where &&
223
+ !options.show
224
+ ) {
225
+ fail("--all-projects is only supported for rm/enable/disable/ls/where/show.");
198
226
  }
199
227
  if (options.projectPath && options.allProjects) {
200
228
  fail("Use either --project or --all-projects (not both).");
201
229
  }
202
- if (options.where && (options.list || options.remove || options.toggle)) {
203
- fail("The where command does not support ls/rm/enable/disable flags.");
230
+ if (options.where && (options.list || options.remove || options.toggle || options.show)) {
231
+ fail("The where command does not support ls/rm/enable/disable/show flags.");
204
232
  }
205
- if (options.projectPath && effectiveAction === "add" && options.scope !== "global") {
233
+ if (
234
+ options.projectPath &&
235
+ ["add", "update"].includes(effectiveAction) &&
236
+ options.scope !== "global"
237
+ ) {
206
238
  fail("--project is only supported for Claude global config.");
207
239
  }
208
240
 
209
241
  const requiresConnectionParams =
210
- !options.remove && !options.toggle && !options.list && !options.where;
242
+ !options.remove && !options.toggle && !options.list && !options.where && !options.show;
211
243
 
212
244
  if (requiresConnectionParams && options.transport === "stdio") {
213
245
  if (!options.envPath && !options.mcpDestination && !options.useSessionEnv) {
@@ -251,6 +283,8 @@ for (const client of options.clients) {
251
283
  requireScope("Cline", ["global"], scope);
252
284
  if (options.list) {
253
285
  listJsonConfig(getClinePath(platform, home, appData), "cline");
286
+ } else if (options.show) {
287
+ showJsonConfig(getClinePath(platform, home, appData), "cline", options.name);
254
288
  } else if (options.where) {
255
289
  whereJsonConfig(getClinePath(platform, home, appData), "cline", options.name);
256
290
  } else {
@@ -264,6 +298,8 @@ for (const client of options.clients) {
264
298
  requireScope("Codex", ["global", "local"], scope);
265
299
  if (options.list) {
266
300
  listCodexConfig(getCodexPath(platform, home, userProfile, scope));
301
+ } else if (options.show) {
302
+ showCodexConfig(getCodexPath(platform, home, userProfile, scope), options.name);
267
303
  } else if (options.where) {
268
304
  whereCodexConfig(getCodexPath(platform, home, userProfile, scope), options.name);
269
305
  } else {
@@ -295,6 +331,13 @@ for (const client of options.clients) {
295
331
  options.allProjects,
296
332
  options.projectPath,
297
333
  );
334
+ } else if (options.show) {
335
+ showClaudeConfig(
336
+ getClaudePath(platform, home, appData, claudeToggleScope),
337
+ options.name,
338
+ options.allProjects,
339
+ options.projectPath,
340
+ );
298
341
  } else if (options.where) {
299
342
  whereClaudeConfig(
300
343
  getClaudePath(platform, home, appData, claudeToggleScope),
@@ -315,6 +358,8 @@ for (const client of options.clients) {
315
358
  requireScope("Goose", ["global"], scope);
316
359
  if (options.list) {
317
360
  listGooseConfig(getGoosePath(platform, home, appData));
361
+ } else if (options.show) {
362
+ showGooseConfig(getGoosePath(platform, home, appData), options.name);
318
363
  } else if (options.where) {
319
364
  whereGooseConfig(getGoosePath(platform, home, appData), options.name);
320
365
  } else {
@@ -325,6 +370,8 @@ for (const client of options.clients) {
325
370
  requireScope("OpenCode", ["global", "local"], scope);
326
371
  if (options.list) {
327
372
  listJsonConfig(getOpenCodePath(platform, home, appData, scope), "opencode");
373
+ } else if (options.show) {
374
+ showJsonConfig(getOpenCodePath(platform, home, appData, scope), "opencode", options.name);
328
375
  } else if (options.where) {
329
376
  whereJsonConfig(getOpenCodePath(platform, home, appData, scope), "opencode", options.name);
330
377
  } else {
@@ -344,6 +391,8 @@ for (const client of options.clients) {
344
391
  }
345
392
  if (options.list) {
346
393
  listJsonConfig(getAntigravityPath(home, scope), "antigravity");
394
+ } else if (options.show) {
395
+ showJsonConfig(getAntigravityPath(home, scope), "antigravity", options.name);
347
396
  } else if (options.where) {
348
397
  whereJsonConfig(getAntigravityPath(home, scope), "antigravity", options.name);
349
398
  } else {
@@ -354,6 +403,8 @@ for (const client of options.clients) {
354
403
  requireScope("GitHub Copilot", ["local"], scope);
355
404
  if (options.list) {
356
405
  listJsonConfig(getCopilotPath(), "copilot");
406
+ } else if (options.show) {
407
+ showJsonConfig(getCopilotPath(), "copilot", options.name);
357
408
  } else if (options.where) {
358
409
  whereJsonConfig(getCopilotPath(), "copilot", options.name);
359
410
  } else {
@@ -364,6 +415,8 @@ for (const client of options.clients) {
364
415
  requireScope("Cursor", ["global", "local"], scope);
365
416
  if (options.list) {
366
417
  listJsonConfig(getCursorPath(platform, home, userProfile, scope), "cursor");
418
+ } else if (options.show) {
419
+ showJsonConfig(getCursorPath(platform, home, userProfile, scope), "cursor", options.name);
367
420
  } else if (options.where) {
368
421
  whereJsonConfig(getCursorPath(platform, home, userProfile, scope), "cursor", options.name);
369
422
  } else {
@@ -379,6 +432,8 @@ for (const client of options.clients) {
379
432
  requireScope("Windsurf", ["global"], scope);
380
433
  if (options.list) {
381
434
  listJsonConfig(getWindsurfPath(platform, home, userProfile), "windsurf");
435
+ } else if (options.show) {
436
+ showJsonConfig(getWindsurfPath(platform, home, userProfile), "windsurf", options.name);
382
437
  } else if (options.where) {
383
438
  whereJsonConfig(getWindsurfPath(platform, home, userProfile), "windsurf", options.name);
384
439
  } else {
@@ -1193,6 +1248,246 @@ function whereClaudeConfig(filePath, serverName, allProjects, projectPath) {
1193
1248
  outputWhere(filePath, serverName, Boolean(store[serverName]));
1194
1249
  }
1195
1250
 
1251
+ function showJsonConfig(filePath, clientType, serverName) {
1252
+ const data = readJson(filePath);
1253
+ let store;
1254
+ if (clientType === "opencode") {
1255
+ store = data.mcp || {};
1256
+ } else if (clientType === "antigravity") {
1257
+ store = data.mcpServers || {};
1258
+ } else if (clientType === "copilot") {
1259
+ store = data.servers || {};
1260
+ } else {
1261
+ store = data.mcpServers || {};
1262
+ }
1263
+ const entry = store[serverName];
1264
+ if (!entry) {
1265
+ fail(`Server "${serverName}" not found in ${filePath}.`);
1266
+ }
1267
+ outputShow(filePath, serverName, normalizeServerDetails(clientType, serverName, entry));
1268
+ }
1269
+
1270
+ function showCodexConfig(filePath, serverName) {
1271
+ if (!toml) {
1272
+ fail("TOML dependency not available. Install dependencies and retry.");
1273
+ }
1274
+ const data = readToml(filePath);
1275
+ const store = data.mcp_servers || {};
1276
+ const entry = store[serverName];
1277
+ if (!entry) {
1278
+ fail(`Server "${serverName}" not found in ${filePath}.`);
1279
+ }
1280
+ outputShow(filePath, serverName, normalizeServerDetails("codex", serverName, entry));
1281
+ }
1282
+
1283
+ function showGooseConfig(filePath, serverName) {
1284
+ if (!yaml) {
1285
+ fail("YAML dependency not available. Install dependencies and retry.");
1286
+ }
1287
+ const data = readYaml(filePath);
1288
+ const store = data.extensions || {};
1289
+ const entry = store[serverName];
1290
+ if (!entry) {
1291
+ fail(`Server "${serverName}" not found in ${filePath}.`);
1292
+ }
1293
+ outputShow(filePath, serverName, normalizeServerDetails("goose", serverName, entry));
1294
+ }
1295
+
1296
+ function showClaudeConfig(filePath, serverName, allProjects, projectPath) {
1297
+ const data = readJson(filePath);
1298
+ const isDesktopConfig =
1299
+ filePath.endsWith(".claude.json") || filePath.endsWith("claude_desktop_config.json");
1300
+ if (isDesktopConfig) {
1301
+ const projects = Object.keys(data.projects || {});
1302
+ if (allProjects) {
1303
+ const results = [];
1304
+ for (const key of projects.sort()) {
1305
+ const projectNode = data.projects?.[key];
1306
+ const store = projectNode?.mcpServers || {};
1307
+ if (store[serverName]) {
1308
+ results.push({
1309
+ project: key,
1310
+ ...normalizeServerDetails("claude", serverName, store[serverName]),
1311
+ });
1312
+ }
1313
+ }
1314
+ if (!results.length) {
1315
+ fail(`Server "${serverName}" not found in any Claude projects.`);
1316
+ }
1317
+ if (options.outputJson) {
1318
+ process.stdout.write(`${JSON.stringify(results, null, 2)}\n`);
1319
+ return;
1320
+ }
1321
+ for (const result of results) {
1322
+ outputShow(filePath, serverName, result, result.project);
1323
+ }
1324
+ return;
1325
+ }
1326
+ const projectKey = resolveProjectSelector(data, projectPath);
1327
+ const store = data.projects?.[projectKey]?.mcpServers || {};
1328
+ const entry = store[serverName];
1329
+ if (!entry) {
1330
+ fail(`Server "${serverName}" not found for ${projectKey}.`);
1331
+ }
1332
+ outputShow(
1333
+ filePath,
1334
+ serverName,
1335
+ normalizeServerDetails("claude", serverName, entry, projectKey),
1336
+ projectKey,
1337
+ );
1338
+ return;
1339
+ }
1340
+ const store = data.mcpServers || {};
1341
+ const entry = store[serverName];
1342
+ if (!entry) {
1343
+ fail(`Server "${serverName}" not found in ${filePath}.`);
1344
+ }
1345
+ outputShow(filePath, serverName, normalizeServerDetails("claude", serverName, entry));
1346
+ }
1347
+
1348
+ function normalizeServerDetails(clientType, serverName, entry, projectKey) {
1349
+ const args = Array.isArray(entry?.args) ? entry.args : [];
1350
+ const parsedArgs = parseServerArgs(args);
1351
+ const command = entry?.command || entry?.cmd || "mcp-abap-adt";
1352
+ const normalized = {
1353
+ client: clientType,
1354
+ name: serverName,
1355
+ ...(projectKey ? { project: projectKey } : {}),
1356
+ transport: inferTransport(clientType, entry, parsedArgs),
1357
+ command,
1358
+ timeout: inferTimeout(clientType, entry),
1359
+ url: inferUrl(clientType, entry),
1360
+ headers: inferHeaders(clientType, entry),
1361
+ auth: inferAuth(parsedArgs),
1362
+ };
1363
+ return compactServerDetails(normalized);
1364
+ }
1365
+
1366
+ function compactServerDetails(details) {
1367
+ const compact = {
1368
+ client: details.client,
1369
+ name: details.name,
1370
+ ...(details.project ? { project: details.project } : {}),
1371
+ transport: details.transport,
1372
+ };
1373
+ if (details.command) {
1374
+ compact.command = details.command;
1375
+ }
1376
+ if (Number.isFinite(details.timeout)) {
1377
+ compact.timeout = details.timeout;
1378
+ }
1379
+ if (details.transport === "stdio") {
1380
+ compact.auth = details.auth;
1381
+ } else {
1382
+ if (details.url) {
1383
+ compact.url = details.url;
1384
+ }
1385
+ if (details.headers && Object.keys(details.headers).length > 0) {
1386
+ compact.headers = details.headers;
1387
+ }
1388
+ }
1389
+ return compact;
1390
+ }
1391
+
1392
+ function parseServerArgs(args) {
1393
+ const parsed = {
1394
+ transport: null,
1395
+ envPath: null,
1396
+ mcpDestination: null,
1397
+ useSessionEnv: false,
1398
+ };
1399
+ for (const arg of args) {
1400
+ if (typeof arg !== "string") {
1401
+ continue;
1402
+ }
1403
+ if (arg.startsWith("--transport=")) {
1404
+ const value = arg.slice("--transport=".length);
1405
+ parsed.transport = value === "streamableHttp" ? "http" : value;
1406
+ } else if (arg === "--env") {
1407
+ parsed.useSessionEnv = true;
1408
+ } else if (arg.startsWith("--env-path=") || arg.startsWith("--env=")) {
1409
+ parsed.envPath = arg.slice(arg.indexOf("=") + 1);
1410
+ } else if (arg.startsWith("--mcp=")) {
1411
+ parsed.mcpDestination = arg.slice("--mcp=".length);
1412
+ }
1413
+ }
1414
+ return parsed;
1415
+ }
1416
+
1417
+ function inferTransport(clientType, entry, parsedArgs) {
1418
+ if (parsedArgs.transport) {
1419
+ return parsedArgs.transport;
1420
+ }
1421
+ if (clientType === "goose") {
1422
+ if (entry?.type === "streamable_http") {
1423
+ return "http";
1424
+ }
1425
+ if (entry?.type === "sse") {
1426
+ return "sse";
1427
+ }
1428
+ return "stdio";
1429
+ }
1430
+ if (clientType === "opencode") {
1431
+ return entry?.type === "remote" ? "http" : "stdio";
1432
+ }
1433
+ if (clientType === "copilot") {
1434
+ if (entry?.type === "http") {
1435
+ return "http";
1436
+ }
1437
+ if (entry?.type === "sse") {
1438
+ return "sse";
1439
+ }
1440
+ return "stdio";
1441
+ }
1442
+ if (clientType === "antigravity") {
1443
+ return entry?.type === "http" ? "http" : "stdio";
1444
+ }
1445
+ if (entry?.type === "streamableHttp" || entry?.type === "http") {
1446
+ return "http";
1447
+ }
1448
+ if (entry?.type === "sse") {
1449
+ return "sse";
1450
+ }
1451
+ return "stdio";
1452
+ }
1453
+
1454
+ function inferTimeout(clientType, entry) {
1455
+ if (clientType === "codex") {
1456
+ return entry?.startup_timeout_sec ?? 60;
1457
+ }
1458
+ return entry?.timeout ?? 60;
1459
+ }
1460
+
1461
+ function inferUrl(clientType, entry) {
1462
+ if (clientType === "goose") {
1463
+ return entry?.uri || null;
1464
+ }
1465
+ if (clientType === "antigravity") {
1466
+ return entry?.serverUrl || null;
1467
+ }
1468
+ return entry?.url || null;
1469
+ }
1470
+
1471
+ function inferHeaders(clientType, entry) {
1472
+ if (clientType === "codex") {
1473
+ return entry?.http_headers || {};
1474
+ }
1475
+ return entry?.headers || {};
1476
+ }
1477
+
1478
+ function inferAuth(parsedArgs) {
1479
+ if (parsedArgs.mcpDestination) {
1480
+ return { type: "mcp", value: parsedArgs.mcpDestination };
1481
+ }
1482
+ if (parsedArgs.envPath) {
1483
+ return { type: "env-path", value: parsedArgs.envPath };
1484
+ }
1485
+ if (parsedArgs.useSessionEnv) {
1486
+ return { type: "env", value: null };
1487
+ }
1488
+ return { type: "unknown", value: null };
1489
+ }
1490
+
1196
1491
  function readJson(filePath) {
1197
1492
  if (!fs.existsSync(filePath)) {
1198
1493
  return {};
@@ -1256,6 +1551,16 @@ function outputWhere(filePath, serverName, found, projectKey) {
1256
1551
  process.stdout.write(`- ${serverName}: ${found ? "found" : "not found"}\n`);
1257
1552
  }
1258
1553
 
1554
+ function outputShow(filePath, serverName, details, projectKey) {
1555
+ if (options.outputJson) {
1556
+ process.stdout.write(`${JSON.stringify(details, null, 2)}\n`);
1557
+ return;
1558
+ }
1559
+ const header = projectKey ? `# ${filePath} (${projectKey})` : `# ${filePath}`;
1560
+ process.stdout.write(`${header}\n`);
1561
+ process.stdout.write(`- ${serverName}: ${JSON.stringify(details, null, 2)}\n`);
1562
+ }
1563
+
1259
1564
  function ensureDir(filePath) {
1260
1565
  const dir = path.dirname(filePath);
1261
1566
  if (!fs.existsSync(dir)) {
@@ -1283,7 +1588,7 @@ function printHelp(command) {
1283
1588
  process.stdout.write(`${header}
1284
1589
 
1285
1590
  Usage:
1286
- mcp-conf <add|rm|ls|enable|disable|where> --client <name> [options]
1591
+ mcp-conf <add|rm|ls|enable|disable|where|show|update> --client <name> [options]
1287
1592
  mcp-conf tui
1288
1593
  mcp-conf help <command>
1289
1594
 
@@ -1294,6 +1599,8 @@ Commands:
1294
1599
  enable enable an existing entry
1295
1600
  disable disable an existing entry
1296
1601
  where show where a server name is defined
1602
+ show show server configuration details
1603
+ update update an existing server entry
1297
1604
  tui interactive setup wizard
1298
1605
 
1299
1606
  Run:
@@ -1423,6 +1730,50 @@ Options:
1423
1730
  --all-projects Claude global: search across all projects
1424
1731
  --project <path> Claude global: target a specific project path
1425
1732
 
1733
+ Notes:
1734
+ Antigravity local scope is not supported yet; use --global.
1735
+ `);
1736
+ break;
1737
+ case "show":
1738
+ process.stdout.write(`${header} show
1739
+
1740
+ Usage:
1741
+ mcp-conf show --client <name> --name <serverName> [options]
1742
+
1743
+ Options:
1744
+ --client <name> cline | codex | claude | goose | cursor | windsurf | opencode | kilo | copilot | antigravity (repeatable)
1745
+ --name <serverName> required MCP server name key
1746
+ --global read from global user config (default)
1747
+ --local read from project config (where supported)
1748
+ --all-projects Claude global: show from all projects
1749
+ --project <path> Claude global: target a specific project path
1750
+ --json output JSON only (machine-readable)
1751
+
1752
+ Notes:
1753
+ Antigravity local scope is not supported yet; use --global.
1754
+ `);
1755
+ break;
1756
+ case "update":
1757
+ process.stdout.write(`${header} update
1758
+
1759
+ Usage:
1760
+ mcp-conf update --client <name> --name <serverName> [--env | --env-path <path> | --mcp <dest>] [options]
1761
+
1762
+ Options:
1763
+ --client <name> cline | codex | claude | goose | cursor | windsurf | opencode | kilo | copilot | antigravity (repeatable)
1764
+ --name <serverName> required MCP server name key
1765
+ --env use current shell/session env vars (stdio only)
1766
+ --env-path <path> .env path (stdio only)
1767
+ --mcp <dest> destination name (stdio only)
1768
+ --transport <type> stdio | sse | http (http => streamableHttp)
1769
+ --url <http(s)://...> required for sse/http
1770
+ --header key=value add request header (repeatable)
1771
+ --timeout <seconds> entry timeout (default: 60)
1772
+ --global write to global user config (default)
1773
+ --local write to project config (where supported)
1774
+ --project <path> Claude global: target a specific project path
1775
+ --dry-run print changes without writing files
1776
+
1426
1777
  Notes:
1427
1778
  Antigravity local scope is not supported yet; use --global.
1428
1779
  `);
@@ -1434,9 +1785,10 @@ Usage:
1434
1785
  mcp-conf tui
1435
1786
 
1436
1787
  Description:
1437
- Start interactive setup wizard for add/ls/rm/enable/disable.
1788
+ Start interactive setup wizard for ls/add/show/update/rm/enable/disable.
1438
1789
  Flow: operation -> client -> scope (skips scope when only one is supported).
1439
- For add + sse/http, wizard also asks timeout and repeatable headers.
1790
+ For add/update + sse/http, wizard also asks timeout and repeatable headers.
1791
+ For rm/enable/disable/show/update, wizard selects server from existing entries.
1440
1792
  `);
1441
1793
  break;
1442
1794
  default:
@@ -86,7 +86,7 @@ mcp-conf tui
86
86
  - Controls: arrow keys + Enter, Ctrl+C to cancel.
87
87
 
88
88
  Options:
89
- - Commands: `add`, `rm`, `ls`, `enable`, `disable`, `where`, `tui` (first argument)
89
+ - Commands: `add`, `rm`, `ls`, `enable`, `disable`, `where`, `show`, `update`, `tui` (first argument)
90
90
  - `--client <name>` (repeatable): `cline`, `codex`, `claude`, `goose`, `cursor`, `windsurf`, `opencode` (`kilo` alias), `copilot`, `antigravity`
91
91
  - `--env`: use shell/session environment variables (stdio only)
92
92
  - `--env-path <path>`: use a specific `.env` file (stdio only)
@@ -101,11 +101,12 @@ Options:
101
101
  - `--url <http(s)://...>`: required for `sse` and `http`
102
102
  - `--header key=value`: add request header (repeatable)
103
103
  - `--timeout <seconds>`: timeout value for client entries (default: 60)
104
+ - `--json`: JSON-only output for `show`
104
105
 
105
106
  Notes:
106
107
  - `disable` and `rm` do not require `--env`, `--env-path`, or `--mcp`.
107
108
  - `--env`/`--env-path`/`--mcp` are only valid for `stdio` transport. For `sse/http`, use `--url` and optional `--header`.
108
- - `mcp-conf tui` starts an interactive wizard and writes the same config as `add`.
109
+ - `mcp-conf tui` starts an interactive wizard for `ls`/`add`/`show`/`update`/`rm`/`enable`/`disable`.
109
110
  - Cursor/Copilot enable/disable are not implemented yet.
110
111
  - Antigravity enable/disable uses `disabled: true|false` on the entry.
111
112
  - Antigravity local scope is not supported yet; use `--global`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/configurator",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "MCP client configurator for mcp-abap-adt and mcp-abap-adt-proxy",
5
5
  "license": "MIT",
6
6
  "repository": {