@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 +1 -1
- package/bin/mcp-conf-tui.js +90 -6
- package/bin/mcp-conf.js +365 -13
- package/docs/CLIENT_INSTALLERS.md +3 -2
- package/package.json +1 -1
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.
|
package/bin/mcp-conf-tui.js
CHANGED
|
@@ -33,7 +33,15 @@ async function main() {
|
|
|
33
33
|
timeout: 60,
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
result.tuiAction = await askSelect("Operation", [
|
|
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 (
|
|
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 &&
|
|
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 (
|
|
149
|
-
|
|
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 (
|
|
197
|
-
|
|
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 (
|
|
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/
|
|
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
|
|
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`.
|