diff --git a/CHANGELOG.md b/CHANGELOG.md index 30f5b94..0347d5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## [1.8.2] - 2026-04-11 + +- add `windsurf` support with global installs written to `~/.codeium/windsurf/mcp_config.json` (`mcpServers`) + ## [1.8.1] - 2026-04-07 - fix `find` / `search` package installs to stop pinning npm versions and resolve latest implicitly diff --git a/README.md b/README.md index c298f27..748a3f3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Add MCP servers to your favorite coding agents with a single command. -Supports **Claude Code**, **Codex**, **Cursor**, **OpenCode**, **VSCode** and [9 more](#supported-agents). +Supports **Claude Code**, **Codex**, **Cursor**, **OpenCode**, **VSCode** and [10 more](#supported-agents). ## Install an MCP Server @@ -49,9 +49,10 @@ MCP servers can be installed to any of these agents: | MCPorter | `mcporter` | `config/mcporter.json` | `~/.mcporter/mcporter.json` (or existing `~/.mcporter/mcporter.jsonc`) | | OpenCode | `opencode` | `opencode.json` | `~/.config/opencode/opencode.json` | | VS Code | `vscode` | `.vscode/mcp.json` | `~/Library/Application Support/Code/User/mcp.json` | +| Windsurf | `windsurf` | - | `~/.codeium/windsurf/mcp_config.json` | | Zed | `zed` | `.zed/settings.json` | `~/Library/Application Support/Zed/settings.json` | -**Aliases:** `cline-vscode` → `cline`, `gemini` → `gemini-cli`, `github-copilot` → `vscode` +**Aliases:** `cline-vscode` → `cline`, `codeium` → `windsurf`, `gemini` → `gemini-cli`, `github-copilot` → `vscode` ## Installation Scope diff --git a/bun.lock b/bun.lock index 19c9b90..3829311 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "add-mcp", diff --git a/package.json b/package.json index 1e7692a..a4a315b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "add-mcp", - "version": "1.8.1", + "version": "1.8.2", "description": "Add MCP servers to your favorite coding agents with a single command.", "author": "Andre Landgraf ", "license": "Apache-2.0", @@ -41,6 +41,7 @@ "mcporter", "opencode", "vscode", + "windsurf", "zed" ], "repository": { diff --git a/src/agents.ts b/src/agents.ts index df9c0bb..f63560c 100644 --- a/src/agents.ts +++ b/src/agents.ts @@ -65,6 +65,12 @@ const copilotConfigPath = join( process.env.XDG_CONFIG_HOME || join(home, ".copilot"), "mcp-config.json", ); +const windsurfConfigPath = join( + home, + ".codeium", + "windsurf", + "mcp_config.json", +); function transformGooseConfig( serverName: string, @@ -490,6 +496,20 @@ export const agents: Record = { }, }, + windsurf: { + name: "windsurf", + displayName: "Windsurf", + configPath: windsurfConfigPath, + projectDetectPaths: [], // Global only - no project support + configKey: "mcpServers", + format: "json", + supportedTransports: ["stdio", "http", "sse"], + detectGlobalInstall: async () => { + return existsSync(join(home, ".codeium", "windsurf")); + }, + transformConfig: transformCursorConfig, + }, + zed: { name: "zed", displayName: "Zed", diff --git a/src/types.ts b/src/types.ts index a73202a..159ebfd 100644 --- a/src/types.ts +++ b/src/types.ts @@ -12,12 +12,14 @@ export type AgentType = | "mcporter" | "opencode" | "vscode" + | "windsurf" | "zed"; export const agentAliases: Record = { "cline-vscode": "cline", gemini: "gemini-cli", "github-copilot": "vscode", + codeium: "windsurf", }; export type ConfigFormat = "json" | "yaml" | "toml"; diff --git a/tests/agents.test.ts b/tests/agents.test.ts index 814436d..75573a6 100644 --- a/tests/agents.test.ts +++ b/tests/agents.test.ts @@ -60,9 +60,9 @@ function cleanup() { // Agent Configuration Tests // ============================================ -test("getAgentTypes returns all 14 agents", () => { +test("getAgentTypes returns all 15 agents", () => { const types = getAgentTypes(); - assert.strictEqual(types.length, 14); + assert.strictEqual(types.length, 15); assert.ok(types.includes("antigravity")); assert.ok(types.includes("cline")); assert.ok(types.includes("cline-cli")); @@ -76,9 +76,12 @@ test("getAgentTypes returns all 14 agents", () => { assert.ok(types.includes("mcporter")); assert.ok(types.includes("opencode")); assert.ok(types.includes("vscode")); + assert.ok(types.includes("windsurf")); assert.ok(types.includes("zed")); }); + + test("All agents have required properties", () => { for (const [type, config] of Object.entries(agents)) { assert.ok(config.name, `${type} missing name`); @@ -122,6 +125,7 @@ test("supportsProjectConfig - returns false for global-only agents", () => { assert.strictEqual(supportsProjectConfig("cline-cli"), false); assert.strictEqual(supportsProjectConfig("claude-desktop"), false); assert.strictEqual(supportsProjectConfig("goose"), false); + assert.strictEqual(supportsProjectConfig("windsurf"), false); }); test("getProjectCapableAgents returns 9 agents", () => { @@ -138,14 +142,15 @@ test("getProjectCapableAgents returns 9 agents", () => { assert.ok(projectAgents.includes("zed")); }); -test("getGlobalOnlyAgents returns 5 agents", () => { +test("getGlobalOnlyAgents returns 6 agents", () => { const globalAgents = getGlobalOnlyAgents(); - assert.strictEqual(globalAgents.length, 5); + assert.strictEqual(globalAgents.length, 6); assert.ok(globalAgents.includes("antigravity")); assert.ok(globalAgents.includes("cline")); assert.ok(globalAgents.includes("cline-cli")); assert.ok(globalAgents.includes("claude-desktop")); assert.ok(globalAgents.includes("goose")); + assert.ok(globalAgents.includes("windsurf")); }); test("Project + global-only agents equals all agents", () => { @@ -241,7 +246,9 @@ test("detectProjectAgents - detects .zed directory", () => { const detected = detectProjectAgents(tempDir); assert.ok(detected.includes("zed")); }); - +test("agentAliases includes codeium -> windsurf", () => { + assert.strictEqual(agentAliases.codeium, "windsurf"); +}); test("detectProjectAgents - detects config/mcporter.json", () => { const tempDir = createTempDir(); mkdirSync(join(tempDir, "config"), { recursive: true }); @@ -277,6 +284,7 @@ test("detectProjectAgents - does not detect global-only agents", () => { assert.ok(!detected.includes("cline-cli")); assert.ok(!detected.includes("claude-desktop")); assert.ok(!detected.includes("goose")); + assert.ok(!detected.includes("windsurf")); }); // ============================================ @@ -307,6 +315,7 @@ test("isTransportSupported - most agents support http", () => { "mcporter", "opencode", "vscode", + "windsurf", "zed", ]; @@ -333,6 +342,7 @@ test("isTransportSupported - most agents support sse", () => { "mcporter", "opencode", "vscode", + "windsurf", "zed", ]; diff --git a/tests/e2e/install.test.ts b/tests/e2e/install.test.ts index 13612bd..de8a026 100644 --- a/tests/e2e/install.test.ts +++ b/tests/e2e/install.test.ts @@ -321,6 +321,40 @@ test("E2E: Install to Gemini CLI (local)", () => { assert.ok(mcpServers.github); }); +// ============================================ +// E2E Tests: JSON format agents (global install) +// ============================================ + +test("E2E: Install to Windsurf (global) writes global mcp_config.json", () => { + const tempDir = createTempDir(); + const parsed = parseSource("https://mcp.example.com/api"); + const config = buildServerConfig(parsed); + + const originalConfigPath = agents.windsurf.configPath; + agents.windsurf.configPath = join(tempDir, ".codeium", "windsurf", "mcp_config.json"); + + try { + const result = installServerForAgent("example", config, "windsurf", { + cwd: tempDir, + }); + + assert.strictEqual(result.success, true); + + const configPath = join(tempDir, ".codeium", "windsurf", "mcp_config.json"); + assert.strictEqual(existsSync(configPath), true); + + const savedConfig = readJsonConfig(configPath); + const mcpServers = savedConfig.mcpServers as Record; + assert.ok(mcpServers); + + const serverConfig = mcpServers.example as Record; + assert.ok(!("type" in serverConfig)); + assert.strictEqual(serverConfig.url, "https://mcp.example.com/api"); + } finally { + agents.windsurf.configPath = originalConfigPath; + } +}); + // ============================================ // E2E Tests: Merge existing config // ============================================