Integration guide · 2026-06-12 · MCP client configuration
MCP Server Integration Guide: Claude Desktop, Cursor, Cline, Windsurf, and Continue.dev
MCP server integration looks deceptively similar across AI clients — they all use a JSON config with an mcpServers key, and they all support stdio and SSE transports. The similarity ends there. Config files live in different directories, field names diverge (Windsurf uses serverUrl where Cursor uses url), reload behaviors differ (Cline reconnects on save; Claude Desktop requires a full quit-and-relaunch), and the mcpServers value is an array in Continue.dev but an object keyed by server name in every other client. Copy-pasting a working config from one client to another produces silent failures more often than connection errors. This guide consolidates the five major clients — Claude Desktop, Cursor, Cline, Windsurf, and Continue.dev — into a single reference for the config differences that matter.
TL;DR comparison table
| Client | Config file path (macOS) | mcpServers type | Remote SSE field | Reload method |
|---|---|---|---|---|
| Claude Desktop | ~/Library/Application Support/Claude/claude_desktop_config.json |
Object (keyed by name) | type: "sse" + url |
Full quit and relaunch required |
| Cursor | ~/.cursor/mcp.json (global) or .cursor/mcp.json (project) |
Object (keyed by name) | url field (HTTP transport) |
Command palette: MCP: Reload Servers |
| Cline | ~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json |
Object (keyed by name) | url field |
Reconnects immediately on config save |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
Object (keyed by name) | serverUrl field (not url) |
Cascade panel → plug icon → Refresh |
| Continue.dev | ~/.continue/config.json |
Array (not an object) | url field |
Command palette: Continue: Reload config |
The field-name divergence problem
The most common silent failure when connecting an MCP server to a new client is using the wrong field name for the remote endpoint URL. The external surface of the configs looks identical — JSON with an mcpServers key — but the field that points to your SSE endpoint differs by client in ways that produce no parse error and no connection error, just a server that never loads.
Windsurf uses serverUrl. Every other client uses url. If you paste a working Cursor or Cline config into Windsurf's mcp_config.json, the url field is silently ignored — the server entry loads in the panel but connects to nothing. The fix is a one-field rename:
// Cursor / Cline / Continue — uses "url"
{
"mcpServers": {
"my-api": {
"url": "https://api.example.com/sse"
}
}
}
// Windsurf — uses "serverUrl"
{
"mcpServers": {
"my-api": {
"serverUrl": "https://api.example.com/sse"
}
}
}
Continue.dev's mcpServers is an array, not an object. Claude Desktop, Cursor, Cline, and Windsurf all use an object keyed by server name. Continue.dev uses an array of objects where each entry includes a name field:
// Claude Desktop / Cursor / Cline / Windsurf — object keyed by name
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["/absolute/path/to/dist/index.js"]
}
}
}
// Continue.dev — array with name field
{
"mcpServers": [
{
"name": "my-server",
"command": "node",
"args": ["/absolute/path/to/dist/index.js"]
}
]
}
Copy-pasting the object format into Continue.dev produces a JSON schema mismatch. Continue silently skips invalid server entries rather than throwing an error, so the symptom is a missing tool list, not a visible failure.
Claude Desktop uses an explicit transport discriminator. For SSE servers, Claude Desktop requires type: "sse" alongside the url field. Other clients infer the transport from the field type (string URL vs. command/args object). In Claude Desktop, omitting type: "sse" causes the URL to be treated as a command path, which fails differently:
// Claude Desktop — explicit type discriminator required for SSE
{
"mcpServers": {
"my-api": {
"type": "sse",
"url": "https://api.example.com/sse",
"headers": {
"Authorization": "Bearer YOUR_TOKEN"
}
}
}
}
stdio vs. SSE transport: when to use each
Choosing between stdio and SSE (or HTTP) transport is an architectural decision that determines whether your MCP server can be shared, monitored, or accessed from multiple machines. The decision criteria are consistent across all five clients.
Use stdio when: the server runs on the same machine as the client, the server is single-user, and you don't need external monitoring. Every client spawns the stdio server as a subprocess when a session starts and kills it when the session ends. This makes stdio servers invisible to external health checks — they have no URL to probe and are only reachable through the client's subprocess pipe. See MCP server debugging for how to inspect stdio server output when things go wrong.
Use SSE or HTTP transport when: the server runs on a remote host, multiple team members share it, or you need to monitor it externally. An SSE server runs persistently at a URL your clients connect to — it exists independently of any individual client session, which means it can fail between sessions without the client noticing immediately.
The latency difference between the two transports is rarely the deciding factor in practice. A stdio server and a local SSE server have similar round-trip times; a remote SSE server adds network latency but enables sharing and external monitoring. The right question is not "which transport is faster?" but "does this server need to exist when no client is actively connected?"
Stdio servers have one important footgun: absolute paths. Every client spawns the subprocess with a minimal environment that may not inherit your shell's PATH, nvm shims, or pyenv configuration. Use the full path to your runtime binary, not just node or python:
// Fragile — "node" may not resolve in the client's subprocess environment
"command": "node",
"args": ["/path/to/server/dist/index.js"]
// Robust — full path from "which node" or "nvm which node"
"command": "/Users/you/.nvm/versions/node/v20.11.0/bin/node",
"args": ["/path/to/server/dist/index.js"]
This same issue affects all five clients. Claude Desktop on macOS is especially prone to it because the application bundle has a minimal launch environment. The symptom is an MCP log entry like spawn node ENOENT — the runtime binary couldn't be found.
Cross-client error patterns
Three error patterns appear consistently across all five clients, regardless of transport.
stdout contamination
The stdio transport uses stdin/stdout as the communication channel. Any console.log() or process.stdout.write() call in your server handler writes to the same pipe that carries MCP JSON-RPC messages. The client receives interleaved text and JSON, fails to parse the stream, and surfaces an opaque connection error. The fix is to redirect all diagnostic output to stderr:
// Breaks stdio transport — pollutes the JSON-RPC pipe
console.log("Tool called:", args);
// Safe — stderr goes to the client's diagnostic log, not the pipe
console.error("Tool called:", args);
This pattern is the single most common cause of working MCP servers failing when connected to Claude Desktop, Cursor, Cline, or Windsurf. The MCP server debugging guide covers how to inspect the MCP log in each client to confirm stdout contamination as the cause.
Environment variable injection
All five clients support an env block in the server config for injecting secrets into the subprocess environment. This is the correct way to pass API keys, database URLs, and tokens to stdio servers — the values live only in the local config file, not in the server process's hard-coded arguments:
{
"mcpServers": {
"my-server": {
"command": "/usr/local/bin/node",
"args": ["/path/to/dist/index.js"],
"env": {
"DATABASE_URL": "postgresql://localhost/mydb",
"OPENAI_API_KEY": "sk-..."
}
}
}
}
For remote SSE/HTTP servers, secrets go in the headers block rather than env, since the server runs remotely and doesn't inherit a subprocess environment from the client:
// Claude Desktop SSE with auth header
{
"mcpServers": {
"my-api": {
"type": "sse",
"url": "https://api.example.com/sse",
"headers": {
"Authorization": "Bearer YOUR_TOKEN"
}
}
}
}
Cline's config uses a requestOptions.headers nesting; the other clients use a flat headers object directly on the server entry. Consult the Cline integration guide for the exact nesting when setting auth headers there.
JSON syntax errors in the config file
All five clients parse the config file with strict JSON — trailing commas, comments, and unquoted keys produce a parse failure that silently drops all MCP servers rather than reporting a useful error. The symptom is an empty tool list with no error message. Validate your config file with a JSON linter before assuming the server is at fault. A missing brace in a large multi-server config file can disable every server configured in that file.
Reload behaviors and when they matter
How quickly a client picks up config changes affects the development iteration loop for server authors and the operational impact of config mistakes.
Cline has the fastest reload: it watches the settings file and reconnects to MCP servers immediately when the file is saved, without requiring VS Code to restart. This makes Cline the most convenient client for iterative server development — you can edit the config, switch to VS Code, and see the updated connection status in the MCP panel within seconds.
Cursor and Continue.dev support a command-palette reload (MCP: Reload Servers and Continue: Reload config respectively) that picks up config changes without restarting the IDE. The reload is manual but fast.
Windsurf requires a manual refresh via the Cascade panel's plug icon, but does not require an IDE restart. The Cascade panel also exposes a "MCP: Restart All Servers" command for force-reconnecting without editing the config — useful when a remote server recovers from a crash and the client needs to re-establish the session.
Claude Desktop requires a full quit-and-relaunch to pick up config changes. This is the slowest reload cycle of the five clients. The practical implication: if you ship a bad config to Claude Desktop (wrong path, missing type discriminator), you need a full restart to recover — you cannot hot-fix it. Keep a known-good backup of the config file before making changes.
For remote SSE servers, the reload behavior is less critical during development because the server runs independently of any client. What matters more is whether the client reconnects automatically after a server restart. Cline and Windsurf attempt to reconnect on a backoff schedule. Claude Desktop and Cursor surface a connection error in the UI and require a manual reload or restart to try again after a server returns from downtime.
Client-specific features worth knowing
Beyond the config format differences, three client-specific features change how you configure servers in practice.
Cline auto-approve lets you skip the per-call confirmation dialog for specific tools. The autoApprove array in the server config lists tool names that Cline calls without prompting:
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["/path/to/dist/index.js"],
"autoApprove": ["search_files", "read_document", "list_tables"]
}
}
}
Limit auto-approve to read-only, idempotent tools. Any tool that writes data, sends messages, or modifies state should stay under manual approval — the agentic loop context means Cline may call tools dozens of times in sequence, and a misconfigured auto-approved write tool can cause significant unintended side effects.
Cursor's project-scoped config (.cursor/mcp.json in the project root) makes server configuration portable across a team. Team members who clone the repo get the MCP config automatically. The caveat: project-scoped configs that contain secrets should be in .gitignore. Commit a .cursor/mcp.json.example with placeholder values so teammates know what to fill in.
Continue.dev's config.ts alternative lets you programmatically construct the MCP server list using environment variables or file system checks — useful for monorepos where different branches or projects need different server configurations without editing a JSON file:
// ~/.continue/config.ts
export default {
mcpServers: process.env.USE_STAGING
? [{ name: "api-staging", url: process.env.STAGING_MCP_URL }]
: [{ name: "api-prod", url: process.env.PROD_MCP_URL }]
};
The monitoring gap all five clients share
Every client in this comparison shows server status only while the client application is open and you're actively looking at the MCP panel. A remote server that crashes at 3 AM, has its TLS certificate expire, or starts returning protocol errors for a subset of tool calls does not produce any notification in any client until a user opens the application, navigates to the MCP panel, and happens to observe the red indicator — or until the LLM tries to call a tool during a session and the call fails.
This is a fundamental limitation of in-client status indicators, not a bug in any specific client. The indicator tells you whether the client's current session has a live connection. It doesn't tell you about connection health between sessions, protocol correctness (a server can return HTTP 200 but fail the MCP initialize handshake), TLS certificate validity, or degradation in a subset of tool responses.
The MCP server health check problem is more specific than a generic HTTP uptime check. A server can pass GET /healthz and still fail the MCP initialize handshake if a dependency used by the tools endpoint is down. A valid HTTP 200 response is necessary but not sufficient to confirm that your server is ready to serve MCP tool calls.
For servers that more than one person depends on — team-shared servers, publicly listed registry entries, servers powering autonomous agent workflows — external protocol monitoring fills the gap that in-client indicators leave open. AliveMCP probes the full MCP initialize handshake from external infrastructure on a 60-second interval, alerts via Slack or email when the handshake fails, and provides a status history visible without opening any IDE. Adding your server takes under a minute: paste the SSE endpoint URL and select your alert channel.
The monitoring gap is uniform across all five clients. The right fix is not to pick a client with better status reporting — it's to run an external probe that doesn't depend on any client being open.
See the SSE transport guide for how external probes interact with the SSE handshake and what a failing initialize looks like from the protocol level.
Setup checklist for a new MCP server
A practical sequence for connecting a new server to multiple clients without the silent failures described above:
- Start with one client. Get the server working in Cline first — its immediate reload on config save makes iteration the fastest. Confirm the server appears with a green dot and tools are listed.
- Test with the MCP inspector. Run
npx @modelcontextprotocol/inspectoragainst the server to verify the initialize handshake and tool list before connecting any client. This isolates the server from client-specific config issues. - Fix stdout contamination before adding clients. Run the server manually with
node dist/index.jsand verify no output goes to stdout during normal operation. Any stdout output will break the stdio pipe silently. - Use absolute paths in all configs. Run
which node(ornvm which node) and use the full path. Do not rely on PATH resolution in the client's subprocess environment. - Adapt the config for each client. Use the comparison table at the top of this guide as a checklist: verify the
mcpServerstype (object vs. array for Continue.dev), the remote URL field name (serverUrlfor Windsurf,urlfor others), and the transport discriminator (type: "sse"for Claude Desktop). - Add external monitoring before sharing with teammates. Once the server is team-shared, add it to AliveMCP so failures surface via alert rather than via a confused teammate with an empty tool list.
Related guides
- Configure MCP server in Claude Desktop — config file, stdio, SSE
- MCP server with Cursor IDE — global and project-scoped config
- MCP server with Cline — auto-approve, settings file, immediate reload
- MCP server with Windsurf — serverUrl field, Cascade integration
- MCP server with Continue.dev — array config format, config.ts
- MCP server debugging — reading client logs, inspector, common errors
- MCP server health check — what HTTP probes miss at the protocol level
- MCP server SSE transport — session affinity, keep-alives, CORS
- MCP Server Transports Guide: stdio, SSE, and Streamable HTTP
Monitor your MCP server across all clients
Every client in this guide shows server status only while the app is open. Add your server to AliveMCP and get protocol-level health checks every 60 seconds — with Slack and email alerts when the initialize handshake fails, independent of which client is running.
Add your server to AliveMCP