Share MCP servers between Claude Code, Codex, Gemini CLI and OpenCode
On this page
Where each CLI keeps user-level MCP serversThe same two servers in all four formatsAdding them from the command lineSecrets, environment variables and headersProject-level files and precedenceMoving the config somewhere elseTraps when translatingHow Codeman handles itThere is no shared MCP file. Each CLI reads its own user-level config in its own format: Claude Code uses ~/.claude.json, Codex uses ~/.codex/config.toml, Gemini CLI uses ~/.gemini/settings.json and OpenCode uses ~/.config/opencode/opencode.json. To use the same servers everywhere, translate each one into all four formats, using each CLI's own variable syntax to keep secrets out of the files where it allows. The tables and examples below reflect the docs as of October 2026.
Where each CLI keeps user-level MCP servers
| CLI | User-level file | Format | Top-level key | Add command | Relocation env var |
|---|---|---|---|---|---|
| Claude Code | ~/.claude.json |
JSON | mcpServers |
claude mcp add --scope user |
CLAUDE_CONFIG_DIR |
| Codex | ~/.codex/config.toml |
TOML | [mcp_servers.<name>] |
codex mcp add |
CODEX_HOME |
| Gemini CLI | ~/.gemini/settings.json |
JSON | mcpServers |
gemini mcp add -s user |
GEMINI_CLI_HOME |
| OpenCode | ~/.config/opencode/opencode.json |
JSON or JSONC | mcp |
opencode mcp add (guided) |
XDG_CONFIG_HOME |
Sources: Claude Code's MCP docs, Codex's MCP page and command reference, Gemini CLI's MCP server docs and OpenCode's MCP servers, config and CLI pages.
Watch the defaults. ~/.claude.json also holds your sign-in session and per-project state, and Claude Code writes it itself; plain claude mcp add uses local scope, which stores the server under the current project's entry and loads it only there. gemini mcp add defaults to project scope (.gemini/settings.json). codex mcp add always writes ~/.codex/config.toml, and opencode mcp add walks you through a local or remote server.
The same two servers in all four formats
One stdio server (npx -y example-mcp-server, which needs EXAMPLE_API_KEY) and one remote server at https://mcp.example.com/mcp that wants an Authorization: Bearer header. The names use hyphens, not underscores (see traps).
Claude Code (~/.claude.json, top level):
{
"mcpServers": {
"example": {
"type": "stdio",
"command": "npx",
"args": ["-y", "example-mcp-server"],
"env": { "EXAMPLE_API_KEY": "${EXAMPLE_API_KEY}" }
},
"example-remote": {
"type": "http",
"url": "https://mcp.example.com/mcp",
"headers": { "Authorization": "Bearer ${EXAMPLE_TOKEN}" }
}
}
}
Codex (~/.codex/config.toml):
[mcp_servers.example]
command = "npx"
args = ["-y", "example-mcp-server"]
env_vars = ["EXAMPLE_API_KEY"] # forward it from the environment Codex runs in
[mcp_servers.example-remote]
url = "https://mcp.example.com/mcp"
bearer_token_env_var = "EXAMPLE_TOKEN" # sent as Authorization: Bearer <value>
Gemini CLI (~/.gemini/settings.json):
{
"mcpServers": {
"example": {
"command": "npx",
"args": ["-y", "example-mcp-server"],
"env": { "EXAMPLE_API_KEY": "<your key>" }
},
"example-remote": {
"type": "http",
"url": "https://mcp.example.com/mcp",
"headers": { "Authorization": "Bearer <your token>" }
}
}
}
OpenCode (~/.config/opencode/opencode.json):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"example": {
"type": "local",
"command": ["npx", "-y", "example-mcp-server"],
"environment": { "EXAMPLE_API_KEY": "{env:EXAMPLE_API_KEY}" },
"enabled": true
},
"example-remote": {
"type": "remote",
"url": "https://mcp.example.com/mcp",
"headers": { "Authorization": "Bearer {env:EXAMPLE_TOKEN}" },
"oauth": false,
"enabled": true
}
}
}
OpenCode tries OAuth automatically on remote servers, and its docs say to set "oauth": false for a server that uses an API key instead.
Adding them from the command line
# Claude Code: keep another option between --env and the name
claude mcp add --env EXAMPLE_API_KEY=sk-123 --scope user --transport stdio example -- npx -y example-mcp-server
claude mcp add --scope user --transport http example-remote https://mcp.example.com/mcp --header "Authorization: Bearer sk-456"
# Codex
codex mcp add example --env EXAMPLE_API_KEY=sk-123 -- npx -y example-mcp-server
codex mcp add example-remote --url https://mcp.example.com/mcp --bearer-token-env-var EXAMPLE_TOKEN
# Gemini CLI: arguments that start with a dash go after --
gemini mcp add -e EXAMPLE_API_KEY=sk-123 -s user example npx -- -y example-mcp-server
gemini mcp add -H "Authorization: Bearer sk-456" -s user -t http example-remote https://mcp.example.com/mcp
# OpenCode
opencode mcp add
Claude Code reads a name placed directly after --env as another KEY=value pair and rejects it. Check the result with each CLI's mcp list.
These commands store the literal values you type. To keep a variable reference in Claude Code, pass single-quoted JSON so the shell leaves ${...} alone: claude mcp add-json --scope user example '{"type":"stdio","command":"npx","args":["-y","example-mcp-server"],"env":{"EXAMPLE_API_KEY":"${EXAMPLE_API_KEY}"}}'. That is also safer than editing ~/.claude.json by hand while Claude Code is rewriting it.
Secrets, environment variables and headers
| CLI | Literal values | Reference to a variable |
|---|---|---|
| Claude Code | env, headers |
${VAR} or ${VAR:-default} in command, args, env, url, headers |
| Codex | env, http_headers |
env_vars (forward), bearer_token_env_var, env_http_headers (header name to variable name) |
| Gemini CLI | env, headers |
$VAR or ${VAR} in env, with the caveat below |
| OpenCode | environment, headers |
{env:VAR} in config values, {file:path} for a file's contents |
Sources: Claude Code's variable expansion, Codex's config reference, Gemini CLI's environment variable expansion and OpenCode's variable substitution.
Unset variables behave differently. Claude Code keeps the unexpanded ${VAR} text and warns in claude mcp list. Gemini CLI and OpenCode substitute an empty string. Claude Code also reads its own and your cloud provider's credentials (such as ANTHROPIC_API_KEY) as empty inside a remote server's url and headers, so copy such a value into a variable with a name of your own.
The Gemini caveat: it redacts inherited variables whose names match *TOKEN*, *KEY*, *SECRET*, *PASSWORD* and similar, and in v0.63.0 $VAR references are expanded from that redacted environment: a repository test asserts that $MY_AWS_TOKEN does not expand to the secret. For such a name, write the value into env and protect the file. More generally, do not count on a stdio server inheriting secrets from your shell in Codex (list them in env_vars) or Gemini CLI.
Project-level files and precedence
- Claude Code:
.mcp.jsonat the project root (project scope; interactive sessions ask for approval before using its servers). When the same name exists in several scopes, local wins over project, project over user, and the whole entry comes from the winner; fields are not merged. - Codex:
.codex/config.toml, loaded only for trusted projects, closest to the working directory wins, and project files rank above~/.codex/config.toml. - Gemini CLI:
.gemini/settings.jsonoverrides~/.gemini/settings.json, and a system settings file overrides both. The settings schema mergesmcpServersshallowly, so a project entry replaces the user entry of the same name. - OpenCode: a project
opencode.jsonis merged over the global file, and later configs override earlier ones only for conflicting keys.
Moving the config somewhere else
CODEX_HOME: Codex keeps its state,config.tomlincluded, under it (default~/.codex).GEMINI_CLI_HOME: the root under which Gemini CLI creates its.geminifolder, so the file becomes$GEMINI_CLI_HOME/.gemini/settings.json.CLAUDE_CONFIG_DIR: replaces~/.claude; settings, session history and plugins move with it, and Anthropic's dev container guide notes that with the variable set, Claude Code writes.claude.json(and with it your user-scope MCP servers) inside that directory instead of at~/.claude.json.XDG_CONFIG_HOME: OpenCode's docs only show~/.config/opencode/, but its source resolves that directory with the XDG base-directory rules. Separately,OPENCODE_CONFIG(a config file) andOPENCODE_CONFIG_DIR(a config directory) add layers on top of the global config rather than moving it.
Traps when translating
- Always write
type. Claude Code reads an entry with aurlbut notypeas stdio and skips it. Gemini's docs call a bareurlSSE andhttpUrlstreamable HTTP, but v0.63.0 tries a bareurlas streamable HTTP first and logshttpUrlas deprecated in favour ofurlplus"type": "http", whichgemini mcp addwrites. - Transport gaps. Codex documents stdio and streamable HTTP only, so an SSE server has no Codex equivalent. Claude Code marks SSE as deprecated, and its WebSocket type (
"type": "ws") can only be added through JSON. - Shape differences. OpenCode puts the command and its arguments in one
commandarray and calls the variablesenvironment; Codex calls static headershttp_headers. Switching a server off isenabled = falsein Codex and"enabled": falsein OpenCode. - Server names. Gemini CLI's configuration reference warns against underscores in server names, because its policy engine splits tool names at the first underscore. In TOML, a name with characters other than letters, digits,
-and_must be quoted:[mcp_servers."my.server"]. - Quoting. TOML basic strings take backslash escapes like JSON, so write a Windows path as a single-quoted literal:
command = 'C:\tools\server.exe'. OpenCode accepts JSONC (comments, trailing commas), which strict JSON parsers refuse. - Gemini
-eand=. In v0.63.0,gemini mcp addsplits each-evalue on every=, so a value that itself contains=(base64 padding, for example) is cut short. Editsettings.jsonfor those. - OpenCode V2. OpenCode V2 has its own installer and docs. It groups servers under
mcp.serversand usesdisabledinstead ofenabled, and its migration guide says it still reads V1 entries but that V1 should not be pointed at files converted to V2-only shapes. - Permissions. Any of these files can end up holding literal tokens, and
~/.claude.jsonalready holds your sign-in session: keep them readable by you only (chmod 600), and keep literals out of committed project files such as.mcp.json.
How Codeman handles it
Codeman has an opt-in MCP server sync, in the MCP servers group of Settings → Agents & CLIs (since 1.34.0). It is off by default, synced across devices and admin only in multi-user mode. Turn on Enable MCP server sync and save, then Preview shows what would change and Sync now applies it. Details are in Settings Reference.
- Which CLIs. Claude Code, Codex, Gemini CLI, OpenCode and Antigravity (
~/.gemini/config/mcp_config.json), each when it is enabled in Codeman and installed or already has its config file. Other installed agent CLIs are listed as unsupported and left alone. Only the user-level files are read and written; project files are not touched. - Additive only. A name a CLI already has is never edited or removed. The same name with a different definition is reported as a conflict and both definitions stay. A server switched off in its own CLI is not copied, and a server a format cannot express (SSE for Codex and Antigravity) is skipped and reported.
- Careful writes. A file that does not parse (OpenCode JSONC with comments, for example) is never written. The previous file is kept as
<file>.codeman-bak, overwritten by each sync, and a file that receives env values or headers is left readable by its owner only. - Relocation.
CLAUDE_CONFIG_DIR(looked up as$CLAUDE_CONFIG_DIR/.claude.json),CODEX_HOME,XDG_CONFIG_HOMEandGEMINI_CLI_HOMEare followed as the Codeman server's environment sets them; a per-session override is not. - Values are copied as written. A
${VAR},$VARor{env:VAR}reference is not translated to the target's syntax, and Codex'senv_vars,bearer_token_env_varandenv_http_headersfields are not carried over.
Over the API it is GET /api/mcp-sync (dry run) and POST /api/mcp-sync (apply), both 403 while the setting is off; results carry server names only, never env values or headers (HTTP API). For running these CLIs side by side on one project, see Run Claude Code, Codex, Gemini CLI and other agents side by side and Agent CLIs.