Add a Remote MCP Server to Windsurf (Devin Desktop)
How to add a remote MCP server to Windsurf, now Devin Desktop: which mcp_config.json its two agents read, url vs serverUrl, keeping the token out of the file, signing in, and fixing one that will not connect.
Windsurf has been called Devin Desktop since 2 June 2026: same editor, new name [1]. A remote MCP server goes into it as one entry with a url in mcp_config.json:
{
"mcpServers": {
"my-server": {
"url": "https://mcp.example.com/mcp"
}
}
}
The user-wide file is ~/.config/devin/mcp_config.json on macOS and Linux, and %APPDATA%\devin\mcp_config.json on Windows [2][3]. Save it and the server's tools become available to the agent.
Everything below is checked against Cognition's Devin Desktop and Devin CLI documentation on 15 October 2026 [1][2][3][4].
TL;DR:
- There are two agents now. New tabs open Devin Local; Cascade is the legacy agent your old conversations still use [4]. They configure MCP differently, and this page covers both.
- Write
url, notserverUrl. Cascade accepts either [2]; Devin Local documents onlyurl[3]. One entry withurlworks for both. - A server that needs sign-in shows Needs auth with an Authenticate button in Devin Local [4].
- Cascade sees at most 100 tools across all servers at once [2].
Which file, for which agent
Older guides, including most of the ones that rank for this search, point to a file under ~/.codeium/. Devin Desktop reads both the legacy Windsurf paths and the new Devin paths during the transition, and writes new data to the Devin paths [1]. So an old config keeps working, and a new one belongs in the Devin location.
| Agent | Where its servers live |
|---|---|
| Devin Local, for you everywhere | ~/.config/devin/mcp_config.json (%APPDATA%\devin\mcp_config.json on Windows) [3] |
| Devin Local, for one project, shared | .devin/mcp_config.json in the repo [3] |
| Devin Local, for one project, just you | .devin/mcp_config.local.json, which is gitignored [3] |
| Cascade | ~/.config/devin/mcp_config.json, opened from the ... menu in the Cascade panel, MCPs, Open MCP config file [2] |
Devin Local shares its configuration with Devin CLI, because it runs on the same agent harness [4]. Versions before v3000.3 kept servers under mcpServers in config.json at the same locations. Newer ones use the dedicated mcp_config.json files and move any old entries over on startup [3].
Add the server
For Devin Local
The entry above is the whole configuration for an open server. Devin Local tries Streamable HTTP first and, if the server answers with a 4xx such as 404 or 405, retries over the legacy SSE transport on the same URL [3]. To skip the attempt, name the transport:
{
"mcpServers": {
"my-server": {
"url": "https://mcp.example.com/mcp",
"transport": "http"
}
}
}
"transport": "sse" is for an old server whose SSE endpoint lives at a different path, such as /sse [3].
From Devin CLI, the same entry is one command, with -s user for every project or -s project to share it through the repo [3]:
devin mcp add -s user my-server https://mcp.example.com/mcp
Devin Local asks before it calls any MCP tool, and you can allow one tool or the whole server, for the session or for good [4]. Cascade does not ask by default [4].
For Cascade
Cascade has no marketplace or one-click install, so you edit the file by hand [2]. Its own example uses serverUrl:
{
"mcpServers": {
"my-server": {
"serverUrl": "https://mcp.example.com/mcp"
}
}
}
Cascade accepts url as well [2], which is why the TL;DR says to write url: it is the one field both agents document.
The URL is the MCP endpoint itself, usually ending in /mcp [2], not the product's homepage or its docs page.
Keep the token out of the file
A server that takes an API key gets a headers object. Cascade expands ${env:NAME} and ${file:/path} in url, serverUrl and headers, so the key can live outside the file [2]:
{
"mcpServers": {
"my-server": {
"url": "https://mcp.example.com/mcp",
"headers": {
"Authorization": "Bearer ${env:MY_SERVER_TOKEN}"
}
}
}
}
An unset variable becomes an empty string [2], so a missing variable produces a request with Bearer and nothing after it, which the server answers with a 401.
For Devin Local the documented place for a personal key is .devin/mcp_config.local.json, which is gitignored: define the server in the shared .devin/mcp_config.json and put the key in the local file [3].
Sign in to a server that needs it
Devin Desktop supports OAuth on every transport [2]. Devin Local prompts you to sign in the first time a server needs it [3], and when stored credentials expire, the server shows Needs auth in the MCP list with an Authenticate button that reruns the browser sign-in [4]. From Devin CLI the same is devin mcp login my-server [3].
Each MCP client keeps its own sign-in, so a server you already authorised in Claude Code or another client needs signing in again here [3].
Most servers let the client register itself during that sign-in. We read the authorization metadata of 169 servers that ask for credentials, from our registry census on 9 October 2026: 162 offered Dynamic Client Registration [5]. For the few that do not, Devin Local takes a pre-registered client [3]:
{
"mcpServers": {
"my-server": {
"url": "https://mcp.example.com/mcp",
"oauthClientId": "your-client-id",
"oauthClientSecret": "${env:MY_MCP_CLIENT_SECRET}"
}
}
}
Some authorization servers reject the RFC 8707 resource parameter Devin Local sends. "oauthResource": "" leaves it out [3]. After changing any of these three fields, sign out and in again, because credentials issued under the old settings are not reused [3]. MCP OAuth covers what a server has to publish for any of this to work.
When it will not connect
| What you see | Usual cause | Fix |
|---|---|---|
| Devin Local does not load the server | The entry has only serverUrl, which Devin Local does not document |
Use url, which both agents read |
| No tools, or an error on the server | The URL is the homepage or docs page | Use the endpoint, usually ending in /mcp |
401 with a key in headers |
${env:NAME} was unset, so the header went out empty |
Set the variable where Devin Desktop starts, or check the name |
Needs auth / Auth required |
The server uses sign-in, or the sign-in expired | Click Authenticate, or devin mcp login |
| Sign-in fails with a registration error | The server only takes pre-registered clients | Add oauthClientId |
Sign-in rejected over resource |
The authorization server refuses RFC 8707 | Set "oauthResource": "" and sign in again |
| A server you configured is blocked | Your team's admin allowlist does not include it | Ask the admin; the allowlist matches the key name in your file, case-sensitively [2] |
| Cascade ignores some tools | More than 100 tools across all servers [2] | Disable some with disabledTools; see how many tools an MCP server should have |
For what the server's own error codes mean, see MCP server errors and MCP server connection failed.
Check it from outside before you share it
Devin Desktop connects from your own machine, so a server on localhost or your VPN works for you and for nobody else. Our MCP connection test calls your server from our network, runs the handshake and tools/list, and reports what a stranger on the internet gets back, with a dated URL you can share. If it passes there and fails in Devin Desktop, the problem is in your config file, not the server.
The same setup in other clients is in Cursor, VS Code, Cline, Claude Code, Codex, Gemini CLI, GitHub Copilot CLI, Claude Desktop and ChatGPT, and remote MCP server vs stdio covers whether your server should have a public address at all.