From 746c9c4fd37f05614923738161a8ff4f99c7486e Mon Sep 17 00:00:00 2001 From: Greg Methvin Date: Thu, 20 Nov 2025 01:02:31 -0800 Subject: [PATCH] Add support for Gemini CLI --- README.md | 9 ++++++--- package.json | 2 ++ src/install.ts | 40 +++++++++++++++++++++++----------------- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 0a80c6f..2f61918 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ![Iterable MCP Server Setup](images/iterable-mcp-setup.png) -With the new Iterable MCP server, you can now connect Iterable to your favorite AI tools like Cursor, Claude Desktop, and Claude Code! +With the new Iterable MCP server, you can now connect Iterable to your favorite AI tools like Cursor, Claude Desktop, Claude Code, and Gemini CLI! ## What is MCP? @@ -58,7 +58,7 @@ Throughout this guide, commands are shown as `iterable-mcp` for brevity. If not ```bash git clone https://github.com/iterable/mcp-server.git cd mcp-server -pnpm install-dev:cursor # or install-dev:claude-desktop or install-dev:claude-code +pnpm install-dev:cursor # or install-dev:claude-desktop, install-dev:claude-code, or install-dev:gemini-cli ``` ### Claude Code @@ -94,11 +94,14 @@ claude mcp add-from-claude-desktop For more information, see the [Claude Code MCP documentation](https://docs.claude.com/en/docs/claude-code/mcp). -### Manual configuration (Cursor & Claude Desktop) +### Manual configuration (Cursor, Claude Desktop & Gemini CLI) The above commands will automatically configure your AI tool to use the MCP server by editing the appropriate configuration file, but you can also manually edit the appropriate configuration file: - **Claude Desktop:** `~/Library/Application Support/Claude/claude_desktop_config.json` - **Cursor:** `~/.cursor/mcp.json` +- **Gemini CLI:** `~/.gemini/settings.json` + +All three use the same configuration format: **Recommended: Using key manager:** ```bash diff --git a/package.json b/package.json index cb9d4be..f171703 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,8 @@ "install-dev:claude-code:debug": "pnpm install-dev && node dist/index.js setup --claude-code --debug --local", "install-dev:cursor": "pnpm install-dev && node dist/index.js setup --cursor --local", "install-dev:cursor:debug": "pnpm install-dev && node dist/index.js setup --cursor --debug --local", + "install-dev:gemini-cli": "pnpm install-dev && node dist/index.js setup --gemini-cli --local", + "install-dev:gemini-cli:debug": "pnpm install-dev && node dist/index.js setup --gemini-cli --debug --local", "lint:fix": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" --fix --quiet", "prepublishOnly": "pnpm build", "prestart": "pnpm build", diff --git a/src/install.ts b/src/install.ts index 0035f85..f733281 100644 --- a/src/install.ts +++ b/src/install.ts @@ -19,14 +19,6 @@ const { dirname, join } = path; const LOCAL_BINARY_NAME = "iterable-mcp"; const NPX_PACKAGE_NAME = "@iterable/mcp"; -// Tool display names -const TOOL_NAMES = { - cursor: "Cursor", - "claude-desktop": "Claude Desktop", - "claude-code": "Claude Code", - manual: "Manual Setup", -} as const; - // Get package version const packageJson = JSON.parse( readFileSync( @@ -35,6 +27,17 @@ const packageJson = JSON.parse( ) ) as { version: string }; +// Tool display names +type ToolName = keyof typeof TOOL_NAMES; +const TOOL_NAMES = { + cursor: "Cursor", + "claude-desktop": "Claude Desktop", + "claude-code": "Claude Code", + "gemini-cli": "Gemini CLI", + manual: "Manual Setup", +} as const; + +type FileBasedToolName = keyof typeof TOOL_CONFIGS; const TOOL_CONFIGS = { "claude-desktop": (() => { switch (process.platform) { @@ -61,7 +64,8 @@ const TOOL_CONFIGS = { } })(), cursor: path.join(os.homedir(), ".cursor", "mcp.json"), -} as const; + "gemini-cli": path.join(os.homedir(), ".gemini", "settings.json"), +} as const satisfies Record; const execFileAsync = promisify(execFile); @@ -221,10 +225,11 @@ export const setupMcpServer = async (): Promise => { const showHelp = args.includes("--help") || args.includes("-h"); const advanced = args.includes("--advanced"); const autoUpdate = args.includes("--auto-update"); - let tools = [ + let tools: ToolName[] = [ ...(args.includes("--claude-desktop") ? ["claude-desktop" as const] : []), ...(args.includes("--cursor") ? ["cursor" as const] : []), ...(args.includes("--claude-code") ? ["claude-code" as const] : []), + ...(args.includes("--gemini-cli") ? ["gemini-cli" as const] : []), ...(args.includes("--manual") ? ["manual" as const] : []), ]; @@ -254,6 +259,7 @@ export const setupMcpServer = async (): Promise => { [`${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"], [ `${commandName} setup --cursor --claude-desktop`, @@ -359,9 +365,7 @@ export const setupMcpServer = async (): Promise => { showIterableLogo(packageJson.version); const { selectedTools } = await inquirer.prompt<{ - selectedTools: Array< - "cursor" | "claude-desktop" | "claude-code" | "manual" - >; + selectedTools: ToolName[]; }>([ { type: "checkbox", @@ -371,6 +375,7 @@ export const setupMcpServer = async (): Promise => { { name: "Cursor", value: "cursor" }, { name: "Claude Desktop", value: "claude-desktop" }, { name: "Claude Code (CLI)", value: "claude-code" }, + { name: "Gemini CLI", value: "gemini-cli" }, { name: "Other / Manual Setup", value: "manual" }, ], validate: (arr: any) => @@ -898,8 +903,8 @@ export const setupMcpServer = async (): Promise => { } const fileBasedTools = tools.filter( - (tool) => tool === "claude-desktop" || tool === "cursor" - ) as Array<"claude-desktop" | "cursor">; + (tool): tool is FileBasedToolName => tool in TOOL_CONFIGS + ); const needsClaudeCode = tools.includes("claude-code"); const needsManual = tools.includes("manual"); @@ -907,8 +912,7 @@ export const setupMcpServer = async (): Promise => { const { updateToolConfig } = await import("./utils/tool-config.js"); for (const tool of fileBasedTools) { const configPath = TOOL_CONFIGS[tool]; - const toolName = - tool === "claude-desktop" ? "Claude Desktop" : "Cursor"; + const toolName = TOOL_NAMES[tool]; spinner.start(`Configuring ${toolName}...`); try { await updateToolConfig(configPath, iterableMcpConfig); @@ -1004,6 +1008,8 @@ export const setupMcpServer = async (): Promise => { if (fileBasedTools.includes("cursor")) configuredTools.push("Cursor"); if (fileBasedTools.includes("claude-desktop")) configuredTools.push("Claude Desktop"); + if (fileBasedTools.includes("gemini-cli")) + configuredTools.push("Gemini CLI"); if (needsClaudeCode) configuredTools.push("Claude Code"); if (needsManual) configuredTools.push("your AI tool");