feat: add Windsurf to MCP one-click install (#8749)
* Added Windsurf icon * Added Windsurf as option to mcpServerTab * Added Windsurf instalation on backend
This commit is contained in:
parent
3b7b701813
commit
04f8f90649
7 changed files with 66 additions and 2 deletions
|
|
@ -327,7 +327,7 @@ async def install_mcp_config(
|
|||
request: Request,
|
||||
current_user: CurrentActiveMCPUser,
|
||||
):
|
||||
"""Install MCP server configuration for Cursor or Claude."""
|
||||
"""Install MCP server configuration for Cursor, Windsurf, or Claude."""
|
||||
# Check if the request is coming from a local IP address
|
||||
client_ip = get_client_ip(request)
|
||||
if not is_local_ip(client_ip):
|
||||
|
|
@ -408,6 +408,8 @@ async def install_mcp_config(
|
|||
# Determine the config file path based on the client and OS
|
||||
if body.client.lower() == "cursor":
|
||||
config_path = Path.home() / ".cursor" / "mcp.json"
|
||||
elif body.client.lower() == "windsurf":
|
||||
config_path = Path.home() / ".codeium" / "windsurf" / "mcp_config.json"
|
||||
elif body.client.lower() == "claude":
|
||||
if os_type == "Darwin": # macOS
|
||||
config_path = Path.home() / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json"
|
||||
|
|
@ -502,7 +504,7 @@ async def check_installed_mcp_servers(
|
|||
project_id: UUID,
|
||||
current_user: CurrentActiveMCPUser,
|
||||
):
|
||||
"""Check if MCP server configuration is installed for this project in Cursor or Claude."""
|
||||
"""Check if MCP server configuration is installed for this project in Cursor, Windsurf, or Claude."""
|
||||
try:
|
||||
# Verify project exists and user has access
|
||||
async with session_scope() as session:
|
||||
|
|
@ -544,6 +546,27 @@ async def check_installed_mcp_servers(
|
|||
except json.JSONDecodeError:
|
||||
logger.warning("Failed to parse Cursor config JSON at: %s", cursor_config_path)
|
||||
|
||||
# Check Windsurf configuration
|
||||
windsurf_config_path = Path.home() / ".codeium" / "windsurf" / "mcp_config.json"
|
||||
logger.debug(
|
||||
"Checking Windsurf config at: %s (exists: %s)", windsurf_config_path, windsurf_config_path.exists()
|
||||
)
|
||||
if windsurf_config_path.exists():
|
||||
try:
|
||||
with windsurf_config_path.open("r") as f:
|
||||
windsurf_config = json.load(f)
|
||||
if "mcpServers" in windsurf_config and project_server_name in windsurf_config["mcpServers"]:
|
||||
logger.debug("Found Windsurf config for project server: %s", project_server_name)
|
||||
results.append("windsurf")
|
||||
else:
|
||||
logger.debug(
|
||||
"Windsurf config exists but no entry for server: %s (available servers: %s)",
|
||||
project_server_name,
|
||||
list(windsurf_config.get("mcpServers", {}).keys()),
|
||||
)
|
||||
except json.JSONDecodeError:
|
||||
logger.warning("Failed to parse Windsurf config JSON at: %s", windsurf_config_path)
|
||||
|
||||
# Check Claude configuration
|
||||
claude_config_path = None
|
||||
os_type = platform.system()
|
||||
|
|
|
|||
18
src/frontend/src/icons/Windsurf/Windsurf.jsx
Normal file
18
src/frontend/src/icons/Windsurf/Windsurf.jsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { stringToBool } from "@/utils/utils";
|
||||
|
||||
const SvgWindsurf = (props) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="512"
|
||||
height="297"
|
||||
viewBox="0 0 512 297"
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M507.28 0.142623H502.4C476.721 0.10263 455.882 20.899 455.882 46.5745V150.416C455.882 171.153 438.743 187.95 418.344 187.95C406.224 187.95 394.125 181.851 386.945 171.613L280.889 20.1391C272.089 7.56133 257.77 0.0626373 242.271 0.0626373C218.091 0.0626373 196.332 20.6191 196.332 45.9946V150.436C196.332 171.173 179.333 187.97 158.794 187.97C146.634 187.97 134.555 181.871 127.375 171.633L8.69966 2.12228C6.01976 -1.71705 0 0.182617 0 4.8618V95.426C0 100.005 1.39995 104.444 4.01984 108.204L120.815 274.995C127.715 284.853 137.895 292.172 149.634 294.831C179.013 301.51 206.052 278.894 206.052 250.079V145.697C206.052 124.961 222.851 108.164 243.59 108.164H243.65C256.15 108.164 267.87 114.263 275.049 124.501L381.125 275.955C389.945 288.552 403.524 296.031 419.724 296.031C444.443 296.031 465.622 275.455 465.622 250.099V145.677C465.622 124.941 482.421 108.144 503.16 108.144H507.3C509.9 108.144 512 106.044 512 103.445V4.8418C512 2.24226 509.9 0.142623 507.3 0.142623H507.28Z"
|
||||
fill={stringToBool(props.isdark) ? "white" : "black"}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default SvgWindsurf;
|
||||
11
src/frontend/src/icons/Windsurf/index.tsx
Normal file
11
src/frontend/src/icons/Windsurf/index.tsx
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { useDarkStore } from "@/stores/darkStore";
|
||||
import React, { forwardRef } from "react";
|
||||
import SvgWindsurf from "./Windsurf";
|
||||
|
||||
export const WindsurfIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
const isdark = useDarkStore((state) => state.dark).toString();
|
||||
return <SvgWindsurf ref={ref} isdark={isdark} {...props} />;
|
||||
});
|
||||
3
src/frontend/src/icons/Windsurf/windsurf.svg
Normal file
3
src/frontend/src/icons/Windsurf/windsurf.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="297" viewBox="0 0 512 297" fill="none">
|
||||
<path d="M507.28 0.142623H502.4C476.721 0.10263 455.882 20.899 455.882 46.5745V150.416C455.882 171.153 438.743 187.95 418.344 187.95C406.224 187.95 394.125 181.851 386.945 171.613L280.889 20.1391C272.089 7.56133 257.77 0.0626373 242.271 0.0626373C218.091 0.0626373 196.332 20.6191 196.332 45.9946V150.436C196.332 171.173 179.333 187.97 158.794 187.97C146.634 187.97 134.555 181.871 127.375 171.633L8.69966 2.12228C6.01976 -1.71705 0 0.182617 0 4.8618V95.426C0 100.005 1.39995 104.444 4.01984 108.204L120.815 274.995C127.715 284.853 137.895 292.172 149.634 294.831C179.013 301.51 206.052 278.894 206.052 250.079V145.697C206.052 124.961 222.851 108.164 243.59 108.164H243.65C256.15 108.164 267.87 114.263 275.049 124.501L381.125 275.955C389.945 288.552 403.524 296.031 419.724 296.031C444.443 296.031 465.622 275.455 465.622 250.099V145.677C465.622 124.941 482.421 108.144 503.16 108.144H507.3C509.9 108.144 512 106.044 512 103.445V4.8418C512 2.24226 509.9 0.142623 507.3 0.142623H507.28Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -112,6 +112,7 @@ import { WolframIcon } from "@/icons/Wolfram";
|
|||
import { XAIIcon } from "@/icons/xAI";
|
||||
import { YouTubeSvgIcon as YouTubeIcon } from "@/icons/Youtube";
|
||||
import { ZepMemoryIcon } from "@/icons/ZepMemory";
|
||||
import { WindsurfIcon } from "./Windsurf";
|
||||
|
||||
// Export the eagerly loaded icons map
|
||||
export const eagerIconsMapping = {
|
||||
|
|
@ -225,6 +226,7 @@ export const eagerIconsMapping = {
|
|||
WatsonxAI: WatsonxAiIcon,
|
||||
Weaviate: WeaviateIcon,
|
||||
Wikipedia: WikipediaIcon,
|
||||
Windsurf: WindsurfIcon,
|
||||
Wolfram: WolframIcon,
|
||||
xAI: XAIIcon,
|
||||
YouTube: YouTubeIcon,
|
||||
|
|
|
|||
|
|
@ -296,6 +296,8 @@ export const lazyIconsMapping = {
|
|||
import("@/icons/Wikipedia/Wikipedia").then((mod) => ({
|
||||
default: mod.default,
|
||||
})),
|
||||
Windsurf: () =>
|
||||
import("@/icons/Windsurf").then((mod) => ({ default: mod.WindsurfIcon })),
|
||||
Wolfram: () =>
|
||||
import("@/icons/Wolfram/Wolfram").then((mod) => ({ default: mod.default })),
|
||||
xAI: () => import("@/icons/xAI").then((mod) => ({ default: mod.XAIIcon })),
|
||||
|
|
|
|||
|
|
@ -102,6 +102,11 @@ const autoInstallers = [
|
|||
title: "Claude",
|
||||
icon: "Claude",
|
||||
},
|
||||
{
|
||||
name: "windsurf",
|
||||
title: "Windsurf",
|
||||
icon: "Windsurf",
|
||||
},
|
||||
];
|
||||
|
||||
const operatingSystemTabs = [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue