@mcp-abap-adt/configurator 0.0.4 → 0.0.6

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
@@ -11,17 +11,25 @@ npm install -g @mcp-abap-adt/configurator
11
11
  ## Usage
12
12
 
13
13
  ```bash
14
- mcp-conf --client cline --env /path/to/.env --name abap
14
+ mcp-conf --client cline --env-path /path/to/.env --name abap
15
15
  mcp-conf --client cline --mcp TRIAL --name abap
16
16
  mcp-conf --client cline --name direct-jwt-test-001 --transport http --url http://localhost:4004/mcp/stream/http --header x-sap-url=https://... --header x-sap-client=210 --header x-sap-auth-type=jwt --header x-sap-jwt-token=...
17
17
  mcp-conf --client cline --name local-mcp-sse --transport sse --url http://localhost:3001/sse
18
18
  mcp-conf --client codex --name abap-http --transport http --url http://localhost:3000/mcp/stream/http
19
19
  mcp-conf --client codex --name abap-http --transport http --url http://localhost:3000/mcp/stream/http --header x-mcp-destination=trial
20
20
  mcp-conf --client opencode --name abap --transport http --url http://localhost:3000/mcp/stream/http
21
+ mcp-conf --client kilo --name abap --transport http --url http://localhost:3000/mcp/stream/http
21
22
  mcp-conf --client copilot --name abap --transport http --url http://localhost:3000/mcp/stream/http --header x-mcp-destination=trial
23
+ mcp-conf tui
22
24
  ```
23
25
 
26
+ ## TUI
27
+
28
+ `mcp-conf tui` starts an interactive wizard.
29
+ - Step order: `operation` -> `client` -> `scope` (auto-skipped if only one scope is supported).
30
+ - For `add` + `sse/http`: prompts for URL, timeout, and repeatable headers.
31
+ - Keyboard: arrow keys + Enter, Ctrl+C to cancel.
32
+
24
33
  ## Docs
25
34
 
26
35
  - See `docs/CLIENT_INSTALLERS.md` for client-specific behavior, defaults, and config paths.
27
-
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { Select, Input, Confirm } = require("enquirer");
4
+ const fs = require("node:fs");
5
+
6
+ const CLIENTS = [
7
+ { name: "cline", message: "Cline" },
8
+ { name: "codex", message: "Codex" },
9
+ { name: "claude", message: "Claude" },
10
+ { name: "goose", message: "Goose" },
11
+ { name: "cursor", message: "Cursor" },
12
+ { name: "windsurf", message: "Windsurf" },
13
+ { name: "opencode", message: "OpenCode (kilo)" },
14
+ { name: "copilot", message: "GitHub Copilot" },
15
+ { name: "antigravity", message: "Antigravity" },
16
+ ];
17
+
18
+ const HEADER_KEYS = [
19
+ "x-mcp-destination",
20
+ "x-sap-url",
21
+ "x-sap-client",
22
+ "x-sap-auth-type",
23
+ "x-sap-jwt-token",
24
+ "x-sap-user",
25
+ "x-sap-password",
26
+ ];
27
+
28
+ async function main() {
29
+ const result = {
30
+ headers: {},
31
+ timeout: 60,
32
+ };
33
+
34
+ result.tuiAction = await askSelect("Operation", ["ls", "add", "rm", "enable", "disable"]);
35
+ const client = await askSelect(
36
+ "Client",
37
+ CLIENTS.map((item) => item.name),
38
+ CLIENTS.map((item) => item.message),
39
+ );
40
+ result.clients = [client];
41
+
42
+ const scopes = getSupportedScopes(client);
43
+ result.scope = scopes.length === 1 ? scopes[0] : await askSelect("Scope", ["global", "local"]);
44
+
45
+ if (result.tuiAction !== "ls") {
46
+ result.name = await askInput("Server name", "abap");
47
+ } else {
48
+ result.name = null;
49
+ }
50
+
51
+ if (result.tuiAction !== "add") {
52
+ emitResult(result);
53
+ return;
54
+ }
55
+
56
+ const transports = getSupportedTransports(client);
57
+ result.transport =
58
+ transports.length === 1 ? transports[0] : await askSelect("Transport", transports);
59
+
60
+ if (result.transport === "stdio") {
61
+ const authSource = await askSelect("Auth source for stdio", [
62
+ "service key destination (--mcp)",
63
+ "session environment (--env)",
64
+ "specific env file (--env-path)",
65
+ ]);
66
+ if (authSource.startsWith("service key")) {
67
+ result.mcpDestination = await askInput("Destination name", "TRIAL");
68
+ result.useSessionEnv = false;
69
+ result.envPath = null;
70
+ result.url = null;
71
+ emitResult(result);
72
+ return;
73
+ }
74
+ if (authSource.startsWith("specific env file")) {
75
+ result.envPath = await askInput("Path to .env file");
76
+ result.useSessionEnv = false;
77
+ result.mcpDestination = null;
78
+ result.url = null;
79
+ emitResult(result);
80
+ return;
81
+ }
82
+ result.useSessionEnv = true;
83
+ result.envPath = null;
84
+ result.mcpDestination = null;
85
+ result.url = null;
86
+ emitResult(result);
87
+ return;
88
+ }
89
+
90
+ result.url = await askInput("Server URL (http/https)");
91
+ result.timeout = await askPositiveNumber("Timeout seconds", 60);
92
+ result.headers = await askHeaders();
93
+ result.useSessionEnv = false;
94
+ result.envPath = null;
95
+ result.mcpDestination = null;
96
+
97
+ emitResult(result);
98
+ }
99
+
100
+ function emitResult(result) {
101
+ fs.writeFileSync(3, JSON.stringify(result), "utf8");
102
+ }
103
+
104
+ async function askSelect(message, choices, choiceLabels) {
105
+ const promptChoices = choices.map((value, index) => ({
106
+ name: value,
107
+ message: choiceLabels?.[index] || value,
108
+ }));
109
+ const select = new Select({
110
+ name: "value",
111
+ message,
112
+ choices: promptChoices,
113
+ footer: "Use arrow keys + Enter. Ctrl+C to cancel.",
114
+ });
115
+ return select.run();
116
+ }
117
+
118
+ async function askInput(message, initial) {
119
+ const input = new Input({
120
+ name: "value",
121
+ message,
122
+ initial,
123
+ });
124
+ const value = await input.run();
125
+ if (!String(value || "").trim()) {
126
+ return askInput(message, initial);
127
+ }
128
+ return String(value).trim();
129
+ }
130
+
131
+ async function askPositiveNumber(message, initialValue) {
132
+ while (true) {
133
+ const raw = await askInput(message, String(initialValue));
134
+ const parsed = Number(raw);
135
+ if (Number.isFinite(parsed) && parsed > 0) {
136
+ return parsed;
137
+ }
138
+ }
139
+ }
140
+
141
+ async function askHeaders() {
142
+ const headers = {};
143
+ while (true) {
144
+ const key = await askSelect("Header key", [...HEADER_KEYS, "done"]);
145
+ if (key === "done") {
146
+ return headers;
147
+ }
148
+ headers[key] = await askInput(`Value for ${key}`);
149
+ const addMore = await new Confirm({
150
+ name: "value",
151
+ message: "Add another header?",
152
+ initial: true,
153
+ }).run();
154
+ if (!addMore) {
155
+ return headers;
156
+ }
157
+ }
158
+ }
159
+
160
+ function getSupportedScopes(clientName) {
161
+ if (clientName === "copilot") {
162
+ return ["local"];
163
+ }
164
+ if (["cline", "goose", "windsurf", "antigravity"].includes(clientName)) {
165
+ return ["global"];
166
+ }
167
+ return ["global", "local"];
168
+ }
169
+
170
+ function getSupportedTransports(clientName) {
171
+ if (clientName === "codex") {
172
+ return ["stdio", "http"];
173
+ }
174
+ return ["stdio", "sse", "http"];
175
+ }
176
+
177
+ main().catch((error) => {
178
+ if (error === "") {
179
+ process.exit(0);
180
+ }
181
+ process.stderr.write(`${error?.message || String(error)}\n`);
182
+ process.exit(1);
183
+ });
package/bin/mcp-conf.js CHANGED
@@ -3,6 +3,7 @@
3
3
  const fs = require("node:fs");
4
4
  const path = require("node:path");
5
5
  const os = require("node:os");
6
+ const { spawnSync } = require("node:child_process");
6
7
 
7
8
  let yaml;
8
9
  try {
@@ -23,12 +24,13 @@ try {
23
24
 
24
25
  const args = process.argv.slice(2);
25
26
  const action = args[0] && !args[0].startsWith("-") ? args[0] : null;
26
- if (action && ["add", "rm", "ls", "enable", "disable", "where", "help"].includes(action)) {
27
+ if (action && ["add", "rm", "ls", "enable", "disable", "where", "tui", "help"].includes(action)) {
27
28
  args.shift();
28
29
  }
29
30
  const options = {
30
31
  clients: [],
31
32
  envPath: null,
33
+ useSessionEnv: false,
32
34
  mcpDestination: null,
33
35
  name: null,
34
36
  transport: "stdio",
@@ -52,7 +54,7 @@ if (args.includes("--help") || args.includes("-h") || action === "help") {
52
54
  const helpAction =
53
55
  action === "help"
54
56
  ? args[0]
55
- : action && ["add", "rm", "ls", "enable", "disable", "where"].includes(action)
57
+ : action && ["add", "rm", "ls", "enable", "disable", "where", "tui"].includes(action)
56
58
  ? action
57
59
  : null;
58
60
  printHelp(helpAction);
@@ -62,13 +64,34 @@ if (args.includes("--help") || args.includes("-h") || action === "help") {
62
64
  for (let i = 0; i < args.length; i += 1) {
63
65
  const arg = args[i];
64
66
  if (arg === "--client") {
65
- options.clients.push(args[i + 1]);
67
+ options.clients.push(normalizeClientName(args[i + 1]));
66
68
  i += 1;
67
69
  } else if (arg === "--env") {
70
+ const maybePath = args[i + 1];
71
+ if (maybePath && !maybePath.startsWith("-")) {
72
+ // Backward-compatible form: --env /path/to/.env
73
+ options.envPath = maybePath;
74
+ options.useSessionEnv = false;
75
+ options.mcpDestination = null;
76
+ i += 1;
77
+ } else {
78
+ options.useSessionEnv = true;
79
+ options.envPath = null;
80
+ options.mcpDestination = null;
81
+ }
82
+ } else if (arg === "--env-path") {
68
83
  options.envPath = args[i + 1];
84
+ options.useSessionEnv = false;
85
+ options.mcpDestination = null;
69
86
  i += 1;
87
+ } else if (arg === "--session-env") {
88
+ options.useSessionEnv = true;
89
+ options.envPath = null;
90
+ options.mcpDestination = null;
70
91
  } else if (arg === "--mcp") {
71
92
  options.mcpDestination = args[i + 1];
93
+ options.useSessionEnv = false;
94
+ options.envPath = null;
72
95
  i += 1;
73
96
  } else if (arg === "--name") {
74
97
  options.name = args[i + 1];
@@ -122,40 +145,46 @@ for (let i = 0; i < args.length; i += 1) {
122
145
  }
123
146
  }
124
147
 
125
- if (!action || !["add", "rm", "ls", "enable", "disable", "where"].includes(action)) {
126
- fail("Provide a command: add | rm | ls | enable | disable | where.");
148
+ if (!action || !["add", "rm", "ls", "enable", "disable", "where", "tui"].includes(action)) {
149
+ fail("Provide a command: add | rm | ls | enable | disable | where | tui.");
150
+ }
151
+
152
+ let effectiveAction = action;
153
+ if (action === "tui") {
154
+ runTuiWizard(options);
155
+ effectiveAction = options.tuiAction || "add";
127
156
  }
128
157
 
129
158
  if (options.clients.length === 0) {
130
159
  fail("Provide at least one --client.");
131
160
  }
132
161
 
133
- if (!["ls"].includes(action) && !options.name) {
162
+ if (!["ls"].includes(effectiveAction) && !options.name) {
134
163
  fail("Provide --name <serverName> (required).");
135
164
  }
136
165
 
137
166
  const transportNormalized = options.transport === "http" ? "streamableHttp" : options.transport;
138
167
  options.transport = transportNormalized;
139
168
 
140
- if (action === "rm") {
169
+ if (effectiveAction === "rm") {
141
170
  options.remove = true;
142
171
  }
143
- if (action === "ls") {
172
+ if (effectiveAction === "ls") {
144
173
  options.list = true;
145
174
  }
146
- if (action === "enable") {
175
+ if (effectiveAction === "enable") {
147
176
  options.toggle = true;
148
177
  options.disabled = false;
149
178
  }
150
- if (action === "disable") {
179
+ if (effectiveAction === "disable") {
151
180
  options.toggle = true;
152
181
  options.disabled = true;
153
182
  }
154
- if (action === "where") {
183
+ if (effectiveAction === "where") {
155
184
  options.where = true;
156
185
  }
157
186
 
158
- if (options.remove && action !== "rm") {
187
+ if (options.remove && effectiveAction !== "rm") {
159
188
  fail("Use the rm command instead of --remove.");
160
189
  }
161
190
  if (options.list && options.toggle) {
@@ -173,15 +202,16 @@ if (options.projectPath && options.allProjects) {
173
202
  if (options.where && (options.list || options.remove || options.toggle)) {
174
203
  fail("The where command does not support ls/rm/enable/disable flags.");
175
204
  }
176
- if (options.projectPath && action === "add" && options.scope !== "global") {
205
+ if (options.projectPath && effectiveAction === "add" && options.scope !== "global") {
177
206
  fail("--project is only supported for Claude global config.");
178
207
  }
179
208
 
180
- const requiresConnectionParams = !options.remove && !options.toggle && !options.list && !options.where;
209
+ const requiresConnectionParams =
210
+ !options.remove && !options.toggle && !options.list && !options.where;
181
211
 
182
212
  if (requiresConnectionParams && options.transport === "stdio") {
183
- if (!options.envPath && !options.mcpDestination) {
184
- fail("Provide either --env <path> or --mcp <destination>.");
213
+ if (!options.envPath && !options.mcpDestination && !options.useSessionEnv) {
214
+ fail("Provide --env, --env-path <path>, or --mcp <destination>.");
185
215
  }
186
216
  }
187
217
 
@@ -189,8 +219,8 @@ if (requiresConnectionParams && options.transport !== "stdio") {
189
219
  if (!options.url) {
190
220
  fail("Provide --url <http(s)://...> for sse/http transports.");
191
221
  }
192
- if (options.envPath || options.mcpDestination) {
193
- fail("--env/--mcp are only valid for stdio transport.");
222
+ if (options.envPath || options.mcpDestination || options.useSessionEnv) {
223
+ fail("--env/--env-path/--mcp are only valid for stdio transport.");
194
224
  }
195
225
  }
196
226
 
@@ -201,20 +231,22 @@ const userProfile = process.env.USERPROFILE || home;
201
231
 
202
232
  const serverArgsRaw = [
203
233
  `--transport=${options.transport}`,
204
- options.envPath
205
- ? `--env=${options.envPath}`
206
- : options.mcpDestination
207
- ? `--mcp=${options.mcpDestination.toLowerCase()}`
208
- : undefined,
234
+ options.useSessionEnv
235
+ ? "--env"
236
+ : options.envPath
237
+ ? `--env-path=${options.envPath}`
238
+ : options.mcpDestination
239
+ ? `--mcp=${options.mcpDestination.toLowerCase()}`
240
+ : undefined,
209
241
  ];
210
242
  const serverArgs = serverArgsRaw.filter(Boolean);
211
243
 
212
- for (const client of options.clients) {
213
- const scope = options.scope || getDefaultScope(client);
214
- if (options.projectPath && client !== "claude") {
215
- fail("--project is only supported for Claude.");
216
- }
217
- switch (client) {
244
+ for (const client of options.clients) {
245
+ const scope = options.scope || getDefaultScope(client);
246
+ if (options.projectPath && client !== "claude") {
247
+ fail("--project is only supported for Claude.");
248
+ }
249
+ switch (client) {
218
250
  case "cline":
219
251
  requireScope("Cline", ["global"], scope);
220
252
  if (options.list) {
@@ -229,16 +261,20 @@ const serverArgs = serverArgsRaw.filter(Boolean);
229
261
  if (options.transport === "sse") {
230
262
  fail("Codex does not support SSE transport.");
231
263
  }
232
- requireScope("Codex", ["global"], scope);
264
+ requireScope("Codex", ["global", "local"], scope);
233
265
  if (options.list) {
234
- listCodexConfig(getCodexPath(platform, home, userProfile));
266
+ listCodexConfig(getCodexPath(platform, home, userProfile, scope));
235
267
  } else if (options.where) {
236
- whereCodexConfig(getCodexPath(platform, home, userProfile), options.name);
268
+ whereCodexConfig(getCodexPath(platform, home, userProfile, scope), options.name);
237
269
  } else {
238
- writeCodexConfig(getCodexPath(platform, home, userProfile), options.name, serverArgs);
270
+ writeCodexConfig(
271
+ getCodexPath(platform, home, userProfile, scope),
272
+ options.name,
273
+ serverArgs,
274
+ );
239
275
  }
240
276
  break;
241
- case "claude":
277
+ case "claude": {
242
278
  requireScope("Claude", ["global", "local"], scope);
243
279
  const claudeToggleScope = options.toggle ? "global" : scope;
244
280
  if (options.allProjects && claudeToggleScope !== "global") {
@@ -274,6 +310,7 @@ const serverArgs = serverArgsRaw.filter(Boolean);
274
310
  );
275
311
  }
276
312
  break;
313
+ }
277
314
  case "goose":
278
315
  requireScope("Goose", ["global"], scope);
279
316
  if (options.list) {
@@ -310,12 +347,7 @@ const serverArgs = serverArgsRaw.filter(Boolean);
310
347
  } else if (options.where) {
311
348
  whereJsonConfig(getAntigravityPath(home, scope), "antigravity", options.name);
312
349
  } else {
313
- writeJsonConfig(
314
- getAntigravityPath(home, scope),
315
- options.name,
316
- serverArgs,
317
- "antigravity",
318
- );
350
+ writeJsonConfig(getAntigravityPath(home, scope), options.name, serverArgs, "antigravity");
319
351
  }
320
352
  break;
321
353
  case "copilot":
@@ -363,6 +395,43 @@ const serverArgs = serverArgsRaw.filter(Boolean);
363
395
  }
364
396
  }
365
397
 
398
+ function normalizeClientName(clientName) {
399
+ if (!clientName) {
400
+ return clientName;
401
+ }
402
+ const normalized = clientName.toLowerCase();
403
+ return normalized === "kilo" ? "opencode" : normalized;
404
+ }
405
+
406
+ function runTuiWizard(opts) {
407
+ if (!process.stdin.isTTY) {
408
+ fail("TUI mode requires an interactive terminal.");
409
+ }
410
+ const helperPath = path.join(__dirname, "mcp-conf-tui.js");
411
+ const run = spawnSync(process.execPath, [helperPath], {
412
+ stdio: ["inherit", "inherit", "inherit", "pipe"],
413
+ encoding: "utf8",
414
+ });
415
+ if (run.error) {
416
+ fail(`Failed to start TUI helper: ${run.error.message}`);
417
+ }
418
+ if (run.status !== 0) {
419
+ process.exit(run.status || 1);
420
+ }
421
+ const rawPayload = run.output?.[3] || "";
422
+ const payload = String(rawPayload).trim();
423
+ if (!payload) {
424
+ fail("TUI did not return configuration.");
425
+ }
426
+ let selected;
427
+ try {
428
+ selected = JSON.parse(payload);
429
+ } catch {
430
+ fail("Invalid TUI output.");
431
+ }
432
+ Object.assign(opts, selected);
433
+ }
434
+
366
435
  function getClinePath(platformValue, homeDir, appDataDir) {
367
436
  if (platformValue === "win32") {
368
437
  return path.join(
@@ -387,7 +456,10 @@ function getClinePath(platformValue, homeDir, appDataDir) {
387
456
  );
388
457
  }
389
458
 
390
- function getCodexPath(platformValue, homeDir, userProfileDir) {
459
+ function getCodexPath(platformValue, homeDir, userProfileDir, scopeValue) {
460
+ if (scopeValue === "local") {
461
+ return path.join(process.cwd(), ".codex", "config.toml");
462
+ }
391
463
  if (platformValue === "win32") {
392
464
  return path.join(userProfileDir, ".codex", "config.toml");
393
465
  }
@@ -1212,6 +1284,7 @@ function printHelp(command) {
1212
1284
 
1213
1285
  Usage:
1214
1286
  mcp-conf <add|rm|ls|enable|disable|where> --client <name> [options]
1287
+ mcp-conf tui
1215
1288
  mcp-conf help <command>
1216
1289
 
1217
1290
  Commands:
@@ -1221,6 +1294,7 @@ Commands:
1221
1294
  enable enable an existing entry
1222
1295
  disable disable an existing entry
1223
1296
  where show where a server name is defined
1297
+ tui interactive setup wizard
1224
1298
 
1225
1299
  Run:
1226
1300
  mcp-conf <command> --help
@@ -1229,6 +1303,7 @@ Run:
1229
1303
  Notes:
1230
1304
  Scope defaults to --global (Copilot uses --local only).
1231
1305
  For Claude, --local maps to the project scope file ./.mcp.json.
1306
+ For Codex, --local writes to ./.codex/config.toml.
1232
1307
  `);
1233
1308
  return;
1234
1309
  }
@@ -1237,12 +1312,13 @@ Notes:
1237
1312
  process.stdout.write(`${header} add
1238
1313
 
1239
1314
  Usage:
1240
- mcp-conf add --client <name> --name <serverName> [--env <path> | --mcp <dest>] [options]
1315
+ mcp-conf add --client <name> --name <serverName> [--env | --env-path <path> | --mcp <dest>] [options]
1241
1316
 
1242
1317
  Options:
1243
- --client <name> cline | codex | claude | goose | cursor | windsurf | opencode | copilot | antigravity (repeatable)
1318
+ --client <name> cline | codex | claude | goose | cursor | windsurf | opencode | kilo | copilot | antigravity (repeatable)
1244
1319
  --name <serverName> required MCP server name key
1245
- --env <path> .env path (stdio only)
1320
+ --env use current shell/session env vars (stdio only)
1321
+ --env-path <path> .env path (stdio only)
1246
1322
  --mcp <dest> destination name (stdio only)
1247
1323
  --transport <type> stdio | sse | http (http => streamableHttp)
1248
1324
  --command <bin> command to run (default: mcp-abap-adt)
@@ -1266,7 +1342,7 @@ Usage:
1266
1342
  mcp-conf rm --client <name> --name <serverName> [options]
1267
1343
 
1268
1344
  Options:
1269
- --client <name> cline | codex | claude | goose | cursor | windsurf | opencode | copilot | antigravity (repeatable)
1345
+ --client <name> cline | codex | claude | goose | cursor | windsurf | opencode | kilo | copilot | antigravity (repeatable)
1270
1346
  --name <serverName> required MCP server name key
1271
1347
  --global write to global user config (default)
1272
1348
  --local write to project config (where supported)
@@ -1285,7 +1361,7 @@ Usage:
1285
1361
  mcp-conf ls --client <name> [options]
1286
1362
 
1287
1363
  Options:
1288
- --client <name> cline | codex | claude | goose | cursor | windsurf | opencode | copilot | antigravity (repeatable)
1364
+ --client <name> cline | codex | claude | goose | cursor | windsurf | opencode | kilo | copilot | antigravity (repeatable)
1289
1365
  --global write to global user config (default)
1290
1366
  --local write to project config (where supported)
1291
1367
  --all-projects Claude global: list across all projects
@@ -1302,7 +1378,7 @@ Usage:
1302
1378
  mcp-conf enable --client <name> --name <serverName> [options]
1303
1379
 
1304
1380
  Options:
1305
- --client <name> cline | codex | claude | goose | cursor | windsurf | opencode | copilot | antigravity (repeatable)
1381
+ --client <name> cline | codex | claude | goose | cursor | windsurf | opencode | kilo | copilot | antigravity (repeatable)
1306
1382
  --name <serverName> required MCP server name key
1307
1383
  --global write to global user config (default)
1308
1384
  --local write to project config (where supported)
@@ -1321,7 +1397,7 @@ Usage:
1321
1397
  mcp-conf disable --client <name> --name <serverName> [options]
1322
1398
 
1323
1399
  Options:
1324
- --client <name> cline | codex | claude | goose | cursor | windsurf | opencode | copilot | antigravity (repeatable)
1400
+ --client <name> cline | codex | claude | goose | cursor | windsurf | opencode | kilo | copilot | antigravity (repeatable)
1325
1401
  --name <serverName> required MCP server name key
1326
1402
  --global write to global user config (default)
1327
1403
  --local write to project config (where supported)
@@ -1340,7 +1416,7 @@ Usage:
1340
1416
  mcp-conf where --client <name> --name <serverName> [options]
1341
1417
 
1342
1418
  Options:
1343
- --client <name> cline | codex | claude | goose | cursor | windsurf | opencode | copilot | antigravity (repeatable)
1419
+ --client <name> cline | codex | claude | goose | cursor | windsurf | opencode | kilo | copilot | antigravity (repeatable)
1344
1420
  --name <serverName> required MCP server name key
1345
1421
  --global write to global user config (default)
1346
1422
  --local write to project config (where supported)
@@ -1349,6 +1425,18 @@ Options:
1349
1425
 
1350
1426
  Notes:
1351
1427
  Antigravity local scope is not supported yet; use --global.
1428
+ `);
1429
+ break;
1430
+ case "tui":
1431
+ process.stdout.write(`${header} tui
1432
+
1433
+ Usage:
1434
+ mcp-conf tui
1435
+
1436
+ Description:
1437
+ Start interactive setup wizard for add/ls/rm/enable/disable.
1438
+ Flow: operation -> client -> scope (skips scope when only one is supported).
1439
+ For add + sse/http, wizard also asks timeout and repeatable headers.
1352
1440
  `);
1353
1441
  break;
1354
1442
  default:
@@ -11,9 +11,9 @@ npm install -g @mcp-abap-adt/configurator
11
11
  ## Usage
12
12
 
13
13
  ```bash
14
- mcp-conf add --client cline --env /path/to/.env --name abap
14
+ mcp-conf add --client cline --env-path /path/to/.env --name abap
15
15
  mcp-conf add --client cline --mcp TRIAL --name abap
16
- mcp-conf add --client cline --env /path/to/.env --name abap --transport stdio
16
+ mcp-conf add --client cline --env-path /path/to/.env --name abap --transport stdio
17
17
  mcp-conf add --client claude --mcp TRIAL --name abap
18
18
  mcp-conf rm --client codex --name abap
19
19
  mcp-conf add --client cline --name direct-jwt-test-001 --transport http --url http://localhost:4004/mcp/stream/http --header x-sap-url=https://... --header x-sap-client=210 --header x-sap-auth-type=jwt --header x-sap-jwt-token=...
@@ -21,8 +21,10 @@ mcp-conf add --client cline --name local-mcp-sse --transport sse --url http://lo
21
21
  mcp-conf add --client codex --name abap-http --transport http --url http://localhost:3000/mcp/stream/http
22
22
  mcp-conf add --client codex --name abap-http --transport http --url http://localhost:3000/mcp/stream/http --header x-mcp-destination=trial
23
23
  mcp-conf add --client opencode --name abap --transport http --url http://localhost:3000/mcp/stream/http
24
+ mcp-conf add --client kilo --name abap --transport http --url http://localhost:3000/mcp/stream/http
24
25
  mcp-conf add --client copilot --name abap --transport http --url http://localhost:3000/mcp/stream/http --header x-mcp-destination=trial
25
26
  mcp-conf add --client antigravity --name abap --transport http --url http://localhost:3000/mcp/stream/http
27
+ mcp-conf tui
26
28
  ```
27
29
 
28
30
  ## Common Tasks
@@ -30,7 +32,7 @@ mcp-conf add --client antigravity --name abap --transport http --url http://loca
30
32
  Add MCP:
31
33
  ```bash
32
34
  mcp-conf add --client codex --mcp TRIAL --name abap
33
- mcp-conf add --client cline --env /path/to/.env --name abap
35
+ mcp-conf add --client cline --env-path /path/to/.env --name abap
34
36
  mcp-conf add --client claude --mcp TRIAL --name abap
35
37
  mcp-conf add --client claude --name abap-http --transport http --url http://localhost:3000/mcp/stream/http --header x-mcp-destination=trial
36
38
  mcp-conf add --client claude --name abap --project /path/to/project --mcp TRIAL
@@ -73,16 +75,26 @@ mcp-conf where --client claude --name goose --project /path/to/project
73
75
  mcp-conf where --client claude --name goose --all-projects
74
76
  ```
75
77
 
78
+ TUI wizard:
79
+ ```bash
80
+ mcp-conf tui
81
+ ```
82
+ - Flow order: `operation` -> `client` -> `scope`.
83
+ - Scope step is skipped automatically for single-scope clients.
84
+ - For `add` with `sse/http`, the wizard asks URL, timeout, and repeatable headers.
85
+ - Controls: arrow keys + Enter, Ctrl+C to cancel.
86
+
76
87
  Options:
77
- - Commands: `add`, `rm`, `ls`, `enable`, `disable`, `where` (first argument)
78
- - `--client <name>` (repeatable): `cline`, `codex`, `claude`, `goose`, `cursor`, `windsurf`, `opencode`, `copilot`, `antigravity`
79
- - `--env <path>`: use a specific `.env` file
88
+ - Commands: `add`, `rm`, `ls`, `enable`, `disable`, `where`, `tui` (first argument)
89
+ - `--client <name>` (repeatable): `cline`, `codex`, `claude`, `goose`, `cursor`, `windsurf`, `opencode` (`kilo` alias), `copilot`, `antigravity`
90
+ - `--env`: use shell/session environment variables (stdio only)
91
+ - `--env-path <path>`: use a specific `.env` file (stdio only)
80
92
  - `--mcp <destination>`: use service key destination
81
93
  - `--name <serverName>`: MCP server name (required)
82
94
  - `--transport <type>`: `stdio`, `sse`, or `http` (`http` maps to `streamableHttp`)
83
95
  - `--command <bin>`: command to run (default: `mcp-abap-adt`)
84
96
  - `--global`: write to the global user config (default)
85
- - `--local`: write to the project config (supported by `cursor`, `opencode`, `copilot`, `claude`)
97
+ - `--local`: write to the project config (supported by `cursor`, `opencode`/`kilo`, `copilot`, `claude`, `codex`)
86
98
  - `--all-projects`: for Claude (global scope), apply `rm/enable/disable/ls/where` across all projects
87
99
  - `--project <path>`: for Claude (global scope), target a specific project path
88
100
  - `--url <http(s)://...>`: required for `sse` and `http`
@@ -90,8 +102,9 @@ Options:
90
102
  - `--timeout <seconds>`: timeout value for client entries (default: 60)
91
103
 
92
104
  Notes:
93
- - `disable` and `rm` do not require `--env` or `--mcp`.
94
- - `--env`/`--mcp` are only valid for `stdio` transport. For `sse/http`, use `--url` and optional `--header`.
105
+ - `disable` and `rm` do not require `--env`, `--env-path`, or `--mcp`.
106
+ - `--env`/`--env-path`/`--mcp` are only valid for `stdio` transport. For `sse/http`, use `--url` and optional `--header`.
107
+ - `mcp-conf tui` starts an interactive wizard and writes the same config as `add`.
95
108
  - Cursor/Copilot enable/disable are not implemented yet.
96
109
  - Antigravity enable/disable uses `disabled: true|false` on the entry.
97
110
  - Antigravity local scope is not supported yet; use `--global`.
@@ -100,9 +113,9 @@ Notes:
100
113
  - Antigravity HTTP entries use `serverUrl` instead of `url`.
101
114
  - New entries for Cline, Codex, Windsurf, Goose, Claude, and OpenCode are added **disabled by default**. Use `enable` to turn them on.
102
115
  - Windsurf follows `disabled` like Cline. The configurator sets `disabled = true` for default-disabled entries.
103
- - `enable`/`disable` only work if the server entry already exists. Use add commands with `--env` or `--mcp` first.
116
+ - `enable`/`disable` only work if the server entry already exists. Use add commands with `--env`, `--env-path`, or `--mcp` first.
104
117
  - Non-stdio transports are supported for Cline/Cursor/Windsurf/Claude/Goose. Codex supports `http` (streamable HTTP) but not `sse`.
105
- - Codex writes custom headers under `http_headers` in `~/.codex/config.toml`.
118
+ - Codex writes custom headers under `http_headers` in `~/.codex/config.toml` (or `./.codex/config.toml` for `--local`).
106
119
  - Codex HTTP entries include `startup_timeout_sec` (default: 60).
107
120
  - `--dry-run`: print changes without writing files
108
121
  - `--force`: overwrite existing server entry if it exists
@@ -120,6 +133,7 @@ Global (default) locations:
120
133
  - **Codex**:
121
134
  - Linux/macOS: `~/.codex/config.toml`
122
135
  - Windows: `%USERPROFILE%\.codex\config.toml`
136
+ - Local (project): `./.codex/config.toml`
123
137
  - **Claude Code (CLI)**:
124
138
  - Linux default: `~/.claude.json` (per-project entries under `projects.<cwd>.mcpServers`)
125
139
  - **Claude Desktop**:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/configurator",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "MCP client configurator for mcp-abap-adt and mcp-abap-adt-proxy",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,9 +28,10 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "@iarna/toml": "^3.0.0",
31
+ "enquirer": "^2.4.1",
31
32
  "yaml": "^2.8.1"
32
33
  },
33
34
  "devDependencies": {
34
- "@biomejs/biome": "^2.3.14"
35
+ "@biomejs/biome": "^2.3.15"
35
36
  }
36
37
  }