Skip to content

Commit 89de218

Browse files
slimslenderslackscolinmcneil
authored andcommitted
add support for windsurf
1 parent d0fc33b commit 89de218

File tree

4 files changed

+128
-0
lines changed

4 files changed

+128
-0
lines changed
Lines changed: 5 additions & 0 deletions
Loading

src/extension/ui/src/components/tabs/YourClients.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import ChatGPTIcon from '../../assets/chatgpt.svg';
2323
import ClaudeIcon from '../../assets/claude-ai-icon.svg';
2424
import CursorIcon from '../../assets/cursor.svg';
2525
import GordonIcon from '../../assets/gordon-icon.png';
26+
import WindsurfIcon from '../../assets/windsurf.svg';
2627
import { CATALOG_LAYOUT_SX, DOCKER_MCP_COMMAND } from '../../Constants';
2728

2829
// Initialize the Docker Desktop client
@@ -36,6 +37,7 @@ const iconMap = {
3637
'Claude Desktop': ClaudeIcon,
3738
Gordon: GordonIcon,
3839
Cursor: CursorIcon,
40+
Windsurf: WindsurfIcon,
3941
};
4042

4143
const MCPClientSettings = ({ appProps }: MCPClientSettingsProps) => {
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { v1 } from "@docker/extension-api-client-types";
2+
import { escapeJSONForPlatformShell, getUser } from "../FileUtils";
3+
import { MCPClient, SAMPLE_MCP_CONFIG } from "./MCPTypes";
4+
import { DOCKER_MCP_COMMAND } from "../Constants";
5+
import { mergeDeep } from "../MergeDeep";
6+
7+
class WindsurfDesktopClient implements MCPClient {
8+
name = 'Windsurf';
9+
url = 'https://windsurf.com/downloads';
10+
manualConfigSteps = [
11+
'Open <strong>Windsurf Settings</strong>',
12+
'Click on the <strong>MCP</strong> tab',
13+
'Click on the <strong>Add new MCP server</strong> button',
14+
'Set name: <code>MCP_DOCKER</code>',
15+
'Set command: <pre style="font-family: monospace; white-space: nowrap; overflow: auto; width: 80%; background-color: grey.200; padding: 1; border-radius: 1; font-size: 12px;">' +
16+
DOCKER_MCP_COMMAND +
17+
'</pre>'
18+
];
19+
expectedConfigPath = {
20+
darwin: '$HOME/.codeium/mcp_config.json',
21+
linux: '$HOME/.codeium/mcp_config.json',
22+
win32: '$USERPROFILE\\.codeium\\mcp_config.json'
23+
};
24+
readConfig = async (client: v1.DockerDesktopClient) => {
25+
const platform = client.host.platform as keyof typeof this.expectedConfigPath;
26+
const configPath = this.expectedConfigPath[platform].replace('$USER', await getUser(client));
27+
try {
28+
const result = await client.docker.cli.exec('run', ['--rm', '--mount', `type=bind,source=${configPath},target=/codeium_config/mcp_config.json`, 'alpine:latest', 'cat', '/codeium_config/mcp_config.json']);
29+
return {
30+
content: result.stdout,
31+
path: configPath
32+
};
33+
} catch (e) {
34+
return {
35+
content: null,
36+
path: configPath
37+
};
38+
}
39+
};
40+
connect = async (client: v1.DockerDesktopClient) => {
41+
const config = await this.readConfig(client);
42+
let windsurfConfig = null;
43+
try {
44+
windsurfConfig = JSON.parse(config.content || '{}') as typeof SAMPLE_MCP_CONFIG;
45+
if (windsurfConfig.mcpServers?.MCP_DOCKER) {
46+
client.desktopUI.toast.success('Windsurf MCP server already connected.');
47+
return;
48+
}
49+
} catch (e) {
50+
windsurfConfig = mergeDeep({}, SAMPLE_MCP_CONFIG);
51+
}
52+
const payload = mergeDeep(windsurfConfig, SAMPLE_MCP_CONFIG);
53+
try {
54+
await client.docker.cli.exec('run',
55+
[
56+
'--rm',
57+
'--mount',
58+
`type=bind,source="${config.path}",target=/codeium_config/mcp_config.json`,
59+
'--workdir',
60+
'/codeium_config', 'vonwig/function_write_files:latest',
61+
escapeJSONForPlatformShell({ files: [{ path: 'mcp_config.json', content: JSON.stringify(payload) }] }, client.host.platform)
62+
]
63+
);
64+
client.desktopUI.toast.success('Connected Docker MCP Server to Windsurf.');
65+
} catch (e) {
66+
if ((e as any).stderr) {
67+
client.desktopUI.toast.error((e as any).stderr);
68+
} else {
69+
client.desktopUI.toast.error((e as Error).message);
70+
}
71+
}
72+
};
73+
disconnect = async (client: v1.DockerDesktopClient) => {
74+
const config = await this.readConfig(client);
75+
if (!config.content) {
76+
client.desktopUI.toast.error('No config found');
77+
return;
78+
}
79+
let windsurfConfig = null;
80+
try {
81+
windsurfConfig = JSON.parse(config.content) as typeof SAMPLE_MCP_CONFIG;
82+
if (!windsurfConfig.mcpServers?.MCP_DOCKER) {
83+
client.desktopUI.toast.error('Docker MCP Server not connected to Windsurf');
84+
return;
85+
}
86+
} catch (e) {
87+
client.desktopUI.toast.error('Failed to disconnect. Invalid Windsurf config found at ' + config.path);
88+
return;
89+
}
90+
const payload = {
91+
...windsurfConfig,
92+
mcpServers: Object.fromEntries(Object.entries(windsurfConfig.mcpServers).filter(([key]) => key !== 'MCP_DOCKER'))
93+
};
94+
try {
95+
await client.docker.cli.exec('run',
96+
[
97+
'--rm',
98+
'--mount',
99+
`type=bind,source="${config.path}",target=/codeium_config/mcp_config.json`,
100+
'--workdir',
101+
'/codeium_config', 'vonwig/function_write_files:latest',
102+
escapeJSONForPlatformShell({ files: [{ path: 'mcp_config.json', content: JSON.stringify(payload) }] }, client.host.platform)
103+
]
104+
);
105+
} catch (e) {
106+
if ((e as any).stderr) {
107+
client.desktopUI.toast.error((e as any).stderr);
108+
} else {
109+
client.desktopUI.toast.error((e as Error).message);
110+
}
111+
}
112+
};
113+
validateConfig = (content: string) => {
114+
const config = JSON.parse(content || '{}') as typeof SAMPLE_MCP_CONFIG;
115+
return !!config.mcpServers?.MCP_DOCKER;
116+
};
117+
}
118+
119+
export default new WindsurfDesktopClient();

src/extension/ui/src/mcp-clients/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
import Cursor from "./Cursor";
99
import ClaudeDesktop from "./ClaudeDesktop";
1010
import Gordon from "./Gordon";
11+
import Windsurf from "./Windsurf";
1112
import { MCPClient } from "./MCPTypes";
1213

1314
export const SUPPORTED_MCP_CLIENTS: MCPClient[] = [
1415
Gordon,
1516
ClaudeDesktop,
1617
Cursor,
18+
Windsurf,
1719
]

0 commit comments

Comments
 (0)