Skip to content
MoggingLabs

Learn

Managing MCP Servers Across Agent CLIs

Model Context Protocol servers are configured differently by Claude Code, Codex, and Gemini. Here is where each one keeps its config, how the formats differ, and how to edit them safely.

July 8, 2026

The Model Context Protocol is a standard way for an AI agent to discover and call tools that live outside its own process: a browser, a database, an issue tracker, your own scripts. The protocol is standard; the configuration is not. Claude Code, Codex, and Gemini each read a different file, in a different format, with different key names for the same idea, so one MCP server has to be registered three ways to reach all three.

This guide shows exactly where each CLI keeps its config, what a server entry looks like in each dialect, which differences are traps, and what a tool that edits those files on your behalf has to do so that it never costs you a working setup.

What MCP gives an agent

An MCP server advertises a list of tools with typed inputs. An agent asks for the list, decides a tool is relevant, calls it with arguments, and reads the result. The transport is stdio for a local server the agent launches, or HTTP for a remote one. A tool written once is callable by any agent that speaks the protocol.

The cost arrives at configuration time. Registering a server means editing the config file of every CLI you use, in whatever format that CLI chose. If you are looking for servers to register, the directory of MCP servers for agent workspaces is a curated, verified starting point.

Three CLIs, three config dialects

Claude Code reads a JSON file at `~/.claude.json` and registers servers under a top-level `mcpServers` object, keyed by server id. Each entry names its transport explicitly with a `type` field.

// ~/.claude.json
{
  "mcpServers": {
    "example": {
      "type": "stdio",
      "command": "node",
      "args": ["/path/to/server.mjs"]
    }
  }
}

Codex reads TOML at `~/.codex/config.toml`, or wherever `CODEX_HOME` points. Servers live in a `[mcp_servers.<id>]` table, and there is no `type` key at all: a table with a `command` is stdio, a table with a `url` is remote.

# ~/.codex/config.toml
[mcp_servers.example]
command = "node"
args = ["/path/to/server.mjs"]

Gemini reads JSON at `~/.gemini/settings.json`, or wherever `GEMINI_CONFIG_DIR` points. It uses the same `mcpServers` object as Claude Code and, like Codex, has no `type` field.

// ~/.gemini/settings.json
{
  "mcpServers": {
    "example": {
      "command": "node",
      "args": ["/path/to/server.mjs"]
    }
  }
}

The differences that will cost you an hour

For local stdio servers the dialects are close enough that copying between them mostly works. Remote servers diverge, silently.

  • Gemini spells a remote server's address `httpUrl`. Claude Code and Codex spell it `url`. Copying an entry between them without renaming that key is the most common way to register a remote server that never gets reached
  • Codex accepts bearer authentication only, as a `bearer_token_env_var` naming an environment variable. Claude Code and Gemini take an arbitrary `headers` map. A server that needs a custom auth header is reachable from two of the three CLIs
  • Claude Code discriminates transports with an explicit `type`. The other two infer the transport from which keys are present, so a mistyped key quietly changes what kind of server you declared
  • One is TOML and two are JSON, so a script that round-trips them all through a JSON parser will destroy the Codex file
# Codex: bearer via an env var name, never the token itself
[mcp_servers.example]
url = "https://mcp.example.com/mcp"
bearer_token_env_var = "EXAMPLE_API_KEY"

What it takes to edit those files safely

These are your files. Five properties separate a manager you can trust from a script that eats a Tuesday.

  • Back up before the first write, to a timestamped copy beside the original, so the recovery path exists before it is needed
  • Merge surgically. Set one key, and leave every other key and the file's own indentation alone. In the TOML dialect a managed entry is a tagged range of lines, so comments around it survive verbatim. JSON has no comments, and a manager that cannot parse a file should refuse it rather than guess
  • Show what will be written before writing it. A rendered block in the target dialect is a preview a human can actually check
  • Mark what you own. A managed entry carries a marker, and removal only ever touches entries carrying that marker, so a hand-written server with the same name survives
  • Detect drift, and never heal it. If a managed entry no longer matches what was written, say so and stop. Silently rewriting the file a user edited by hand is the one unforgivable behavior

The last two matter more than they look. A manager that cannot tell its own entries from yours must choose between clobbering your work and refusing to work, and one that auto-heals drift will eventually undo a deliberate edit.

Never write a secret into a config file

Remote MCP servers need credentials, and a config file is the most tempting place to put them. It is also a plain-text file that lands in backups and screen shares.

Write a reference instead. All three dialects support naming an environment variable rather than embedding a token: an env pointer such as `${EXAMPLE_API_KEY}` in the JSON dialects, and `bearer_token_env_var` in Codex. The variable resolves at launch, and the value never rests on disk where an agent can read it.

Workspace enforces this rather than recommending it. A server entry whose environment value is anything other than a `${VAR}` reference is refused, and any entry containing a string shaped like a credential is refused before it is saved. Credential literals never reach a CLI's config file. The optional keys you paste into the app go to your operating system's keychain, write-only, and are decrypted at the point of use.

Exposing your own tools to agents

Workspace ships a first-party MCP server. It speaks stdio JSON-RPC and lets an agent see the panes around it, read a pane's output, message a teammate, claim files, and drive the browser dock. Browser control is consented: off by default, per workspace, and the dock wears a banner with a stop control whenever an agent is driving it.

Two design rules from it are worth stealing. The first is that write tools are hidden, not merely refused, until a human grants them for a workspace. A tool the model cannot see is a tool a prompt injection cannot talk it into calling. The second is that the merge gate is not a tool at all. The verb that approves a branch exists for humans in a pane and appears in no tool list, so an agent driving the app through MCP cannot approve its own work. That and the reviewer gate in a swarm are the same rule, enforced twice.

Registering a server everywhere is also rarely what you want. A tool list costs context on every request, and a pane doing a database migration has no use for a design tool. Scoping which servers a workspace's agents can see keeps the agent's attention where the work is, and you can watch the difference in a grid that streams every pane live.

Where this is going

Doing this by hand stops scaling after a few servers. A manager that registers a server once and writes it into every hosted CLI's config format, with the preview and drift behavior above, is what Workspace is building. It is not in a public build yet.

Until then the rules hold on their own. Know which file each CLI reads, remember that Gemini says `httpUrl`, and keep tokens in environment variables.

The first-party server and its consent model are documented in the automation and control API docs. For the wider category, start at the AI agent command center.