Blog
Protocol·8 min read

Where Every MCP Client Keeps Its Config

The protocol is standardised. The config file is not. A field guide to mcpServers vs servers vs mcp, project scope vs global scope, and the one file more than one vendor agreed to read.

You add the same MCP server to two different tools and end up with two files, in two places, with two different key names, one of which silently does nothing. This is not you doing it wrong. It is the actual state of the ecosystem, and it has a specific, understandable cause.

The protocol is standard. The config file never was.

MCP was introduced by Anthropic in late 2024 and released as an open standard. What that standard covers is the wire protocol: how a client and a server talk to each other, how tools are listed and called, which transports are allowed.

What it has never covered is how a client should store its list of servers on disk. That was left to each client, and each client duly invented its own. The first widely copied shape came from Claude Desktop, whose config used a top-level mcpServers object. Most tools followed it, because it was the only one that existed. Some did not.

Two questions people keep merging into one

Almost all the confusion comes from treating these as a single question when they are independent:

  • Which file does the client read? This decides the scope. A file inside the repository applies to that project and can be committed and shared. A file in your home directory applies to every project on your machine and belongs to you alone.
  • What shape does the content have? This decides what you paste: the root key, what the address field is called, and whether the file is JSON or TOML.

For nearly every tool, the two files, project and global, hold exactly the same content. You paste the same block; only the location changes. Which is why the fragmentation is annoying rather than hard: there are fewer real formats than there are file paths.

The map

Correct as of writing. This moves, so check your tool if something does not connect.

ClientIn the projectGlobalRoot keyAddress
VS Code.vscode/mcp.jsonuser profile mcp.jsonserversurl + type: "http"
Claude Code.mcp.json~/.claude.jsonmcpServersurl
GitHub Copilot CLI.mcp.json~/.copilot/mcp-config.jsonmcpServersurl
Cursor.cursor/mcp.json~/.cursor/mcp.jsonmcpServersurl
Antigravity 2.0 / IDE / CLInone~/.gemini/config/mcp_config.jsonmcpServersserverUrl
opencodeopencode.json~/.config/opencode/opencode.jsonmcpurl + type: "remote"
Codexnone~/.codex/config.tomlmcp_servers (TOML)url
Zed.zed/settings.jsonsettings.jsoncontext_serversnone

Highlighted cells deviate from the most common shape. Note that they are not a minority.

The one file more than one vendor reads

Look at the file names. .vscode/mcp.json and .cursor/mcp.json live inside a folder named after a product. They are private by construction: that file is read by that program and nothing else. If you use Cursor and a teammate uses VS Code, you keep two files with identical contents and you both have to remember to update them.

.mcp.json sits in the repository root and carries no vendor name. That is the entire point. Today it is read by Claude Code and by the VS Code Agent Host with GitHub Copilot CLI: two competing vendors, one file. Microsoft has actively pushed Copilot CLI users to migrate their config away from .vscode/mcp.json towards it.

This is what “portable” means here, and it is worth being precise about the limits. The content is not special. It is the same mcpServers object Cursor uses. What is portable is the location: a neutral name nobody claims. And it is portable among the clients that chose to read it, not universally. Nothing obliges a new client to look there.

If you are setting up a repository for a team, this is the file to write. It gets committed with the code, and anyone who clones the project has the server already configured regardless of which tool they open it with.

The exceptions, and why they are not arbitrary

VS Code uses servers in its own file, and requires the transport to be declared explicitly with a type field. There is a long-running issue asking it to accept the common key as well; the report lists seven other clients that all use mcpServers and points out that install scripts have to special-case VS Code. It remains the single most common cause of a config that looks right and does nothing.

Antigravity keeps the common root key but calls the address serverUrl instead of url, a one-word difference that is very easy to miss. In exchange it is unusually tidy elsewhere: since 2.x, the desktop builder, the IDE and the CLI all share a single config, so you set the server up once for all three. It has no project-level file at all.

opencode nests servers under mcp and requires type: "remote" on every remote entry. A block copied from a tool that uses the common shape will not work there: the key is wrong and the required field is missing.

Codex is not JSON at all. Its config is TOML, and servers go under [mcp_servers.name] in ~/.codex/config.toml, in your home directory, not in the project.

Claude Desktop has no file to edit

One entry in the table above does not belong in it, and it is worth calling out: Claude Desktop does not take remote MCP servers from a config file at all. The claude_desktop_config.json file is for local servers. Remote ones are added through Settings, under Connectors, by pasting the server URL into a dialog. There is nothing to copy into a file.

If your server authenticates with a fixed token rather than OAuth, that dialog has a Request headers section where you enter the header value exactly as the server expects it, including the word Bearer and the space, since Claude sends the value verbatim and adds no prefix. Header names are restricted to a reviewed allowlist. At the time of writing this capability is in beta and rolling out gradually, so it may not be visible on your account yet.

A standard is being proposed. It is not ratified.

There is an open proposal in the protocol repository, called SEP-2633, for a single client-side configuration format: a file called mcp.json with the familiar mcpServers root key. Its stated motivation is exactly the situation above: every client invented its own file name, format and root key.

It is still a draft, and the open questions are not cosmetic: whether it belongs in the core protocol or in a separate extension, how OAuth should be expressed, how secrets should be handled. Treat it as a direction of travel, not as something you can rely on. Anyone telling you there is already a standard config format is describing a convention, not a specification.

What to do about it

Three habits save most of the pain. First, decide the scope deliberately: put it in the project when the whole team should get it, in your home directory when it is yours. Second, when a server appears to connect but exposes nothing, check the root key before checking anything else. That is the failure this ecosystem produces most often, and it fails silently. Third, be careful with one-click install links: both the VS Code and Cursor handlers accept only the server configuration and give you no way to choose where it lands, so they write to your global config whether or not that is what you wanted.

And when a config file is silently ignored, which happens with paths that look plausible but that the tool never reads, you get no error at all. The server simply never appears. If a setup does not work and nothing is complaining, suspect the location before you suspect the server.

Or skip the table entirely

Every meridiaan project gives you a ready-made block for the tool you actually use: the right file, the right key, the right field name, with your token already in it.

Where Every MCP Client Keeps Its Config | meridiaan