@iterable/mcp 1.2.2 → 1.3.1

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/dist/install.js CHANGED
@@ -7,12 +7,11 @@ import path from "path";
7
7
  import { fileURLToPath } from "url";
8
8
  import { promisify } from "util";
9
9
  import { getKeyManager } from "./key-manager.js";
10
+ import { displayKeyDetails, saveKeyInteractive } from "./keys-cli.js";
11
+ import { COMMAND_NAME, KEYS_COMMAND_TABLE, NPX_PACKAGE_NAME, } from "./utils/command-info.js";
10
12
  import { getKeyStorageMessage } from "./utils/formatting.js";
11
13
  const { dirname, join } = path;
12
14
  // IMPORTANT: UI imports are loaded lazily inside functions to avoid ESM issues in Jest/CommonJS.
13
- // Executable/package names
14
- const LOCAL_BINARY_NAME = "iterable-mcp";
15
- const NPX_PACKAGE_NAME = "@iterable/mcp";
16
15
  // Get package version
17
16
  const packageJson = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf-8"));
18
17
  const TOOL_NAMES = {
@@ -20,6 +19,8 @@ const TOOL_NAMES = {
20
19
  "claude-desktop": "Claude Desktop",
21
20
  "claude-code": "Claude Code",
22
21
  "gemini-cli": "Gemini CLI",
22
+ windsurf: "Windsurf",
23
+ antigravity: "Antigravity",
23
24
  manual: "Manual Setup",
24
25
  };
25
26
  const TOOL_CONFIGS = {
@@ -33,7 +34,9 @@ const TOOL_CONFIGS = {
33
34
  return path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "Claude", "claude_desktop_config.json");
34
35
  }
35
36
  })(),
37
+ antigravity: path.join(os.homedir(), ".gemini", "antigravity", "mcp_config.json"),
36
38
  cursor: path.join(os.homedir(), ".cursor", "mcp.json"),
39
+ windsurf: path.join(os.homedir(), ".codeium", "windsurf", "mcp_config.json"),
37
40
  "gemini-cli": path.join(os.homedir(), ".gemini", "settings.json"),
38
41
  };
39
42
  const execFileAsync = promisify(execFile);
@@ -128,16 +131,14 @@ export const setupMcpServer = async () => {
128
131
  const advanced = args.includes("--advanced");
129
132
  const autoUpdate = args.includes("--auto-update");
130
133
  let tools = [
131
- ...(args.includes("--claude-desktop") ? ["claude-desktop"] : []),
132
134
  ...(args.includes("--cursor") ? ["cursor"] : []),
135
+ ...(args.includes("--claude-desktop") ? ["claude-desktop"] : []),
133
136
  ...(args.includes("--claude-code") ? ["claude-code"] : []),
134
137
  ...(args.includes("--gemini-cli") ? ["gemini-cli"] : []),
138
+ ...(args.includes("--windsurf") ? ["windsurf"] : []),
139
+ ...(args.includes("--antigravity") ? ["antigravity"] : []),
135
140
  ...(args.includes("--manual") ? ["manual"] : []),
136
141
  ];
137
- // Detect how the command was invoked
138
- const isNpx = process.argv[1]?.includes("npx") ||
139
- process.env.npm_execpath?.includes("npx");
140
- const commandName = isNpx ? `npx ${NPX_PACKAGE_NAME}` : LOCAL_BINARY_NAME;
141
142
  if (showHelp) {
142
143
  const { createTable, icons, showBox, showIterableLogo, showSection } = await import("./utils/ui.js");
143
144
  const chalk = (await import("chalk")).default;
@@ -150,11 +151,14 @@ export const setupMcpServer = async () => {
150
151
  colWidths: [45, 40],
151
152
  style: "normal",
152
153
  });
153
- setupTable.push([`${commandName} setup --claude-desktop`, "Configure for Claude Desktop"], [`${commandName} setup --cursor`, "Configure for Cursor"], [`${commandName} setup --claude-code`, "Configure for Claude Code"], [`${commandName} setup --gemini-cli`, "Configure for Gemini CLI"], [`${commandName} setup --manual`, "Show manual config instructions"], [
154
- `${commandName} setup --cursor --claude-desktop`,
154
+ setupTable.push([`${COMMAND_NAME} setup --cursor`, "Configure for Cursor"], [
155
+ `${COMMAND_NAME} setup --claude-desktop`,
156
+ "Configure for Claude Desktop",
157
+ ], [`${COMMAND_NAME} setup --claude-code`, "Configure for Claude Code"], [`${COMMAND_NAME} setup --gemini-cli`, "Configure for Gemini CLI"], [`${COMMAND_NAME} setup --windsurf`, "Configure for Windsurf"], [`${COMMAND_NAME} setup --antigravity`, "Configure for Antigravity"], [`${COMMAND_NAME} setup --manual`, "Show manual config instructions"], [
158
+ `${COMMAND_NAME} setup --cursor --claude-desktop`,
155
159
  "Configure multiple tools",
156
- ], [`${commandName} setup --cursor --debug`, "Enable debug logging"], [
157
- `${commandName} setup --cursor --auto-update`,
160
+ ], [`${COMMAND_NAME} setup --cursor --debug`, "Enable debug logging"], [
161
+ `${COMMAND_NAME} setup --cursor --auto-update`,
158
162
  "Always use latest version",
159
163
  ]);
160
164
  console.log(setupTable.toString());
@@ -167,7 +171,7 @@ export const setupMcpServer = async () => {
167
171
  colWidths: [45, 40],
168
172
  style: "normal",
169
173
  });
170
- keysTable.push([`${commandName} keys list`, "View all stored API keys"], [`${commandName} keys add`, "Add a new API key"], [`${commandName} keys activate <name>`, "Switch to a different key"], [`${commandName} keys delete <id>`, "Remove a key by ID"]);
174
+ keysTable.push(...KEYS_COMMAND_TABLE);
171
175
  console.log(keysTable.toString());
172
176
  console.log();
173
177
  console.log();
@@ -195,7 +199,7 @@ export const setupMcpServer = async () => {
195
199
  console.log();
196
200
  showBox("Quick Start", [
197
201
  chalk.white.bold("1. Run setup for your AI tool:"),
198
- chalk.cyan(` ${commandName} setup --cursor`),
202
+ chalk.cyan(` ${COMMAND_NAME} setup --cursor`),
199
203
  "",
200
204
  chalk.white.bold("2. Restart your AI tool"),
201
205
  "",
@@ -209,38 +213,11 @@ export const setupMcpServer = async () => {
209
213
  ], { icon: icons.lock, theme: "info", padding: 1 });
210
214
  return;
211
215
  }
212
- // New behavior: if no tools were provided as flags, prompt user to select one or more
213
- if (tools.length === 0) {
214
- // Show logo first when no flags provided
215
- const { showIterableLogo } = await import("./utils/ui.js");
216
- console.clear();
217
- showIterableLogo(packageJson.version);
218
- const { selectedTools } = await inquirer.prompt([
219
- {
220
- type: "checkbox",
221
- name: "selectedTools",
222
- message: "Select your AI tools to configure:",
223
- choices: [
224
- { name: "Cursor", value: "cursor" },
225
- { name: "Claude Desktop", value: "claude-desktop" },
226
- { name: "Claude Code (CLI)", value: "claude-code" },
227
- { name: "Gemini CLI", value: "gemini-cli" },
228
- { name: "Other / Manual Setup", value: "manual" },
229
- ],
230
- validate: (arr) => Array.isArray(arr) && arr.length > 0
231
- ? true
232
- : "Please select at least one tool",
233
- },
234
- ]);
235
- tools = selectedTools;
236
- }
237
- // Start the setup flow
216
+ // Start the setup flow - show logo and load UI
238
217
  console.clear();
239
- const { formatKeyValue, icons, showCompletion, showError, showInfo, showIterableLogo, showSection, showSuccess, showWarning, linkColor, valueColor, } = await import("./utils/ui.js");
218
+ const { formatKeyValue, icons, showCompletion, showError, showInfo, showIterableLogo, showSection, showSuccess, showWarning, linkColor, } = await import("./utils/ui.js");
240
219
  const chalk = (await import("chalk")).default;
241
220
  showIterableLogo(packageJson.version);
242
- // Succinct overview of what will be configured
243
- console.log(chalk.gray(`Running setup to configure: ${tools.map((t) => TOOL_NAMES[t]).join(", ")}`));
244
221
  console.log();
245
222
  const ora = (await import("ora")).default;
246
223
  const spinner = ora();
@@ -250,9 +227,6 @@ export const setupMcpServer = async () => {
250
227
  showSection("API Key Configuration", icons.key);
251
228
  console.log();
252
229
  let apiKey;
253
- let keyName;
254
- let baseUrl;
255
- let usedKeyName;
256
230
  let usedExistingKey = false;
257
231
  let selectedExistingMeta;
258
232
  // 1) Offer using API key from environment
@@ -285,6 +259,8 @@ export const setupMcpServer = async () => {
285
259
  ]);
286
260
  if (useExisting) {
287
261
  const { formatKeychainChoiceLabel } = await import("./utils/ui.js");
262
+ // Find the active key to use as default
263
+ const activeKey = keys.find((k) => k.isActive);
288
264
  const { chosenId } = await inquirer.prompt([
289
265
  {
290
266
  type: "list",
@@ -295,6 +271,7 @@ export const setupMcpServer = async () => {
295
271
  value: k.id,
296
272
  short: k.name,
297
273
  })),
274
+ default: activeKey?.id,
298
275
  pageSize: Math.min(10, keys.length),
299
276
  },
300
277
  ]);
@@ -302,11 +279,8 @@ export const setupMcpServer = async () => {
302
279
  const meta = keys.find((k) => k.id === chosenId);
303
280
  if (!meta)
304
281
  throw new Error("Selected key metadata not found");
305
- baseUrl = meta.baseUrl;
306
- usedKeyName = meta.name;
307
282
  usedExistingKey = true;
308
283
  selectedExistingMeta = meta;
309
- showSuccess(`Using existing key "${meta.name}"`);
310
284
  }
311
285
  catch (e) {
312
286
  showError(e instanceof Error
@@ -315,265 +289,129 @@ export const setupMcpServer = async () => {
315
289
  process.exit(1);
316
290
  }
317
291
  }
292
+ else {
293
+ console.log();
294
+ }
318
295
  }
319
296
  }
320
- // 3) If neither env nor existing key chosen, prompt for a new key
321
297
  if (!apiKey && !usedExistingKey) {
322
- const { newApiKey } = await inquirer.prompt([
323
- {
324
- type: "password",
325
- name: "newApiKey",
326
- message: "Enter your Iterable API key:",
327
- validate: (input) => {
328
- if (!input)
329
- return "API key is required";
330
- if (!/^[a-f0-9]{32}$/.test(input))
331
- return "API key must be a 32-character lowercase hexadecimal string";
332
- return true;
333
- },
334
- mask: "*",
335
- },
336
- ]);
337
- apiKey = newApiKey;
338
- }
339
- // Step 2: API Endpoint Selection
340
- console.log();
341
- showSection("API Endpoint Configuration", icons.globe);
342
- console.log();
343
- if (!baseUrl) {
344
- if (process.env.ITERABLE_BASE_URL) {
345
- baseUrl = process.env.ITERABLE_BASE_URL;
346
- showSuccess(`Using API endpoint from environment: ${baseUrl}`);
298
+ const keyManager = getKeyManager();
299
+ await keyManager.initialize();
300
+ const { getSpinner } = await import("./utils/cli-env.js");
301
+ const keySpinner = await getSpinner();
302
+ try {
303
+ const newKeyId = await saveKeyInteractive(keyManager, null, {
304
+ chalk,
305
+ icons,
306
+ showError,
307
+ showSuccess,
308
+ showInfo,
309
+ formatKeyValue,
310
+ linkColor,
311
+ showBox: (await import("./utils/ui.js")).showBox,
312
+ }, keySpinner, {
313
+ skipRestartNotice: true,
314
+ advanced,
315
+ autoActivate: true,
316
+ });
317
+ selectedExistingMeta =
318
+ (await keyManager.getKeyMetadata(newKeyId)) ?? undefined;
347
319
  }
348
- else {
349
- const { promptForIterableBaseUrl } = await import("./utils/endpoint-prompt.js");
350
- try {
351
- baseUrl = await promptForIterableBaseUrl({
352
- inquirer,
353
- icons,
354
- chalk,
355
- showError,
356
- });
357
- }
358
- catch {
359
- process.exit(1);
360
- }
320
+ catch (error) {
321
+ showError(error instanceof Error ? error.message : "Failed to add key");
322
+ process.exit(1);
361
323
  }
362
324
  }
363
- else {
364
- showSuccess(`Using API endpoint from selected key: ${baseUrl}`);
365
- }
366
- console.log();
367
- console.log(formatKeyValue("Selected endpoint", baseUrl, linkColor()));
368
- console.log();
369
- // Step 3: Store the key securely
370
- let existingKeyWithValue = null;
371
- const keyManager = getKeyManager();
372
- spinner.start("Initializing secure key storage...");
373
- await keyManager.initialize();
374
- spinner.succeed("Key storage ready");
375
- // Check if key already exists
376
- if (!usedExistingKey && apiKey) {
377
- spinner.start("Checking for existing keys...");
378
- existingKeyWithValue = await keyManager.findKeyByValue(apiKey);
379
- spinner.stop();
380
- }
381
- if (usedExistingKey) {
382
- // Offer to activate the selected existing key if not already active
325
+ else if (usedExistingKey) {
326
+ const keyManager = getKeyManager();
327
+ await keyManager.initialize();
328
+ console.log();
383
329
  if (!selectedExistingMeta?.isActive) {
384
- const { activateExisting } = await inquirer.prompt([
385
- {
386
- type: "confirm",
387
- name: "activateExisting",
388
- message: `Set "${selectedExistingMeta?.name}" as your active API key?`,
389
- default: true,
390
- },
391
- ]);
392
- if (activateExisting) {
393
- await keyManager.setActiveKey(selectedExistingMeta.id);
394
- showSuccess(`"${selectedExistingMeta?.name}" is now your active key`);
395
- }
396
- else {
397
- showInfo("Keeping your current active key");
398
- }
330
+ const { getSpinner } = await import("./utils/cli-env.js");
331
+ const activateSpinner = await getSpinner();
332
+ activateSpinner.start("Activating key...");
333
+ await keyManager.setActiveKey(selectedExistingMeta.id);
334
+ activateSpinner.succeed("Key activated successfully");
335
+ showSuccess(`"${selectedExistingMeta?.name}" is now your active API key`);
399
336
  }
400
337
  else {
401
338
  showSuccess(`"${selectedExistingMeta?.name}" is already your active key`);
402
339
  }
403
- }
404
- else if (existingKeyWithValue) {
405
- showInfo(`This API key is already stored as "${existingKeyWithValue.name}"`);
406
- usedKeyName = existingKeyWithValue.name;
407
- if (!existingKeyWithValue.isActive) {
408
- const { activateExisting } = await inquirer.prompt([
409
- {
410
- type: "confirm",
411
- name: "activateExisting",
412
- message: `Set "${existingKeyWithValue.name}" as your active API key?`,
413
- default: true,
414
- },
415
- ]);
416
- if (activateExisting) {
417
- await keyManager.setActiveKey(existingKeyWithValue.id);
418
- showSuccess(`"${existingKeyWithValue.name}" is now your active key`);
419
- }
420
- else {
421
- showInfo("Keeping your current active key");
422
- }
423
- }
424
- else {
425
- showSuccess(`"${existingKeyWithValue.name}" is already your active key`);
426
- }
340
+ console.log();
341
+ displayKeyDetails(selectedExistingMeta, {
342
+ chalk,
343
+ formatKeyValue,
344
+ linkColor,
345
+ });
346
+ console.log();
347
+ showInfo(`To modify permissions, run: ${chalk.cyan(`${COMMAND_NAME} keys update "${selectedExistingMeta?.name}"`)}`);
427
348
  }
428
349
  else {
429
- // New key - prompt for name
430
- const { newKeyName } = await inquirer.prompt([
431
- {
432
- type: "input",
433
- name: "newKeyName",
434
- message: "Enter a name for this API key:",
435
- default: "default",
436
- validate: (input) => {
437
- if (input && input.length > 50)
438
- return "Key name must be 50 characters or less";
439
- return true;
440
- },
441
- },
442
- ]);
443
- keyName = newKeyName || "default";
444
- spinner.start("Storing API key securely...");
350
+ const keyManager = getKeyManager();
351
+ await keyManager.initialize();
352
+ const { getSpinner } = await import("./utils/cli-env.js");
353
+ const keySpinner = await getSpinner();
445
354
  try {
446
- const newKeyId = await keyManager.addKey(keyName, apiKey, baseUrl);
447
- spinner.succeed("API key stored successfully!");
448
- console.log();
449
- console.log(formatKeyValue("Name", keyName, chalk.white.bold));
450
- console.log(formatKeyValue("ID", newKeyId, chalk.gray));
451
- console.log(formatKeyValue("Endpoint", baseUrl, chalk.cyan));
452
- console.log();
453
- usedKeyName = keyName;
454
- // Check if we should activate this key
455
- const allKeys = await keyManager.listKeys();
456
- if (allKeys.length > 1) {
457
- const { activateNew } = await inquirer.prompt([
458
- {
459
- type: "confirm",
460
- name: "activateNew",
461
- message: `Set "${keyName}" as your active API key?`,
462
- default: true,
463
- },
464
- ]);
465
- if (activateNew) {
466
- await keyManager.setActiveKey(newKeyId);
467
- showSuccess(`"${keyName}" is now your active key`);
468
- }
469
- else {
470
- showInfo(`Keeping your current active key. Run 'iterable-mcp keys activate "${keyName}"' to switch later.`);
471
- }
472
- }
355
+ const newKeyId = await saveKeyInteractive(keyManager, null, {
356
+ chalk,
357
+ icons,
358
+ showError,
359
+ showSuccess,
360
+ showInfo,
361
+ formatKeyValue,
362
+ linkColor,
363
+ showBox: (await import("./utils/ui.js")).showBox,
364
+ }, keySpinner, {
365
+ skipRestartNotice: true,
366
+ advanced,
367
+ prefilledApiKey: apiKey,
368
+ autoActivate: true,
369
+ });
370
+ selectedExistingMeta =
371
+ (await keyManager.getKeyMetadata(newKeyId)) ?? undefined;
473
372
  }
474
373
  catch (error) {
475
- const { sanitizeString } = await import("./utils/sanitize.js");
476
- spinner.fail("Failed to store API key");
477
- console.log();
478
- const msg = error instanceof Error
479
- ? sanitizeString(error.message)
480
- : "Unknown error";
481
- showError(msg);
482
- console.log();
483
- showInfo("Your API key was not stored. You can re-run 'iterable-mcp setup' to try again.");
484
- showInfo("If the problem persists, verify storage access and disk permissions.");
374
+ showError(error instanceof Error ? error.message : "Failed to add key");
485
375
  process.exit(1);
486
376
  }
487
377
  }
488
- // Step 4: Privacy & Security Settings (Basic by default; Advanced via --advanced)
378
+ if (!selectedExistingMeta) {
379
+ showError("Failed to get key metadata after setup");
380
+ process.exit(1);
381
+ }
382
+ let mcpEnv = pickPersistablePermissionEnv(selectedExistingMeta.env || {});
383
+ if (args.includes("--debug")) {
384
+ mcpEnv.ITERABLE_DEBUG = "true";
385
+ mcpEnv.LOG_LEVEL = "debug";
386
+ }
387
+ mcpEnv = enforceSendsRequiresWrites(mcpEnv, (msg) => showWarning(msg));
388
+ // Step 5: Configure AI Tools
489
389
  console.log();
490
- showSection("Privacy & Security Settings", icons.lock);
390
+ showSection("Configure AI Tools", icons.zap);
491
391
  console.log();
492
- // Default conservative env flags
493
- let selectedEnv = {
494
- ITERABLE_USER_PII: "false",
495
- ITERABLE_ENABLE_WRITES: "false",
496
- ITERABLE_ENABLE_SENDS: "false",
497
- };
498
- if (advanced) {
499
- const { permFlags } = await inquirer.prompt([
392
+ if (tools.length === 0) {
393
+ const { selectedTools } = await inquirer.prompt([
500
394
  {
501
395
  type: "checkbox",
502
- name: "permFlags",
503
- message: "Select permissions to enable (default: none):",
396
+ name: "selectedTools",
397
+ message: "Select your AI tools to configure:",
504
398
  choices: [
505
- { name: "View PII (access user data)", value: "pii" },
506
- {
507
- name: "Write operations (create/update/delete)",
508
- value: "writes",
509
- },
510
- { name: "Sends (campaigns/journeys/events)", value: "sends" },
399
+ { name: "Cursor", value: "cursor" },
400
+ { name: "Claude Desktop", value: "claude-desktop" },
401
+ { name: "Claude Code (CLI)", value: "claude-code" },
402
+ { name: "Gemini CLI", value: "gemini-cli" },
403
+ { name: "Windsurf", value: "windsurf" },
404
+ { name: "Antigravity", value: "antigravity" },
405
+ { name: "Other / Manual Setup", value: "manual" },
511
406
  ],
512
- pageSize: 6,
513
- default: [],
407
+ validate: (arr) => Array.isArray(arr) && arr.length > 0
408
+ ? true
409
+ : "Please select at least one tool",
514
410
  },
515
411
  ]);
516
- selectedEnv = {
517
- ITERABLE_USER_PII: permFlags.includes("pii")
518
- ? "true"
519
- : "false",
520
- ITERABLE_ENABLE_WRITES: permFlags.includes("writes")
521
- ? "true"
522
- : "false",
523
- ITERABLE_ENABLE_SENDS: permFlags.includes("sends")
524
- ? "true"
525
- : "false",
526
- };
527
- }
528
- let mcpEnv = {
529
- ITERABLE_USER_PII: selectedEnv.ITERABLE_USER_PII,
530
- ITERABLE_ENABLE_WRITES: selectedEnv.ITERABLE_ENABLE_WRITES,
531
- ITERABLE_ENABLE_SENDS: selectedEnv.ITERABLE_ENABLE_SENDS,
532
- };
533
- if (args.includes("--debug")) {
534
- mcpEnv.ITERABLE_DEBUG = "true";
535
- mcpEnv.LOG_LEVEL = "debug";
536
- }
537
- // Enforce permission invariants before displaying
538
- mcpEnv = enforceSendsRequiresWrites(mcpEnv, (msg) => showWarning(msg));
539
- console.log();
540
- console.log(formatKeyValue("User PII Access", mcpEnv.ITERABLE_USER_PII === "true"
541
- ? chalk.green("Enabled")
542
- : chalk.gray("Disabled")));
543
- console.log(formatKeyValue("Write Operations", mcpEnv.ITERABLE_ENABLE_WRITES === "true"
544
- ? chalk.green("Enabled")
545
- : chalk.gray("Disabled")));
546
- console.log(formatKeyValue("Sends", mcpEnv.ITERABLE_ENABLE_SENDS === "true"
547
- ? chalk.green("Enabled")
548
- : chalk.gray("Disabled")));
549
- // Only show the default note when all flags are disabled
550
- if (mcpEnv.ITERABLE_USER_PII !== "true" &&
551
- mcpEnv.ITERABLE_ENABLE_WRITES !== "true" &&
552
- mcpEnv.ITERABLE_ENABLE_SENDS !== "true") {
553
- console.log();
554
- console.log(chalk.gray("Note: These are disabled by default. If you absolutely need to enable these, run 'iterable-mcp setup --advanced'"));
555
- }
556
- else {
412
+ tools = selectedTools;
557
413
  console.log();
558
- showWarning("Warning: Elevated capabilities enabled. Sends, writes, and/or PII access may now occur.");
559
- }
560
- console.log();
561
- // If we used an existing key, persist the chosen settings to it
562
- if (usedKeyName) {
563
- try {
564
- await keyManager.updateKeyEnv(usedKeyName, pickPersistablePermissionEnv(mcpEnv));
565
- }
566
- catch (err) {
567
- if (process.env.ITERABLE_DEBUG === "true") {
568
- console.warn("Non-fatal: failed to persist per-key env settings", err);
569
- }
570
- }
571
414
  }
572
- // Step 5: Configure AI Tools
573
- console.log();
574
- showSection("Configuring AI Tools", icons.zap);
575
- console.log();
576
- // Ask about auto-update unless flag was provided
577
415
  let useAutoUpdate = autoUpdate;
578
416
  if (!autoUpdate) {
579
417
  const { enableAutoUpdate } = await inquirer.prompt([
@@ -584,6 +422,7 @@ export const setupMcpServer = async () => {
584
422
  default: false,
585
423
  },
586
424
  ]);
425
+ console.log();
587
426
  useAutoUpdate = enableAutoUpdate;
588
427
  }
589
428
  const iterableMcpConfig = buildMcpConfig({
@@ -591,35 +430,6 @@ export const setupMcpServer = async () => {
591
430
  env: omitPersistablePermissionEnv(mcpEnv),
592
431
  autoUpdate: useAutoUpdate,
593
432
  });
594
- // Preflight confirmation summary before applying changes
595
- console.log(chalk.gray("Summary:"));
596
- console.log(formatKeyValue("Tools", tools.map((t) => TOOL_NAMES[t]).join(", "), valueColor()));
597
- if (usedKeyName) {
598
- console.log(formatKeyValue("API Key", usedKeyName, valueColor()));
599
- }
600
- console.log(formatKeyValue("Endpoint", baseUrl, valueColor()));
601
- console.log(formatKeyValue("User PII", mcpEnv.ITERABLE_USER_PII === "true"
602
- ? chalk.green("Enabled")
603
- : chalk.gray("Disabled")));
604
- console.log(formatKeyValue("Writes", mcpEnv.ITERABLE_ENABLE_WRITES === "true"
605
- ? chalk.green("Enabled")
606
- : chalk.gray("Disabled")));
607
- console.log(formatKeyValue("Sends", mcpEnv.ITERABLE_ENABLE_SENDS === "true"
608
- ? chalk.green("Enabled")
609
- : chalk.gray("Disabled")));
610
- console.log();
611
- const { proceed } = await inquirer.prompt([
612
- {
613
- type: "confirm",
614
- name: "proceed",
615
- message: "Proceed with configuration?",
616
- default: true,
617
- },
618
- ]);
619
- if (!proceed) {
620
- showInfo("Setup cancelled by user. No changes were made.");
621
- return;
622
- }
623
433
  const fileBasedTools = tools.filter((tool) => tool in TOOL_CONFIGS);
624
434
  const needsClaudeCode = tools.includes("claude-code");
625
435
  const needsManual = tools.includes("manual");
@@ -649,11 +459,10 @@ export const setupMcpServer = async () => {
649
459
  spinner.fail("Claude CLI not found");
650
460
  showError("Please install Claude Code first: https://docs.claude.com/en/docs/claude-code/overview");
651
461
  console.log();
652
- showInfo("After installing, re-run: iterable-mcp setup --claude-code");
462
+ showInfo(`After installing, re-run: ${COMMAND_NAME} setup --claude-code`);
653
463
  process.exit(1);
654
464
  }
655
465
  const configJson = JSON.stringify(iterableMcpConfig);
656
- // Remove existing config (ignore errors)
657
466
  await execFileAsync("claude", ["mcp", "remove", "iterable"]).catch(() => { });
658
467
  spinner.start("Configuring Claude Code...");
659
468
  await new Promise((resolve, reject) => {
@@ -673,11 +482,9 @@ export const setupMcpServer = async () => {
673
482
  spinner.succeed("Claude Code configured successfully");
674
483
  }
675
484
  if (needsManual) {
676
- console.log();
677
485
  showSection("Manual Configuration", icons.rocket);
678
486
  console.log();
679
487
  const { type, command, args, env } = iterableMcpConfig;
680
- showInfo("Your API key has been stored.");
681
488
  showInfo("Add the MCP server to your AI tool with these settings:");
682
489
  console.log();
683
490
  console.log(chalk.white.bold(" Type:") + ` ${type}`);
@@ -693,16 +500,19 @@ export const setupMcpServer = async () => {
693
500
  console.log();
694
501
  showInfo("Refer to your AI tool's documentation for configuration instructions.");
695
502
  }
696
- // Build configured tools list
697
503
  const configuredTools = [];
698
504
  if (fileBasedTools.includes("cursor"))
699
505
  configuredTools.push("Cursor");
700
506
  if (fileBasedTools.includes("claude-desktop"))
701
507
  configuredTools.push("Claude Desktop");
702
- if (fileBasedTools.includes("gemini-cli"))
703
- configuredTools.push("Gemini CLI");
704
508
  if (needsClaudeCode)
705
509
  configuredTools.push("Claude Code");
510
+ if (fileBasedTools.includes("gemini-cli"))
511
+ configuredTools.push("Gemini CLI");
512
+ if (fileBasedTools.includes("windsurf"))
513
+ configuredTools.push("Windsurf");
514
+ if (fileBasedTools.includes("antigravity"))
515
+ configuredTools.push("Antigravity");
706
516
  if (needsManual)
707
517
  configuredTools.push("your AI tool");
708
518
  const toolsList = configuredTools.length === 1
@@ -712,7 +522,6 @@ export const setupMcpServer = async () => {
712
522
  : configuredTools.slice(0, -1).join(", ") +
713
523
  ", and " +
714
524
  configuredTools[configuredTools.length - 1];
715
- // Success!
716
525
  console.log();
717
526
  const nextSteps = [
718
527
  ...(needsManual ? ["Configure your AI tool as described above"] : []),
@@ -724,8 +533,8 @@ export const setupMcpServer = async () => {
724
533
  ];
725
534
  const tips = [
726
535
  `Try: 'list my Iterable campaigns' in ${toolsList}`,
727
- "Manage keys with 'iterable-mcp keys list'",
728
- "Switch keys with 'iterable-mcp keys activate <name>'",
536
+ `Manage keys with '${COMMAND_NAME} keys list'`,
537
+ `Switch keys with '${COMMAND_NAME} keys activate <name>'`,
729
538
  ];
730
539
  showCompletion("Setup Complete!", nextSteps, tips);
731
540
  }