mcp.json, settings.json, config.toml: which file your client reads.
Updated November 7, 2026
MCP has no standard client config file. Every client decides where it
keeps its server list and what that list looks like. Many use the same mcpServers object as
Claude Desktop's config, so a lot of files look alike without being
interchangeable. A block that works in one
client can be valid JSON in another and still be ignored. This page
explains the files themselves: what mcp.json
is, which file each client reads, how scopes and precedence work, and
where settings.json and
config.toml fit. If you just want a
copy-ready DialMCP block, use
MCP server configuration examples.
We checked each client's docs on November 7, 2026 and tested three of them first-hand: Claude Code 2.1.311, Gemini CLI 0.67.0 and Codex CLI 0.167.0. The test results below come from those versions.
What mcp.json is
"mcp.json" is a habit, not a spec. It names a JSON file holding one
object, usually mcpServers, that maps a
server name you choose to the details your client needs to reach it.
A server entry takes one of two shapes:
- Local (stdio): a
command, itsargs, and an optionalenvmap. The client starts the process and talks to it over standard input and output (how stdio works). - Remote (Streamable HTTP): a
url, often with atypeand optionalheaders. The client connects over HTTPS and, for servers like DialMCP, runs an OAuth sign-in.
{
"mcpServers": {
"local-example": {
"command": "npx",
"args": ["-y", "some-mcp-server"],
"env": { "API_KEY": "..." }
},
"dialmcp": {
"type": "http",
"url": "https://mcp.dialmcp.com/mcp"
}
}
}
Everything else varies by client: the root key, whether
type is required, which extra fields exist,
and how environment variables are written.
Is there an official mcp.json schema? Not yet.
SEP-2633, "Standard Client-Side Configuration Format - mcp.json", is an
open draft pull request on the MCP spec repository. It was last updated
on October 21, 2026, and has not been accepted. Its own problem
statement is the reason this page exists: "Today every MCP client
invents its own format for server configuration." The registry's
server.json is a different thing. It
describes a server for publishing and discovery
(MCP Registry explained), and no
client reads it to connect.
Which file each client reads
| Client | File(s) | Root key | Remote entry |
|---|---|---|---|
| Claude Code | .mcp.json (project), ~/.claude.json (local and user) | mcpServers | url + "type": "http" (required) |
| Claude Desktop | claude_desktop_config.json | mcpServers | Intended for local servers; add remote servers as connectors |
| Cursor | .cursor/mcp.json, ~/.cursor/mcp.json | mcpServers | url |
| VS Code (portable) | .mcp.json, ~/.copilot/mcp-config.json | mcpServers | url + "type": "http" |
| VS Code (older format) | .vscode/mcp.json, profile mcp.json | servers | url + "type": "http" |
| Gemini CLI | .gemini/settings.json, ~/.gemini/settings.json | mcpServers | httpUrl in the docs; the CLI writes url + type |
| Codex | ~/.codex/config.toml, .codex/config.toml | [mcp_servers.<name>] (TOML) | url |
| Kiro | .kiro/settings/mcp.json, ~/.kiro/settings/mcp.json | mcpServers | url |
| Zed | Zed's settings file | context_servers | url |
| Devin Desktop (formerly Windsurf) | ~/.config/devin/mcp_config.json; Devin Local also reads .devin/mcp_config.json | mcpServers | serverUrl or url |
Three patterns cover most mistakes. The name mcp.json
means something different in Cursor, VS Code and Kiro. A file called
settings.json defines servers in Gemini CLI,
and Zed keeps them in its settings file, but Claude Code and current VS
Code don't define servers there. Codex is the one
major client that uses TOML. Devin Desktop's Cascade agent opens
~/.config/devin/mcp_config.json (Windows:
%AppData%/devin/mcp_config.json); the older
~/.codeium/windsurf/mcp_config.json is now only
read by the editor's MCP discovery. Agent setup is on
Windsurf MCP setup, and Claude Desktop's
file, key by key, in
claude_desktop_config.json explained.
.mcp.json in Claude Code
Claude Code has three scopes, and they live in two files. We added the same server once per scope in a scratch project and recorded where each one was written:
- Local (the default, private to you in this project):
~/.claude.json, underprojects["/path/to/project"].mcpServers. - Project (
--scope project, shared through git):.mcp.jsonat the project root. - User (
--scope user, every project):~/.claude.json, top-levelmcpServers.
If CLAUDE_CONFIG_DIR is set, Claude Code
reads .claude.json from that directory instead.
Its docs also list paths it does not read, including
~/.claude/mcp.json and
~/.claude/.mcp.json, which are common
guesses.
Four rules from the docs. Our test confirmed the type rule, local over project, the approval step and variable defaults:
- Remote entries need
type. Claude Code reads an entry with notypeas stdio. Aurl-only entry is skipped, andclaude mcp listsays so: "has a "url" but no "type"; add "type": "http"".streamable-httpis accepted as an alias forhttp. This is the classic Cursor-to-Claude-Code paste failure. - Local beats project, project beats user (servers an administrator manages sit above all three). With the same name in
.mcp.jsonand local scope, Claude Code used the local entry. Fields are not merged across scopes: the whole winning entry is used. When the endpoints differ,claude mcp listwarns that "OAuth tokens are stored per endpoint, so authenticating in one context will not carry over." - Project servers wait for approval. In an interactive session, servers from
.mcp.jsonshow "Pending approval" until you accept them. A cloned repository can't approve its own servers: approval keys committed in the project's.claude/settings.jsonare ignored until you trust the folder. - Environment variables expand.
${VAR}and${VAR:-default}work incommand,args,env,urlandheaders. Our test URL${DM_BASE:-https://mcp.dialmcp.com}/mcpreached the real endpoint. An unset variable with no default stays as literal text, with a "Missing environment variables" warning. One exception: in remoteurlandheaders, Claude Code reads its own credential variables, such asANTHROPIC_API_KEYand cloud-provider tokens, as empty.
claude mcp add-json <name> '<json>'
takes the object that goes inside mcpServers,
not the wrapper. Setup, permissions and tool approval are covered in
Claude Code MCP setup.
settings.json: two different jobs
Searching "mcp settings.json" gets mixed answers, because several clients use a file with that name for different things.
Claude Code: approvals and policy, not servers
Claude Code keeps server definitions in .mcp.json
and ~/.claude.json. Its
settings.json files decide what may run:
enableAllProjectMcpServers: approve every server in the project's.mcp.json.enabledMcpjsonServers/disabledMcpjsonServers: approve or reject specific.mcp.jsonservers by name.allowedMcpServers/deniedMcpServers: allow or deny lists matched by server name, command or URL. The deny list wins.
We put an mcpServers block in both
.claude/settings.json and
.claude/settings.local.json. Claude Code
ignored it: the server wasn't listed and there was no warning. In the
same run, "enabledMcpjsonServers": ["envtest"]
in settings.local.json approved that one
server and left the others pending. Note the naming trap: Claude Code's
MCP "local" scope means ~/.claude.json, not
.claude/settings.local.json. Administrators
are the exception: the managed-only managedMcpServers
setting supplies remote servers, and a separate
managed-mcp.json file, in the
.mcp.json format, can take exclusive control of
which servers users can run.
Gemini CLI: settings.json is the server list
Gemini CLI reads mcpServers from
~/.gemini/settings.json and the project's
.gemini/settings.json. Project settings
override user settings. Two behaviours differ from Claude Code:
gemini mcp add defaults to project
scope, and it refuses to write project settings in a folder you haven't
trusted ("Cannot modify settings in an untrusted workspace"). The docs
describe remote servers as httpUrl
(Streamable HTTP) or url (SSE), with no
type field. Version 0.67.0's own
gemini mcp add --transport http wrote this:
{
"mcpServers": {
"dialmcp": {
"url": "https://mcp.dialmcp.com/mcp",
"type": "http"
}
}
}
Gemini adds per-server fields that most others lack, including
trust (skip every tool confirmation; the docs say to use it only for servers you control),
includeTools /
excludeTools and
timeout. Its env
values accept $VAR and
${VAR}.
VS Code: not settings.json any more
VS Code once read MCP servers from settings.json.
Release 1.102 migrated them into the dedicated
mcp.json files, and the current MCP reference
doesn't list settings.json as a place to
define servers. VS Code now has two formats. The portable one is
.mcp.json at the workspace root or
~/.copilot/mcp-config.json, with root key
mcpServers, and VS Code's docs recommend it
for new servers. The older format is
.vscode/mcp.json or the profile
mcp.json, with root key
servers. Only the older format supports
inputs prompts, envFile
and dev mode. VS Code can also reuse other apps' configs
(chat.mcp.discovery.enabled, per source;
only the Copilot CLI source is on by default). Details:
VS Code MCP setup.
config.toml: Codex
Codex keeps MCP servers in ~/.codex/config.toml,
shared by the CLI, the desktop app and the IDE extension. A project can
add .codex/config.toml, which Codex reads only
in trusted projects. Each server is a TOML table, not a JSON object.
Codex CLI 0.167.0 wrote these two entries:
$ codex mcp add dialmcp --url https://mcp.dialmcp.com/mcp
$ codex mcp add localdemo --env API_KEY=xyz -- npx -y some-server
# ~/.codex/config.toml
[mcp_servers.dialmcp]
url = "https://mcp.dialmcp.com/mcp"
[mcp_servers.localdemo]
command = "npx"
args = ["-y", "some-server"]
[mcp_servers.localdemo.env]
API_KEY = "xyz"
There's no type key: a
command makes a stdio server and a
url a Streamable HTTP server. Codex doesn't document
${VAR} expansion. It names environment
variables instead: env_vars forwards
variables to a local server, bearer_token_env_var
holds a token's variable name, and env_http_headers
builds headers from variables. Codex-specific settings include
enabled_tools,
disabled_tools,
startup_timeout_sec (default 10) and
tool_timeout_sec (default 60). DialMCP uses OAuth, so leave
bearer_token_env_var unset for it. Codex plugins can bundle their own .mcp.json,
but servers you add yourself go in config.toml.
Full walkthrough: Codex MCP setup.
Environment variables and secrets, by client
Copying a block between clients often breaks on the variable syntax, not on the structure:
| Client | Syntax | Notes |
|---|---|---|
Claude Code (.mcp.json) | ${VAR}, ${VAR:-default} | Applies to command, args, env, url and headers |
| VS Code portable files | ${NAME}, ${NAME:-default} | Bare $NAME stays literal; no ${input:} |
VS Code .vscode/mcp.json | ${env:NAME}, ${input:id} | inputs prompts once and stores the value securely |
| Cursor | ${env:NAME} | Also ${userHome}, ${workspaceFolder}; envFile for stdio |
| Gemini CLI | $VAR, ${VAR} | An undefined variable becomes an empty string |
| Codex | None documented | Use env_vars, bearer_token_env_var, env_http_headers |
Whatever the syntax, keep real keys out of any file you commit.
.mcp.json, .cursor/mcp.json
and .vscode/mcp.json are made to be shared
through git, so reference a variable there instead of pasting a key.
For more on what an MCP config can expose, see
MCP security. OAuth servers sidestep
the problem: DialMCP's entry holds only a URL, and the token lives in
the client's credential store after sign-in.
Moving a server between files
A short checklist for moving a server entry between clients:
- Rename the root key.
mcpServersfor most files,serversfor.vscode/mcp.json,context_serversfor Zed, and a[mcp_servers.name]table for Codex. - Fix the remote field. Add
"type": "http"for Claude Code and VS Code. Devin Desktop acceptsserverUrlorurl. Codex and Cursor entries useurlwith notypefield. - Rewrite variables using the table above.
- Drop client-only fields.
inputs,trust,enabled_toolsandalwaysLoadeach mean something to one client only. - Check that it loaded. A wrong root key is still valid JSON, so the file can look fine while nothing loads. In our Claude Code test, an
mcpServersblock insettings.jsonwas ignored without a warning.claude mcp list,gemini mcp list,codex mcp listand VS Code's MCP: List Servers all show what was actually loaded.
For DialMCP the block is the same everywhere: one URL,
https://mcp.dialmcp.com/mcp, with no key and
no headers. The ready-made versions for each file are on
MCP server configuration examples. Per-client
sign-in and troubleshooting are on client setup,
and the stdio bridge for local-only clients is in
Install DialMCP.
FAQ
What is mcp.json?
mcp.json is the common name for a JSON file that lists the MCP servers a client should connect to, usually under an mcpServers object that maps each server name to a command (local stdio) or a URL (remote). It is a convention, not part of the MCP spec. Claude Code reads .mcp.json, Cursor reads .cursor/mcp.json, VS Code reads .vscode/mcp.json or a workspace .mcp.json, and Kiro reads .kiro/settings/mcp.json.
Where is mcp.json located?
It depends on the client. Claude Code: .mcp.json at the project root, with local and user servers in ~/.claude.json. Cursor: .cursor/mcp.json in the project or ~/.cursor/mcp.json. VS Code: .mcp.json at the workspace root or ~/.copilot/mcp-config.json ($COPILOT_HOME/mcp-config.json if set), or the older .vscode/mcp.json. Codex uses ~/.codex/config.toml and Gemini CLI uses ~/.gemini/settings.json instead of an mcp.json file.
Is there an official mcp.json schema?
No. As of November 7, 2026, SEP-2633, a proposed standard client-side mcp.json format, is an open draft on the MCP spec repository and has not been accepted. The MCP Registry's server.json schema describes servers for publishing and discovery, not client configuration.
Does Claude Code read MCP servers from settings.json?
No, not for servers you define yourself. Claude Code reads server definitions from .mcp.json and ~/.claude.json. Its settings.json files hold approval and policy keys such as enableAllProjectMcpServers, enabledMcpjsonServers and deniedMcpServers. The exception is the managed-only managedMcpServers setting used by administrators. In our test with Claude Code 2.1.311, an mcpServers block placed in .claude/settings.json was ignored without a warning.
What is the difference between .mcp.json and .vscode/mcp.json?
In VS Code, .mcp.json at the workspace root is the portable format with root key mcpServers, which VS Code's docs recommend for new servers. .vscode/mcp.json is the older VS Code format with root key servers; it alone supports inputs prompts, envFile and dev mode. A block under the wrong root key isn't read as a server list, so check what loaded with MCP: List Servers.
How do I add an MCP server to Codex config.toml?
Run codex mcp add NAME --url URL for a remote server, or codex mcp add NAME -- COMMAND ARGS for a local one, or write a [mcp_servers.NAME] table in ~/.codex/config.toml with url, or command and args. Codex has no type key and does not document ${VAR} expansion; use env_vars or bearer_token_env_var for secrets.
Back to the docs hub.