@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/README.md +49 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +127 -318
- package/dist/install.js.map +1 -1
- package/dist/key-manager.d.ts +37 -3
- package/dist/key-manager.d.ts.map +1 -1
- package/dist/key-manager.js +109 -53
- package/dist/key-manager.js.map +1 -1
- package/dist/keys-cli.d.ts +38 -0
- package/dist/keys-cli.d.ts.map +1 -1
- package/dist/keys-cli.js +453 -293
- package/dist/keys-cli.js.map +1 -1
- package/dist/tools/snippets.d.ts.map +1 -1
- package/dist/tools/snippets.js +23 -12
- package/dist/tools/snippets.js.map +1 -1
- package/dist/tools/templates.js +2 -2
- package/dist/tools/templates.js.map +1 -1
- package/dist/utils/cli-env.d.ts.map +1 -1
- package/dist/utils/cli-env.js +2 -1
- package/dist/utils/cli-env.js.map +1 -1
- package/dist/utils/command-info.d.ts +11 -0
- package/dist/utils/command-info.d.ts.map +1 -0
- package/dist/utils/command-info.js +25 -0
- package/dist/utils/command-info.js.map +1 -0
- package/dist/utils/endpoint-prompt.d.ts +1 -0
- package/dist/utils/endpoint-prompt.d.ts.map +1 -1
- package/dist/utils/endpoint-prompt.js +20 -4
- package/dist/utils/endpoint-prompt.js.map +1 -1
- package/dist/utils/tool-config.d.ts +2 -0
- package/dist/utils/tool-config.d.ts.map +1 -1
- package/dist/utils/tool-config.js +6 -0
- package/dist/utils/tool-config.js.map +1 -1
- package/package.json +8 -4
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([`${
|
|
154
|
-
`${
|
|
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
|
-
], [`${
|
|
157
|
-
`${
|
|
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(
|
|
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(` ${
|
|
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
|
-
//
|
|
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,
|
|
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
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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 {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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("
|
|
390
|
+
showSection("Configure AI Tools", icons.zap);
|
|
491
391
|
console.log();
|
|
492
|
-
|
|
493
|
-
|
|
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: "
|
|
503
|
-
message: "Select
|
|
396
|
+
name: "selectedTools",
|
|
397
|
+
message: "Select your AI tools to configure:",
|
|
504
398
|
choices: [
|
|
505
|
-
{ name: "
|
|
506
|
-
{
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
},
|
|
510
|
-
{ name: "
|
|
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
|
-
|
|
513
|
-
|
|
407
|
+
validate: (arr) => Array.isArray(arr) && arr.length > 0
|
|
408
|
+
? true
|
|
409
|
+
: "Please select at least one tool",
|
|
514
410
|
},
|
|
515
411
|
]);
|
|
516
|
-
|
|
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(
|
|
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
|
-
|
|
728
|
-
|
|
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
|
}
|