Guide · Windsurf IDE

MCP server with Windsurf IDE — Cascade MCP plugin configuration

Windsurf (by Codeium) is an AI-native IDE built around Cascade, its agentic AI assistant. Cascade supports MCP, letting you extend it with custom tools from any MCP-compatible server. Windsurf's MCP integration is managed through a config file similar to Claude Desktop's, with the same mcpServers JSON structure. The main differences from other clients are the config file location, Windsurf's plugin system for browsing available MCP servers, and how Cascade decides to invoke tools during agentic flows. This guide covers all of that, plus how to monitor remote servers you connect to Windsurf.

TL;DR

Edit the Windsurf MCP config file at ~/.codeium/windsurf/mcp_config.json (macOS/Linux) or %USERPROFILE%\.codeium\windsurf\mcp_config.json (Windows). Add your server under the mcpServers key using command + args for local stdio servers, or serverUrl for remote SSE servers. Open Windsurf's Cascade panel, click the plug icon, and hit "Refresh" to pick up new servers without restarting Windsurf. Verify the server appears with a green status indicator. For remote servers, add the URL to AliveMCP for continuous external monitoring.

Config file location

Windsurf stores its MCP configuration in a directory under your home folder:

If the file doesn't exist, create it. The ~/.codeium/windsurf/ directory is created automatically on first Windsurf launch — you only need to create the mcp_config.json file itself.

The file uses the same top-level structure as Claude Desktop and Cursor:

{
  "mcpServers": {
    "my-server": {
      "command": "node",
      "args": ["/path/to/dist/index.js"]
    }
  }
}

Windsurf also has a built-in MCP plugin marketplace in the Cascade panel (the plug icon) where you can browse and install community-maintained MCP servers. These get added to your mcp_config.json automatically. Custom servers you build always go in the same file, under the same mcpServers key.

Local stdio server setup

Stdio transport is best for servers running on the same machine. Windsurf spawns the server as a subprocess and pipes MCP messages through stdin/stdout:

{
  "mcpServers": {
    "my-dev-tools": {
      "command": "/usr/local/bin/node",
      "args": ["/Users/you/projects/dev-tools-mcp/dist/index.js"],
      "env": {
        "NODE_ENV": "production",
        "DATABASE_URL": "sqlite:///Users/you/.local/share/dev-tools/db.sqlite",
        "LOG_LEVEL": "warn"
      }
    }
  }
}

Use absolute paths — Windsurf's subprocess environment may not include the same PATH as your interactive shell, particularly if you use version managers like nvm, fnm, or asdf. Confirm the node path with which node in a terminal, then use that full path in command.

For development workflows where you want to iterate on the server code quickly:

{
  "mcpServers": {
    "my-dev-server": {
      "command": "/usr/local/bin/npx",
      "args": ["tsx", "--watch", "/Users/you/projects/my-server/src/index.ts"]
    }
  }
}

The --watch flag restarts tsx when source files change, so edits to your server code take effect on the next MCP connection without manually refreshing in Windsurf. Note that this causes the subprocess to restart mid-session if you save a file — for production, use a compiled and stable build, not a watch-mode runner.

Remote SSE server setup

For servers deployed to a remote host, use the serverUrl field (Windsurf's field name for SSE endpoint URLs — note this differs slightly from Cursor's url field):

{
  "mcpServers": {
    "remote-api-tools": {
      "serverUrl": "https://my-mcp-server.fly.dev/sse"
    }
  }
}

For servers that require authentication headers:

{
  "mcpServers": {
    "authenticated-tools": {
      "serverUrl": "https://my-mcp-server.example.com/sse",
      "headers": {
        "Authorization": "Bearer your-api-key",
        "X-Workspace-Id": "your-workspace-id"
      }
    }
  }
}

Windsurf sends the headers with the initial SSE connection and with every subsequent POST request during the session. The server can use request headers to authenticate and associate the session with a specific user or workspace.

The remote server must implement proper CORS headers if Windsurf makes any cross-origin requests (this is less common with SSE transport but relevant if your server also serves a web UI). See MCP server CORS configuration for the specific headers required.

Refreshing servers without restarting

Windsurf doesn't require a full restart to pick up MCP config changes. After editing mcp_config.json:

  1. Open the Cascade panel in Windsurf
  2. Click the plug icon (MCP Servers)
  3. Click "Refresh" or the reload button at the top of the MCP panel

Windsurf re-reads the config file and reconnects to all servers. New servers appear immediately; removed servers drop from the list. For stdio servers, this kills and respawns the subprocess. For SSE servers, Windsurf closes and reopens the connection.

Alternatively, Windsurf's command palette (Cmd+Shift+P on Mac) has an "MCP: Restart All Servers" command that does the same thing.

How Cascade uses MCP tools

Cascade is Windsurf's agentic AI layer. Unlike standard chat UIs, Cascade autonomously decides when to call MCP tools based on the task context — you don't need to explicitly tell it to use a specific tool. Cascade sees all tools from all connected MCP servers and selects the ones relevant to the current task.

To make your tools discoverable by Cascade, write clear tool descriptions in your server's tools/list response. The description is the primary signal Cascade uses to decide whether to invoke a tool:

server.tool(
  "search_codebase",
  {
    query: z.string().describe("Natural language search query for code"),
    file_pattern: z.string().optional().describe("Glob pattern to restrict search (e.g. '**/*.ts')")
  },
  "Search the codebase for code matching a description. Use when the user asks to find code, understand how something is implemented, or locate where a feature lives.",
  async ({ query, file_pattern }) => { /* ... */ }
);

The description "Use when the user asks to..." explicitly tells the model when the tool is appropriate. Without this framing, Cascade has to infer usage from the tool name and parameter schema alone, which leads to under-use or inappropriate use. See MCP server tool design for a deeper guide on writing effective tool descriptions.

Troubleshooting connection issues

If a server isn't connecting in Windsurf's MCP panel:

Check the server status indicator: A red dot means the server failed to connect. Hover over it (or click the server name) to see the error message Windsurf captured.

For stdio servers — test manually first:

npx @modelcontextprotocol/inspector node /path/to/dist/index.js

The MCP inspector runs the same initialization sequence as Windsurf and shows exactly where it fails. Common issues: missing compiled output (run npm run build), wrong node version (server requires Node 18+, system has 16), or unhandled async errors during startup.

For SSE servers — test the endpoint directly:

curl -N -H "Accept: text/event-stream" https://your-server/sse

If this returns a proper event stream, Windsurf should connect. If it returns HTML or an error, the server's SSE route is misconfigured or the server isn't running.

stdout contamination: Windsurf uses stdout as the MCP message channel for stdio servers. Any output to stdout that isn't valid JSON-RPC (a console.log call, a startup message, a module that prints to stdout) corrupts the stream and causes "invalid JSON" or "unexpected token" errors. Redirect all application logging to stderr.

See MCP server debugging for a systematic troubleshooting approach with the MCP protocol inspector.

Monitoring remote MCP servers in Windsurf

Windsurf's MCP panel shows server status only while Windsurf is running and you're looking at the panel. Remote servers that fail between coding sessions don't produce notifications in Windsurf — the failure surfaces only when Cascade tries to call a tool and gets a connection error.

For remote servers, add the SSE endpoint URL to AliveMCP. AliveMCP monitors from an external network, continuously verifying the full initializetools/list handshake and alerting on failures — including TLS certificate expiry, DNS failures, and deployment regressions that kill the MCP protocol without taking down the HTTP server entirely. You catch issues before Cascade does.

See MCP server SSL certificate monitoring and MCP server observability for complementary monitoring approaches.

Related questions

Does Windsurf's MCP integration work with the JetBrains plugin version?

Windsurf is currently a standalone IDE (VS Code fork). Codeium also offers a JetBrains plugin, but the MCP integration is specific to the Windsurf IDE as of this writing. If you use the JetBrains plugin, check Codeium's changelog for MCP support status — it may be added in a later release. For JetBrains IDE users who need MCP support today, Cline-compatible alternatives or direct API integration may be more appropriate.

Can I use the same MCP server with Windsurf and Claude Desktop simultaneously?

Yes, for SSE servers — multiple clients can connect to the same SSE endpoint concurrently. Each connection gets its own session ID and MCP session state. For stdio servers, each client spawns its own subprocess, so both Windsurf and Claude Desktop would run separate instances of your server simultaneously. This is fine as long as your server handles concurrent instances safely (e.g., uses file locking for SQLite databases or connection pooling for Postgres). See MCP server connection pooling for managing concurrent connections.

How do I pass Windsurf workspace context to my MCP server?

Windsurf doesn't automatically inject workspace metadata into MCP tool calls. If your server needs to know which project is open, pass context via tool call parameters — design your tools to accept an optional workspace_path or project_root parameter. Alternatively, use a project-scoped config file approach: create a .windsurf/mcp.json (if Windsurf supports project-scoped config — check current docs) with project-specific env vars injected into the server startup, so the server knows its context at startup rather than per-call.

What happens to in-flight tool calls when the Windsurf MCP config changes?

When you refresh MCP servers in Windsurf, active Cascade sessions that are mid-tool-call may experience interruption. The behavior depends on whether the tool call has already been sent to the server (in which case the server completes it even if the Windsurf connection drops) or is still queued (in which case it's dropped). For production workflows, refresh MCP servers between Cascade sessions rather than during active task execution. Windsurf displays an error in the Cascade output if a tool call is interrupted by a reconnect.

Further reading