@mcp-abap-adt/configurator 0.0.12 β 0.1.0
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-tui.js +157 -49
- package/bin/mcp-conf.js +126 -37
- package/docs/CLIENT_INSTALLERS.md +5 -4
- package/package.json +1 -1
package/bin/mcp-conf-tui.js
CHANGED
|
@@ -35,26 +35,36 @@ async function main() {
|
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
result.tuiAction = await askSelect("Operation", [
|
|
38
|
-
"ls",
|
|
39
|
-
"show",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"enable",
|
|
44
|
-
"disable",
|
|
38
|
+
{ name: "ls", message: "π ls" },
|
|
39
|
+
{ name: "show", message: "π show" },
|
|
40
|
+
{ name: "sep-view", role: "separator", message: "ββββββββββββ" },
|
|
41
|
+
{ name: "add", message: "β add" },
|
|
42
|
+
{ name: "update", message: "βοΈ update" },
|
|
43
|
+
{ name: "enable", message: "β
enable" },
|
|
44
|
+
{ name: "disable", message: "βΈοΈ disable" },
|
|
45
|
+
{ name: "rm", message: "ποΈ rm" },
|
|
46
|
+
{ name: "sep-exit", role: "separator", message: "ββββββββββββ" },
|
|
47
|
+
{ name: "exit", message: "πͺ exit" },
|
|
45
48
|
]);
|
|
49
|
+
if (result.tuiAction === "exit") {
|
|
50
|
+
emitResult({ tuiAction: "exit" });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
46
53
|
const client = await askSelect(
|
|
47
54
|
"Client",
|
|
48
55
|
CLIENTS.map((item) => item.name),
|
|
49
56
|
CLIENTS.map((item) => item.message),
|
|
50
57
|
);
|
|
51
58
|
result.clients = [client];
|
|
52
|
-
|
|
53
|
-
const scopes = getSupportedScopes(client);
|
|
54
|
-
result.scope = scopes.length === 1 ? scopes[0] : await askSelect("Scope", ["global", "local"]);
|
|
59
|
+
await configureScope(result, client);
|
|
55
60
|
|
|
56
61
|
if (["rm", "enable", "disable", "show", "update"].includes(result.tuiAction)) {
|
|
57
|
-
const serverNames = listExistingServers(
|
|
62
|
+
const serverNames = listExistingServers(
|
|
63
|
+
client,
|
|
64
|
+
result.scope,
|
|
65
|
+
result.allProjects,
|
|
66
|
+
result.projectPath,
|
|
67
|
+
);
|
|
58
68
|
if (serverNames.length === 0) {
|
|
59
69
|
throw new Error(`No existing MCP servers found for ${client} (${result.scope})`);
|
|
60
70
|
}
|
|
@@ -79,32 +89,37 @@ async function main() {
|
|
|
79
89
|
|
|
80
90
|
if (result.transport === "stdio") {
|
|
81
91
|
const authSource = await askSelect("Auth source for stdio", [
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
92
|
+
"destination (--mcp=<name>)",
|
|
93
|
+
"env name (--env=<name>)",
|
|
94
|
+
"env file (--env-path=<name>)",
|
|
85
95
|
]);
|
|
86
|
-
if (authSource.startsWith("
|
|
96
|
+
if (authSource.startsWith("destination")) {
|
|
87
97
|
result.mcpDestination = await askInput("Destination name", "TRIAL");
|
|
88
98
|
result.useSessionEnv = false;
|
|
99
|
+
result.envName = null;
|
|
89
100
|
result.envPath = null;
|
|
90
101
|
result.url = null;
|
|
91
102
|
emitResult(result);
|
|
92
103
|
return;
|
|
93
104
|
}
|
|
94
|
-
if (authSource.startsWith("
|
|
105
|
+
if (authSource.startsWith("env name")) {
|
|
106
|
+
result.envName = await askInput("Env name", "TRIAL");
|
|
107
|
+
result.useSessionEnv = false;
|
|
108
|
+
result.envPath = null;
|
|
109
|
+
result.mcpDestination = null;
|
|
110
|
+
result.url = null;
|
|
111
|
+
emitResult(result);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (authSource.startsWith("env file")) {
|
|
95
115
|
result.envPath = await askInput("Path to .env file");
|
|
96
116
|
result.useSessionEnv = false;
|
|
117
|
+
result.envName = null;
|
|
97
118
|
result.mcpDestination = null;
|
|
98
119
|
result.url = null;
|
|
99
120
|
emitResult(result);
|
|
100
121
|
return;
|
|
101
122
|
}
|
|
102
|
-
result.useSessionEnv = true;
|
|
103
|
-
result.envPath = null;
|
|
104
|
-
result.mcpDestination = null;
|
|
105
|
-
result.url = null;
|
|
106
|
-
emitResult(result);
|
|
107
|
-
return;
|
|
108
123
|
}
|
|
109
124
|
|
|
110
125
|
result.url = await askInput("Server URL (http/https)");
|
|
@@ -118,7 +133,13 @@ async function main() {
|
|
|
118
133
|
}
|
|
119
134
|
|
|
120
135
|
async function configureUpdate(result, client) {
|
|
121
|
-
const current = getServerConfig(
|
|
136
|
+
const current = getServerConfig(
|
|
137
|
+
client,
|
|
138
|
+
result.scope,
|
|
139
|
+
result.name,
|
|
140
|
+
result.allProjects,
|
|
141
|
+
result.projectPath,
|
|
142
|
+
);
|
|
122
143
|
const currentTransport = current.transport || "stdio";
|
|
123
144
|
result.transport = currentTransport;
|
|
124
145
|
result.command = current.command || "mcp-abap-adt";
|
|
@@ -128,42 +149,48 @@ async function configureUpdate(result, client) {
|
|
|
128
149
|
|
|
129
150
|
if (currentTransport === "stdio") {
|
|
130
151
|
const authChoices = [
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
"
|
|
152
|
+
"destination (--mcp=<name>)",
|
|
153
|
+
"env name (--env=<name>)",
|
|
154
|
+
"env file (--env-path=<name>)",
|
|
134
155
|
];
|
|
135
156
|
const authType = current.auth?.type || "unknown";
|
|
136
157
|
const authInitial =
|
|
137
158
|
authType === "mcp" ? 0 : authType === "env" ? 1 : authType === "env-path" ? 2 : 0;
|
|
138
159
|
const authSource = await askSelect("Auth source for stdio", authChoices, null, authInitial);
|
|
139
|
-
if (authSource.startsWith("
|
|
160
|
+
if (authSource.startsWith("destination")) {
|
|
140
161
|
result.mcpDestination = await askInput("Destination name", current.auth?.value || "TRIAL");
|
|
141
162
|
result.useSessionEnv = false;
|
|
163
|
+
result.envName = null;
|
|
142
164
|
result.envPath = null;
|
|
143
165
|
result.url = null;
|
|
144
166
|
result.headers = {};
|
|
145
167
|
return;
|
|
146
168
|
}
|
|
147
|
-
if (authSource.startsWith("
|
|
169
|
+
if (authSource.startsWith("env name")) {
|
|
170
|
+
result.envName = await askInput("Env name", current.auth?.value || "TRIAL");
|
|
171
|
+
result.useSessionEnv = false;
|
|
172
|
+
result.envPath = null;
|
|
173
|
+
result.mcpDestination = null;
|
|
174
|
+
result.url = null;
|
|
175
|
+
result.headers = {};
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (authSource.startsWith("env file")) {
|
|
148
179
|
result.envPath = await askInput("Path to .env file", current.auth?.value || undefined);
|
|
149
180
|
result.useSessionEnv = false;
|
|
181
|
+
result.envName = null;
|
|
150
182
|
result.mcpDestination = null;
|
|
151
183
|
result.url = null;
|
|
152
184
|
result.headers = {};
|
|
153
185
|
return;
|
|
154
186
|
}
|
|
155
|
-
result.useSessionEnv = true;
|
|
156
|
-
result.envPath = null;
|
|
157
|
-
result.mcpDestination = null;
|
|
158
|
-
result.url = null;
|
|
159
|
-
result.headers = {};
|
|
160
|
-
return;
|
|
161
187
|
}
|
|
162
188
|
|
|
163
189
|
result.url = await askInput("Server URL (http/https)", current.url || undefined);
|
|
164
190
|
result.timeout = await askPositiveNumber("Timeout seconds", result.timeout);
|
|
165
191
|
result.headers = await askHeaders(current.headers || {});
|
|
166
192
|
result.useSessionEnv = false;
|
|
193
|
+
result.envName = null;
|
|
167
194
|
result.envPath = null;
|
|
168
195
|
result.mcpDestination = null;
|
|
169
196
|
}
|
|
@@ -173,10 +200,15 @@ function emitResult(result) {
|
|
|
173
200
|
}
|
|
174
201
|
|
|
175
202
|
async function askSelect(message, choices, choiceLabels, initial = 0) {
|
|
176
|
-
const promptChoices = choices.map((value, index) =>
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
203
|
+
const promptChoices = choices.map((value, index) => {
|
|
204
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
205
|
+
return value;
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
name: value,
|
|
209
|
+
message: choiceLabels?.[index] || value,
|
|
210
|
+
};
|
|
211
|
+
});
|
|
180
212
|
const select = new Select({
|
|
181
213
|
name: "value",
|
|
182
214
|
message,
|
|
@@ -239,6 +271,33 @@ function getSupportedScopes(clientName) {
|
|
|
239
271
|
return ["global", "local"];
|
|
240
272
|
}
|
|
241
273
|
|
|
274
|
+
async function configureScope(result, clientName) {
|
|
275
|
+
result.allProjects = false;
|
|
276
|
+
result.projectPath = null;
|
|
277
|
+
|
|
278
|
+
if (clientName === "claude") {
|
|
279
|
+
result.scope = await askSelect("Scope", ["global", "local"]);
|
|
280
|
+
if (result.scope === "local") {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (supportsClaudeGlobalAllProjects(result.tuiAction)) {
|
|
284
|
+
const globalMode = await askSelect("Claude global", [
|
|
285
|
+
{ name: "single", message: "for current project" },
|
|
286
|
+
{ name: "all", message: "for all projects" },
|
|
287
|
+
]);
|
|
288
|
+
result.allProjects = globalMode === "all";
|
|
289
|
+
}
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const scopes = getSupportedScopes(clientName);
|
|
294
|
+
result.scope = scopes.length === 1 ? scopes[0] : await askSelect("Scope", ["global", "local"]);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function supportsClaudeGlobalAllProjects(action) {
|
|
298
|
+
return ["ls", "show", "rm", "enable", "disable"].includes(action);
|
|
299
|
+
}
|
|
300
|
+
|
|
242
301
|
function getSupportedTransports(clientName) {
|
|
243
302
|
if (clientName === "codex") {
|
|
244
303
|
return ["stdio", "http"];
|
|
@@ -246,10 +305,16 @@ function getSupportedTransports(clientName) {
|
|
|
246
305
|
return ["stdio", "sse", "http"];
|
|
247
306
|
}
|
|
248
307
|
|
|
249
|
-
function listExistingServers(clientName, scope) {
|
|
308
|
+
function listExistingServers(clientName, scope, allProjects = false, projectPath = null) {
|
|
250
309
|
const cliPath = path.join(__dirname, "mcp-conf.js");
|
|
251
310
|
const scopeArg = scope === "local" ? "--local" : "--global";
|
|
252
|
-
const
|
|
311
|
+
const cliArgs = [cliPath, "ls", "--client", clientName, scopeArg];
|
|
312
|
+
if (allProjects) {
|
|
313
|
+
cliArgs.push("--all-projects");
|
|
314
|
+
} else if (projectPath) {
|
|
315
|
+
cliArgs.push("--project", projectPath);
|
|
316
|
+
}
|
|
317
|
+
const run = spawnSync(process.execPath, cliArgs, {
|
|
253
318
|
encoding: "utf8",
|
|
254
319
|
});
|
|
255
320
|
if (run.status !== 0) {
|
|
@@ -271,16 +336,28 @@ function listExistingServers(clientName, scope) {
|
|
|
271
336
|
return [...new Set(names)].sort((a, b) => a.localeCompare(b));
|
|
272
337
|
}
|
|
273
338
|
|
|
274
|
-
function getServerConfig(clientName, scope, serverName) {
|
|
339
|
+
function getServerConfig(clientName, scope, serverName, allProjects = false, projectPath = null) {
|
|
275
340
|
const cliPath = path.join(__dirname, "mcp-conf.js");
|
|
276
341
|
const scopeArg = scope === "local" ? "--local" : "--global";
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
342
|
+
const cliArgs = [
|
|
343
|
+
cliPath,
|
|
344
|
+
"show",
|
|
345
|
+
"--client",
|
|
346
|
+
clientName,
|
|
347
|
+
"--name",
|
|
348
|
+
serverName,
|
|
349
|
+
scopeArg,
|
|
350
|
+
"--json",
|
|
351
|
+
"--normalized",
|
|
352
|
+
];
|
|
353
|
+
if (allProjects) {
|
|
354
|
+
cliArgs.push("--all-projects");
|
|
355
|
+
} else if (projectPath) {
|
|
356
|
+
cliArgs.push("--project", projectPath);
|
|
357
|
+
}
|
|
358
|
+
const run = spawnSync(process.execPath, cliArgs, {
|
|
359
|
+
encoding: "utf8",
|
|
360
|
+
});
|
|
284
361
|
if (run.status !== 0) {
|
|
285
362
|
const stderr = String(run.stderr || "").trim();
|
|
286
363
|
throw new Error(stderr || "Failed to read existing server config");
|
|
@@ -293,9 +370,40 @@ function getServerConfig(clientName, scope, serverName) {
|
|
|
293
370
|
}
|
|
294
371
|
|
|
295
372
|
main().catch((error) => {
|
|
296
|
-
if (error
|
|
373
|
+
if (isPromptCancelError(error)) {
|
|
374
|
+
process.exit(0);
|
|
375
|
+
}
|
|
376
|
+
process.stderr.write(`${error?.message || String(error)}\n`);
|
|
377
|
+
process.exit(1);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
process.on("uncaughtException", (error) => {
|
|
381
|
+
if (isPromptCancelError(error)) {
|
|
297
382
|
process.exit(0);
|
|
298
383
|
}
|
|
299
384
|
process.stderr.write(`${error?.message || String(error)}\n`);
|
|
300
385
|
process.exit(1);
|
|
301
386
|
});
|
|
387
|
+
|
|
388
|
+
process.on("unhandledRejection", (error) => {
|
|
389
|
+
if (isPromptCancelError(error)) {
|
|
390
|
+
process.exit(0);
|
|
391
|
+
}
|
|
392
|
+
process.stderr.write(`${error?.message || String(error)}\n`);
|
|
393
|
+
process.exit(1);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
function isPromptCancelError(error) {
|
|
397
|
+
if (error === "" || error === null || error === undefined) {
|
|
398
|
+
return true;
|
|
399
|
+
}
|
|
400
|
+
if (typeof error === "string") {
|
|
401
|
+
return error.toLowerCase().includes("cancel");
|
|
402
|
+
}
|
|
403
|
+
const message = String(error?.message || "");
|
|
404
|
+
return (
|
|
405
|
+
error?.code === "ERR_USE_AFTER_CLOSE" ||
|
|
406
|
+
message.toLowerCase().includes("cancel") ||
|
|
407
|
+
message.toLowerCase().includes("readline was closed")
|
|
408
|
+
);
|
|
409
|
+
}
|
package/bin/mcp-conf.js
CHANGED
|
@@ -34,6 +34,7 @@ if (
|
|
|
34
34
|
}
|
|
35
35
|
const options = {
|
|
36
36
|
clients: [],
|
|
37
|
+
envName: null,
|
|
37
38
|
envPath: null,
|
|
38
39
|
useSessionEnv: false,
|
|
39
40
|
mcpDestination: null,
|
|
@@ -52,6 +53,7 @@ const options = {
|
|
|
52
53
|
where: false,
|
|
53
54
|
show: false,
|
|
54
55
|
outputJson: false,
|
|
56
|
+
outputNormalized: false,
|
|
55
57
|
url: null,
|
|
56
58
|
headers: {},
|
|
57
59
|
timeout: 60,
|
|
@@ -76,30 +78,45 @@ for (let i = 0; i < args.length; i += 1) {
|
|
|
76
78
|
if (arg === "--client") {
|
|
77
79
|
options.clients.push(normalizeClientName(args[i + 1]));
|
|
78
80
|
i += 1;
|
|
81
|
+
} else if (arg.startsWith("--env=")) {
|
|
82
|
+
options.envName = arg.slice("--env=".length);
|
|
83
|
+
options.useSessionEnv = false;
|
|
84
|
+
options.envPath = null;
|
|
85
|
+
options.mcpDestination = null;
|
|
79
86
|
} else if (arg === "--env") {
|
|
80
87
|
const maybePath = args[i + 1];
|
|
81
88
|
if (maybePath && !maybePath.startsWith("-")) {
|
|
82
|
-
|
|
83
|
-
|
|
89
|
+
if (looksLikeEnvPath(maybePath)) {
|
|
90
|
+
// Backward-compatible form: --env /path/to/.env
|
|
91
|
+
options.envPath = maybePath;
|
|
92
|
+
options.envName = null;
|
|
93
|
+
} else {
|
|
94
|
+
options.envName = maybePath;
|
|
95
|
+
options.envPath = null;
|
|
96
|
+
}
|
|
84
97
|
options.useSessionEnv = false;
|
|
85
98
|
options.mcpDestination = null;
|
|
86
99
|
i += 1;
|
|
87
100
|
} else {
|
|
88
101
|
options.useSessionEnv = true;
|
|
102
|
+
options.envName = null;
|
|
89
103
|
options.envPath = null;
|
|
90
104
|
options.mcpDestination = null;
|
|
91
105
|
}
|
|
92
106
|
} else if (arg === "--env-path") {
|
|
93
107
|
options.envPath = args[i + 1];
|
|
108
|
+
options.envName = null;
|
|
94
109
|
options.useSessionEnv = false;
|
|
95
110
|
options.mcpDestination = null;
|
|
96
111
|
i += 1;
|
|
97
112
|
} else if (arg === "--session-env") {
|
|
98
113
|
options.useSessionEnv = true;
|
|
114
|
+
options.envName = null;
|
|
99
115
|
options.envPath = null;
|
|
100
116
|
options.mcpDestination = null;
|
|
101
117
|
} else if (arg === "--mcp") {
|
|
102
118
|
options.mcpDestination = args[i + 1];
|
|
119
|
+
options.envName = null;
|
|
103
120
|
options.useSessionEnv = false;
|
|
104
121
|
options.envPath = null;
|
|
105
122
|
i += 1;
|
|
@@ -154,6 +171,8 @@ for (let i = 0; i < args.length; i += 1) {
|
|
|
154
171
|
options.force = true;
|
|
155
172
|
} else if (arg === "--json") {
|
|
156
173
|
options.outputJson = true;
|
|
174
|
+
} else if (arg === "--normalized") {
|
|
175
|
+
options.outputNormalized = true;
|
|
157
176
|
}
|
|
158
177
|
}
|
|
159
178
|
|
|
@@ -169,6 +188,9 @@ if (action === "tui") {
|
|
|
169
188
|
runTuiWizard(options);
|
|
170
189
|
effectiveAction = options.tuiAction || "add";
|
|
171
190
|
}
|
|
191
|
+
if (effectiveAction === "exit") {
|
|
192
|
+
process.exit(0);
|
|
193
|
+
}
|
|
172
194
|
|
|
173
195
|
if (options.clients.length === 0) {
|
|
174
196
|
fail("Provide at least one --client.");
|
|
@@ -242,8 +264,8 @@ const requiresConnectionParams =
|
|
|
242
264
|
!options.remove && !options.toggle && !options.list && !options.where && !options.show;
|
|
243
265
|
|
|
244
266
|
if (requiresConnectionParams && options.transport === "stdio") {
|
|
245
|
-
if (!options.envPath && !options.mcpDestination && !options.useSessionEnv) {
|
|
246
|
-
fail("Provide --env
|
|
267
|
+
if (!options.envName && !options.envPath && !options.mcpDestination && !options.useSessionEnv) {
|
|
268
|
+
fail("Provide --env <name>, --env-path <path>, --session-env, or --mcp <destination>.");
|
|
247
269
|
}
|
|
248
270
|
}
|
|
249
271
|
|
|
@@ -251,8 +273,8 @@ if (requiresConnectionParams && options.transport !== "stdio") {
|
|
|
251
273
|
if (!options.url) {
|
|
252
274
|
fail("Provide --url <http(s)://...> for sse/http transports.");
|
|
253
275
|
}
|
|
254
|
-
if (options.envPath || options.mcpDestination || options.useSessionEnv) {
|
|
255
|
-
fail("--env/--env-path/--mcp are only valid for stdio transport.");
|
|
276
|
+
if (options.envName || options.envPath || options.mcpDestination || options.useSessionEnv) {
|
|
277
|
+
fail("--env/--env-path/--session-env/--mcp are only valid for stdio transport.");
|
|
256
278
|
}
|
|
257
279
|
}
|
|
258
280
|
|
|
@@ -263,13 +285,15 @@ const userProfile = process.env.USERPROFILE || home;
|
|
|
263
285
|
|
|
264
286
|
const serverArgsRaw = [
|
|
265
287
|
`--transport=${options.transport}`,
|
|
266
|
-
options.
|
|
267
|
-
?
|
|
268
|
-
: options.
|
|
269
|
-
?
|
|
270
|
-
: options.
|
|
271
|
-
? `--
|
|
272
|
-
:
|
|
288
|
+
options.envName
|
|
289
|
+
? `--env=${options.envName}`
|
|
290
|
+
: options.useSessionEnv
|
|
291
|
+
? "--session-env"
|
|
292
|
+
: options.envPath
|
|
293
|
+
? `--env-path=${options.envPath}`
|
|
294
|
+
: options.mcpDestination
|
|
295
|
+
? `--mcp=${options.mcpDestination.toLowerCase()}`
|
|
296
|
+
: undefined,
|
|
273
297
|
];
|
|
274
298
|
const serverArgs = serverArgsRaw.filter(Boolean);
|
|
275
299
|
|
|
@@ -489,7 +513,8 @@ function runTuiWizard(opts) {
|
|
|
489
513
|
const rawPayload = run.output?.[3] || "";
|
|
490
514
|
const payload = String(rawPayload).trim();
|
|
491
515
|
if (!payload) {
|
|
492
|
-
|
|
516
|
+
opts.tuiAction = "exit";
|
|
517
|
+
return;
|
|
493
518
|
}
|
|
494
519
|
let selected;
|
|
495
520
|
try {
|
|
@@ -1314,7 +1339,10 @@ function showJsonConfig(filePath, clientType, serverName) {
|
|
|
1314
1339
|
if (!entry) {
|
|
1315
1340
|
fail(`Server "${serverName}" not found in ${filePath}.`);
|
|
1316
1341
|
}
|
|
1317
|
-
|
|
1342
|
+
const details = options.outputNormalized
|
|
1343
|
+
? normalizeServerDetails(clientType, serverName, entry)
|
|
1344
|
+
: cloneJsonLike(entry);
|
|
1345
|
+
outputShow(filePath, serverName, details);
|
|
1318
1346
|
}
|
|
1319
1347
|
|
|
1320
1348
|
function showCodexConfig(filePath, serverName) {
|
|
@@ -1327,7 +1355,10 @@ function showCodexConfig(filePath, serverName) {
|
|
|
1327
1355
|
if (!entry) {
|
|
1328
1356
|
fail(`Server "${serverName}" not found in ${filePath}.`);
|
|
1329
1357
|
}
|
|
1330
|
-
|
|
1358
|
+
const details = options.outputNormalized
|
|
1359
|
+
? normalizeServerDetails("codex", serverName, entry)
|
|
1360
|
+
: cloneJsonLike(entry);
|
|
1361
|
+
outputShow(filePath, serverName, details);
|
|
1331
1362
|
}
|
|
1332
1363
|
|
|
1333
1364
|
function showGooseConfig(filePath, serverName) {
|
|
@@ -1340,7 +1371,10 @@ function showGooseConfig(filePath, serverName) {
|
|
|
1340
1371
|
if (!entry) {
|
|
1341
1372
|
fail(`Server "${serverName}" not found in ${filePath}.`);
|
|
1342
1373
|
}
|
|
1343
|
-
|
|
1374
|
+
const details = options.outputNormalized
|
|
1375
|
+
? normalizeServerDetails("goose", serverName, entry)
|
|
1376
|
+
: cloneJsonLike(entry);
|
|
1377
|
+
outputShow(filePath, serverName, details);
|
|
1344
1378
|
}
|
|
1345
1379
|
|
|
1346
1380
|
function showClaudeConfig(filePath, serverName, allProjects, projectPath) {
|
|
@@ -1355,10 +1389,17 @@ function showClaudeConfig(filePath, serverName, allProjects, projectPath) {
|
|
|
1355
1389
|
const projectNode = data.projects?.[key];
|
|
1356
1390
|
const store = projectNode?.mcpServers || {};
|
|
1357
1391
|
if (store[serverName]) {
|
|
1358
|
-
results.push(
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1392
|
+
results.push(
|
|
1393
|
+
options.outputNormalized
|
|
1394
|
+
? {
|
|
1395
|
+
project: key,
|
|
1396
|
+
...normalizeServerDetails("claude", serverName, store[serverName]),
|
|
1397
|
+
}
|
|
1398
|
+
: {
|
|
1399
|
+
project: key,
|
|
1400
|
+
raw: cloneJsonLike(store[serverName]),
|
|
1401
|
+
},
|
|
1402
|
+
);
|
|
1362
1403
|
}
|
|
1363
1404
|
}
|
|
1364
1405
|
if (!results.length) {
|
|
@@ -1369,7 +1410,12 @@ function showClaudeConfig(filePath, serverName, allProjects, projectPath) {
|
|
|
1369
1410
|
return;
|
|
1370
1411
|
}
|
|
1371
1412
|
for (const result of results) {
|
|
1372
|
-
outputShow(
|
|
1413
|
+
outputShow(
|
|
1414
|
+
filePath,
|
|
1415
|
+
serverName,
|
|
1416
|
+
options.outputNormalized ? result : result.raw,
|
|
1417
|
+
result.project,
|
|
1418
|
+
);
|
|
1373
1419
|
}
|
|
1374
1420
|
return;
|
|
1375
1421
|
}
|
|
@@ -1379,12 +1425,10 @@ function showClaudeConfig(filePath, serverName, allProjects, projectPath) {
|
|
|
1379
1425
|
if (!entry) {
|
|
1380
1426
|
fail(`Server "${serverName}" not found for ${projectKey}.`);
|
|
1381
1427
|
}
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
projectKey,
|
|
1387
|
-
);
|
|
1428
|
+
const details = options.outputNormalized
|
|
1429
|
+
? normalizeServerDetails("claude", serverName, entry, projectKey)
|
|
1430
|
+
: cloneJsonLike(entry);
|
|
1431
|
+
outputShow(filePath, serverName, details, projectKey);
|
|
1388
1432
|
return;
|
|
1389
1433
|
}
|
|
1390
1434
|
const store = data.mcpServers || {};
|
|
@@ -1392,7 +1436,10 @@ function showClaudeConfig(filePath, serverName, allProjects, projectPath) {
|
|
|
1392
1436
|
if (!entry) {
|
|
1393
1437
|
fail(`Server "${serverName}" not found in ${filePath}.`);
|
|
1394
1438
|
}
|
|
1395
|
-
|
|
1439
|
+
const details = options.outputNormalized
|
|
1440
|
+
? normalizeServerDetails("claude", serverName, entry)
|
|
1441
|
+
: cloneJsonLike(entry);
|
|
1442
|
+
outputShow(filePath, serverName, details);
|
|
1396
1443
|
}
|
|
1397
1444
|
|
|
1398
1445
|
function normalizeServerDetails(clientType, serverName, entry, projectKey) {
|
|
@@ -1439,9 +1486,21 @@ function compactServerDetails(details) {
|
|
|
1439
1486
|
return compact;
|
|
1440
1487
|
}
|
|
1441
1488
|
|
|
1489
|
+
function cloneJsonLike(value) {
|
|
1490
|
+
if (value === null || value === undefined) {
|
|
1491
|
+
return value;
|
|
1492
|
+
}
|
|
1493
|
+
try {
|
|
1494
|
+
return JSON.parse(JSON.stringify(value));
|
|
1495
|
+
} catch {
|
|
1496
|
+
return value;
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1442
1500
|
function parseServerArgs(args) {
|
|
1443
1501
|
const parsed = {
|
|
1444
1502
|
transport: null,
|
|
1503
|
+
envName: null,
|
|
1445
1504
|
envPath: null,
|
|
1446
1505
|
mcpDestination: null,
|
|
1447
1506
|
useSessionEnv: false,
|
|
@@ -1456,17 +1515,28 @@ function parseServerArgs(args) {
|
|
|
1456
1515
|
parsed.transport = value === "streamableHttp" ? "http" : value;
|
|
1457
1516
|
} else if (arg === "--session-env") {
|
|
1458
1517
|
parsed.useSessionEnv = true;
|
|
1518
|
+
} else if (arg.startsWith("--env=")) {
|
|
1519
|
+
const value = arg.slice("--env=".length);
|
|
1520
|
+
if (looksLikeEnvPath(value)) {
|
|
1521
|
+
parsed.envPath = value;
|
|
1522
|
+
} else {
|
|
1523
|
+
parsed.envName = value;
|
|
1524
|
+
}
|
|
1459
1525
|
} else if (arg === "--env") {
|
|
1460
1526
|
const next = args[i + 1];
|
|
1461
1527
|
if (typeof next === "string" && next && !next.startsWith("-")) {
|
|
1462
|
-
|
|
1463
|
-
|
|
1528
|
+
if (looksLikeEnvPath(next)) {
|
|
1529
|
+
// Backward-compatible form: --env /path/to/.env
|
|
1530
|
+
parsed.envPath = next;
|
|
1531
|
+
} else {
|
|
1532
|
+
parsed.envName = next;
|
|
1533
|
+
}
|
|
1464
1534
|
parsed.useSessionEnv = false;
|
|
1465
1535
|
i += 1;
|
|
1466
1536
|
} else {
|
|
1467
1537
|
parsed.useSessionEnv = true;
|
|
1468
1538
|
}
|
|
1469
|
-
} else if (arg.startsWith("--env-path=")
|
|
1539
|
+
} else if (arg.startsWith("--env-path=")) {
|
|
1470
1540
|
parsed.envPath = arg.slice(arg.indexOf("=") + 1);
|
|
1471
1541
|
} else if (arg === "--env-path") {
|
|
1472
1542
|
const next = args[i + 1];
|
|
@@ -1557,15 +1627,31 @@ function inferAuth(parsedArgs) {
|
|
|
1557
1627
|
if (parsedArgs.mcpDestination) {
|
|
1558
1628
|
return { type: "mcp", value: parsedArgs.mcpDestination };
|
|
1559
1629
|
}
|
|
1630
|
+
if (parsedArgs.envName) {
|
|
1631
|
+
return { type: "env", value: parsedArgs.envName };
|
|
1632
|
+
}
|
|
1560
1633
|
if (parsedArgs.envPath) {
|
|
1561
1634
|
return { type: "env-path", value: parsedArgs.envPath };
|
|
1562
1635
|
}
|
|
1563
1636
|
if (parsedArgs.useSessionEnv) {
|
|
1564
|
-
return { type: "env", value: null };
|
|
1637
|
+
return { type: "session-env", value: null };
|
|
1565
1638
|
}
|
|
1566
1639
|
return { type: "unknown", value: null };
|
|
1567
1640
|
}
|
|
1568
1641
|
|
|
1642
|
+
function looksLikeEnvPath(value) {
|
|
1643
|
+
if (!value || typeof value !== "string") {
|
|
1644
|
+
return false;
|
|
1645
|
+
}
|
|
1646
|
+
return (
|
|
1647
|
+
value.includes("/") ||
|
|
1648
|
+
value.includes("\\") ||
|
|
1649
|
+
value.endsWith(".env") ||
|
|
1650
|
+
value.startsWith(".") ||
|
|
1651
|
+
value.startsWith("~")
|
|
1652
|
+
);
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1569
1655
|
function readJson(filePath) {
|
|
1570
1656
|
if (!fs.existsSync(filePath)) {
|
|
1571
1657
|
return {};
|
|
@@ -1697,13 +1783,14 @@ Notes:
|
|
|
1697
1783
|
process.stdout.write(`${header} add
|
|
1698
1784
|
|
|
1699
1785
|
Usage:
|
|
1700
|
-
mcp-conf add --client <name> --name <serverName> [--env | --env-path <path> | --mcp <dest>] [options]
|
|
1786
|
+
mcp-conf add --client <name> --name <serverName> [--env <name> | --env-path <path> | --session-env | --mcp <dest>] [options]
|
|
1701
1787
|
|
|
1702
1788
|
Options:
|
|
1703
1789
|
--client <name> cline | codex | claude | goose | cursor | windsurf | opencode | kilo | copilot | antigravity | crush (repeatable)
|
|
1704
1790
|
--name <serverName> required MCP server name key
|
|
1705
|
-
--env
|
|
1791
|
+
--env <name> env profile name (stdio only), writes --env=<name>
|
|
1706
1792
|
--env-path <path> .env path (stdio only)
|
|
1793
|
+
--session-env use current shell/session env vars (stdio only)
|
|
1707
1794
|
--mcp <dest> destination name (stdio only)
|
|
1708
1795
|
--transport <type> stdio | sse | http (http => streamableHttp)
|
|
1709
1796
|
--command <bin> command to run (default: mcp-abap-adt)
|
|
@@ -1826,6 +1913,7 @@ Options:
|
|
|
1826
1913
|
--all-projects Claude global: show from all projects
|
|
1827
1914
|
--project <path> Claude global: target a specific project path
|
|
1828
1915
|
--json output JSON only (machine-readable)
|
|
1916
|
+
--normalized output normalized view (for tooling); default is raw config entry
|
|
1829
1917
|
|
|
1830
1918
|
Notes:
|
|
1831
1919
|
Antigravity local scope is not supported yet; use --global.
|
|
@@ -1835,13 +1923,14 @@ Notes:
|
|
|
1835
1923
|
process.stdout.write(`${header} update
|
|
1836
1924
|
|
|
1837
1925
|
Usage:
|
|
1838
|
-
mcp-conf update --client <name> --name <serverName> [--env | --env-path <path> | --mcp <dest>] [options]
|
|
1926
|
+
mcp-conf update --client <name> --name <serverName> [--env <name> | --env-path <path> | --session-env | --mcp <dest>] [options]
|
|
1839
1927
|
|
|
1840
1928
|
Options:
|
|
1841
1929
|
--client <name> cline | codex | claude | goose | cursor | windsurf | opencode | kilo | copilot | antigravity | crush (repeatable)
|
|
1842
1930
|
--name <serverName> required MCP server name key
|
|
1843
|
-
--env
|
|
1931
|
+
--env <name> env profile name (stdio only), writes --env=<name>
|
|
1844
1932
|
--env-path <path> .env path (stdio only)
|
|
1933
|
+
--session-env use current shell/session env vars (stdio only)
|
|
1845
1934
|
--mcp <dest> destination name (stdio only)
|
|
1846
1935
|
--transport <type> stdio | sse | http (http => streamableHttp)
|
|
1847
1936
|
--url <http(s)://...> required for sse/http
|
|
@@ -94,8 +94,9 @@ mcp-conf tui
|
|
|
94
94
|
Options:
|
|
95
95
|
- Commands: `add`, `rm`, `ls`, `show`, `enable`, `disable`, `where`, `update`, `tui` (first argument)
|
|
96
96
|
- `--client <name>` (repeatable): `cline`, `codex`, `claude`, `goose`, `cursor`, `windsurf`, `opencode` (`kilo` alias), `copilot`, `antigravity`, `crush`
|
|
97
|
-
- `--env
|
|
97
|
+
- `--env <name>`: use named env profile; writes `--env=<name>` (stdio only)
|
|
98
98
|
- `--env-path <path>`: use a specific `.env` file (stdio only)
|
|
99
|
+
- `--session-env`: use shell/session environment variables (stdio only)
|
|
99
100
|
- `--mcp <destination>`: use service key destination
|
|
100
101
|
- `--name <serverName>`: MCP server name (required)
|
|
101
102
|
- `--transport <type>`: `stdio`, `sse`, or `http` (`http` maps to `streamableHttp`)
|
|
@@ -110,8 +111,8 @@ Options:
|
|
|
110
111
|
- `--json`: JSON-only output for `show`
|
|
111
112
|
|
|
112
113
|
Notes:
|
|
113
|
-
- `disable` and `rm` do not require `--env`, `--env-path`, or `--mcp`.
|
|
114
|
-
- `--env`/`--env-path`/`--mcp` are only valid for `stdio` transport. For `sse/http`, use `--url` and optional `--header`.
|
|
114
|
+
- `disable` and `rm` do not require `--env`, `--env-path`, `--session-env`, or `--mcp`.
|
|
115
|
+
- `--env`/`--env-path`/`--session-env`/`--mcp` are only valid for `stdio` transport. For `sse/http`, use `--url` and optional `--header`.
|
|
115
116
|
- `mcp-conf tui` starts an interactive wizard for `ls`/`show`/`add`/`update`/`rm`/`enable`/`disable`.
|
|
116
117
|
- Cursor/Copilot enable/disable are not implemented yet.
|
|
117
118
|
- Antigravity enable/disable uses `disabled: true|false` on the entry.
|
|
@@ -121,7 +122,7 @@ Notes:
|
|
|
121
122
|
- Antigravity HTTP entries use `serverUrl` instead of `url`.
|
|
122
123
|
- New entries for Cline, Codex, Windsurf, Goose, Claude, OpenCode, and Crush are added **disabled by default**. Use `enable` to turn them on.
|
|
123
124
|
- Windsurf follows `disabled` like Cline. The configurator sets `disabled = true` for default-disabled entries.
|
|
124
|
-
- `enable`/`disable` only work if the server entry already exists. Use add commands with `--env`, `--env-path`, or `--mcp` first.
|
|
125
|
+
- `enable`/`disable` only work if the server entry already exists. Use add commands with `--env`, `--env-path`, `--session-env`, or `--mcp` first.
|
|
125
126
|
- Non-stdio transports are supported for Cline/Cursor/Windsurf/Claude/Goose. Codex supports `http` (streamable HTTP) but not `sse`.
|
|
126
127
|
- Codex writes custom headers under `http_headers` in `~/.codex/config.toml` (or `./.codex/config.toml` for `--local`).
|
|
127
128
|
- Codex HTTP entries include `startup_timeout_sec` (default: 60).
|