DEV Community

Eliran Kotzero
Eliran Kotzero

Posted on Originally published at kitquarry.com

One MCP server, three configs: Claude Code, Cursor and Codex

Originally published on KitQuarry. Written by Eliran Kotzero for KitQuarry.

In short: all three tools speak MCP, but each keeps its servers in a different file. Claude Code uses .mcp.json at the repo root, Cursor uses .cursor/mcp.json, and Codex uses [mcp_servers.<name>] tables in .codex/config.toml. The JSON files look alike, but two details break copy-paste: Claude Code needs "type": "http" on remote servers, and every tool writes environment variables differently. Below is the same two-server setup written for each tool, the differences that matter, and how to check that each one loaded.

Where each tool looks

Claude Code Cursor Codex
Project file (commit it) .mcp.json at the project root .cursor/mcp.json .codex/config.toml, trusted projects only
Personal file ~/.claude.json (user scope, and local scope per project) ~/.cursor/mcp.json ~/.codex/config.toml
Format JSON, mcpServers JSON, mcpServers TOML, [mcp_servers.<name>]
Env variable in a value ${VAR} or ${VAR:-default} ${env:VAR} Name the variable in a field such as bearer_token_env_var
See what loaded claude mcp list, /mcp Customize, MCP Logs in the Output panel codex mcp list, /mcp

Sources: the Claude Code MCP docs, the Cursor MCP docs and the Codex MCP docs, all checked on 2026-10-29. Some third-party guides still say Codex has no project-level MCP config. The current Codex docs say it does: put the tables in .codex/config.toml in the repo, and Codex loads them once you trust the project.

The same setup, written three ways

The example has two servers: Context7, a local stdio server that the Codex docs use as their example (npx -y @upstash/context7-mcp), and team-docs, a stand-in for a remote HTTP server that needs a bearer token. mcp.example.com and TEAM_DOCS_TOKEN are placeholders; swap in your server's URL and auth. The token value never appears in a file. Each developer sets TEAM_DOCS_TOKEN in their own shell.

We checked that all three files parse (JSON and TOML). We didn't run them inside the three tools, so test them in yours before you rely on them.

Claude Code: .mcp.json

{
  "mcpServers": {
    "context7": {
      "command": "npx",
      "args": ["-y", "@upstash/context7-mcp"]
    },
    "team-docs": {
      "type": "http",
      "url": "https://mcp.example.com/mcp",
      "headers": {
        "Authorization": "Bearer ${TEAM_DOCS_TOKEN}"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
  • An entry with a url needs a type ("http", "sse" or "ws"). Claude Code reads an entry without one as a stdio server, so it skips a url-only entry and reports has a "url" but no "type". The docs accept "streamable-http" as another name for "http".
  • ${VAR} and ${VAR:-default} expand in command, args, env, url and headers. If a variable is unset and has no default, the config still loads, claude mcp list shows a missing-variable warning, and the literal ${VAR} text is used.
  • In a remote server's url and headers, some credential variables always read as empty, including ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN and NPM_TOKEN. That stops a repo's .mcp.json from sending your own credentials to a server it names. Use a variable name of your own, like TEAM_DOCS_TOKEN.
  • Claude Code sets CLAUDE_PROJECT_DIR inside a stdio server's environment. To use it in command or args of .mcp.json, write ${CLAUDE_PROJECT_DIR:-.}, because the variable isn't set in Claude Code's own environment.

Cursor: .cursor/mcp.json

{
  "mcpServers": {
    "context7": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@upstash/context7-mcp"]
    },
    "team-docs": {
      "url": "https://mcp.example.com/mcp",
      "headers": {
        "Authorization": "Bearer ${env:TEAM_DOCS_TOKEN}"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Variables use ${env:NAME}. Cursor also offers ${userHome}, ${workspaceFolder} (the folder that contains .cursor/mcp.json), ${workspaceFolderBasename} and ${/}. They resolve in command, args, env, url and headers.
  • Remote servers are written as url plus optional headers, as in Cursor's own examples. Cursor's stdio field table lists "type": "stdio" as required, so the example includes it.
  • Stdio servers can load more variables from a file with envFile (for example "${workspaceFolder}/.env"). Remote servers can't use envFile.

Codex: .codex/config.toml

# .codex/config.toml  (loaded only in a trusted project)
[mcp_servers.context7]
command = "npx"
args = ["-y", "@upstash/context7-mcp"]

[mcp_servers.team-docs]
url = "https://mcp.example.com/mcp"
bearer_token_env_var = "TEAM_DOCS_TOKEN"
Enter fullscreen mode Exit fullscreen mode
  • Codex doesn't use ${...} placeholders in these fields. It takes the variable's name instead: bearer_token_env_var for an Authorization bearer token, env_http_headers for other headers read from the environment, and env_vars to forward named variables to a stdio server. env sets fixed values.
  • Codex loads project .codex/ layers only when you trust the project. In an untrusted project it skips them, including project config, hooks and rules. With several .codex/config.toml files, the one closest to your working directory wins (config basics).
  • The ChatGPT desktop app, Codex CLI and IDE extension share this configuration, so setting it up once covers all three.
  • Per-server controls that the other two don't put in this file: enabled_tools and disabled_tools, default_tools_approval_mode (auto, prompt, writes or approve), startup_timeout_sec (default 10) and tool_timeout_sec (default 60).

Adding servers from the command line

# Claude Code: write the server into .mcp.json for the team
claude mcp add --transport http --scope project team-docs https://mcp.example.com/mcp
claude mcp add --transport stdio --scope project context7 -- npx -y @upstash/context7-mcp

# Codex: add a stdio server to your Codex config
codex mcp add context7 -- npx -y @upstash/context7-mcp

# Check what loaded
claude mcp list
codex mcp list
Enter fullscreen mode Exit fullscreen mode

In claude mcp add, everything after -- is the server's own command, so its flags aren't read as Claude's. Without --scope, Claude Code uses local scope: the server goes into your entry for this project inside ~/.claude.json, not into .mcp.json, and your teammates won't see it. For a remote server in Codex, the docs show codex mcp add <name> --url <url>, and codex mcp login <name> starts an OAuth sign-in. Cursor adds servers through Customize or by editing mcp.json.

Which definition wins

  • Claude Code: local scope beats project scope, which beats user scope. Duplicates are matched by name, and the whole entry from the winning scope is used, with no field-by-field merge. A personal local copy of team-docs therefore hides the team's version completely.
  • Codex: CLI flags first, then project .codex/config.toml files (closest first), then a selected profile, then ~/.codex/config.toml.
  • Cursor: the docs list the project and global files but don't say which wins when both define the same server name. Give team servers names you won't reuse in your global file.

Approvals and trust

A committed MCP config runs commands on every machine that opens the repo, so each tool puts a gate in front of it:

  • Claude Code asks you to approve servers from .mcp.json in an interactive session. Until then, claude mcp list shows them as pending approval. A repo can't approve its own servers: approval settings committed to .claude/settings.json are ignored until you trust the folder. claude mcp reset-project-choices resets your choices. In claude -p runs and Agent SDK sessions there's no prompt, and Claude Code loads project servers without asking, so add a server to disabledMcpjsonServers or use --strict-mcp-config if you don't want it there.
  • Codex ignores .codex/config.toml until you trust the project.
  • Cursor asks for approval before it runs an MCP tool by default, and MCP follows the same run modes as terminal commands.

Read a server's source before you approve it, and give it a narrowly scoped token. If you're also keeping agents away from secrets files, our .env guide covers the file-access side for all three tools.

Keeping three files in sync

There's no shared MCP file that all three tools read, so you maintain three. A few habits keep that manageable:

  • Use the same server names in every file, so a rule like "use team-docs for internal API questions" in AGENTS.md or CLAUDE.md means the same thing in each tool.
  • Commit references, never values. Keep a .env.example or a short README table that lists the variables each developer needs.
  • When you change a server, change all three files in the same commit. A short note in your instruction file helps, the same way we suggest pointing CLAUDE.md at AGENTS.md in one source of truth.
  • Third-party sync tools exist. If you use one, check its output against the formats above, especially "type" on remote servers and the variable syntax.

Checklist

  1. Write .mcp.json, .cursor/mcp.json and .codex/config.toml with the same server names.
  2. Add "type": "http" to every remote server in .mcp.json.
  3. Replace every secret with a variable: ${VAR}, ${env:VAR}, or a Codex *_env_var field.
  4. Run claude in the repo and approve the project servers, then check claude mcp list.
  5. Trust the project in Codex, then check codex mcp list or /mcp.
  6. In Cursor, check the servers are on in Customize and look at MCP Logs in the Output panel if one fails.

Setting up instruction files too? The free sampler ships rules and skills in the formats Claude Code, Codex, Cursor, Windsurf and Copilot each expect. The AI Coding Rules Pack adds the full rule set and workflow skills.

FAQ

Where does each tool keep project MCP servers?

Claude Code reads .mcp.json at the project root. Cursor reads .cursor/mcp.json in the project. Codex reads [mcp_servers.<name>] tables in .codex/config.toml, but only in a project you've trusted. The personal versions are ~/.claude.json, ~/.cursor/mcp.json and ~/.codex/config.toml.

Can I copy Cursor's mcp.json into Claude Code's .mcp.json?

Not as is. Both use an mcpServers object, but Claude Code treats an entry with no "type" as a stdio server, so a remote server with only a "url" is skipped with an error until you add "type": "http". Environment variables also differ: Cursor writes ${env:NAME}, Claude Code writes ${NAME} or ${NAME:-default}.

Does Codex support project-level MCP servers?

Yes. Current Codex docs let you add [mcp_servers.<name>] tables to .codex/config.toml in the repo. Codex loads project .codex/ layers only for trusted projects, and the config closest to your working directory wins. The ChatGPT desktop app, Codex CLI and IDE extension share this configuration.

How do I keep API keys out of a committed MCP config?

Commit only variable references. In Claude Code use ${TOKEN} in .mcp.json, in Cursor use ${env:TOKEN}, and in Codex name the variable with bearer_token_env_var, env_http_headers or env_vars instead of writing the value. Each developer sets the variable in their own shell. Codex stores an OAuth client secret in config.toml, so keep that kind of setup in your personal ~/.codex/config.toml.

Why does my project MCP server not show up in Claude Code?

Claude Code asks you to approve project servers from .mcp.json in an interactive session. Until you do, claude mcp list shows the server as pending approval. Run claude in the folder and accept the trust dialog and the server prompt. To ask again later, run claude mcp reset-project-choices.

Sources

Official docs, checked 2026-10-29.


If you want a starting point, KitQuarry's free sampler is on GitHub: ai-coding-rules-sampler. It has a short CLAUDE.md/AGENTS.md, security rules and three skills (plan-feature, review-changes, record-decisions) for Claude Code, Cursor, Codex and Windsurf. MIT, free. (I made it.)

Top comments (0)