mcp-manager
Safely manage Codex MCP server registrations with collision checks, backups, advanced-key patches, dry-run planning, and rollback.
What it does
Section titled “What it does”codex-mcp-manager is a small wrapper around the official codex mcp CLI.
It delegates normal add, remove, list, and get operations to Codex so the
official writer and validation stay in charge, then adds the safety features
that are useful for repeatable setup scripts:
- detects name collisions before add
- creates backups before mutating
config.toml - records backup manifests for rollback
- supports dry-run plans
- patches advanced MCP table keys that
codex mcp adddoes not expose - supports current
auth, remote stdio placement, mixedenv_vars, and per-tool approvals - rejects static credential headers; use environment-variable references instead
- rejects unknown CLI flags instead of silently ignoring typos
- treats only Codex’s explicit not-found response as absence; CLI/config/timeouts fail closed
- hash-guards mutations and automatically restores verified pre-operation bytes on validation failure
The manager supports stdio servers and streamable HTTP servers.
Install
Section titled “Install”npm install @codex-modules/mcp-managerFor local development inside this repository:
cd modules/mcp-managernpm installnpm run buildAdd a stdio server:
codex-mcp-manager add \ --name docs \ --command node \ --arg /path/to/server.jsAdd a streamable HTTP server:
codex-mcp-manager add \ --name web \ --url https://mcp.example.com/mcp \ --bearer-token-env-var MCP_WEB_TOKENUse an environment-backed authorization header and a per-tool approval:
codex-mcp-manager add \ --name web \ --url https://mcp.example.com/mcp \ --auth oauth \ --env-http-header Authorization=MCP_AUTH_HEADER \ --tool-approval search=promptConfigure remote stdio and mixed local/remote environment references:
codex-mcp-manager add \ --name remote_tools \ --command node \ --arg /path/to/server.js \ --experimental-environment remote \ --env-var LOCAL_TOKEN \ --env-var-source REMOTE_TOKEN=remoteRead a server definition from JSON:
{ "name": "github", "url": "https://mcp.example.com/github", "auth": "oauth", "bearerTokenEnvVar": "GITHUB_MCP_TOKEN", "httpHeaders": { "X-Client": "codex" }, "envHttpHeaders": { "Authorization": "GITHUB_MCP_AUTH_HEADER" }, "toolApprovals": { "search": "prompt" }}codex-mcp-manager plan --from github.json --jsoncodex-mcp-manager add --from github.json --forcePatch advanced keys under an existing [mcp_servers.<name>] table:
codex-mcp-manager patch github \ --set startup_timeout_sec=20 \ --set 'enabled_tools=["search","open"]' \ --tool-approval search=prompt--set 'tools={"repo/list":{"approval_mode":"approve"}}' is also
supported. Tool names are emitted as quoted TOML path segments, so names that
contain / or . remain a single tool identifier.
Inspect or remove servers:
codex-mcp-manager list --jsoncodex-mcp-manager get github --jsoncodex-mcp-manager remove githubcodex-mcp-manager doctorUse --codex-home DIR on any command to target an isolated Codex home instead
of the current user’s default ~/.codex.
import { addServer, doctor, getServer, listServers, patchServer, patchServerText, plan, removeServer, rollback, type ServerDef,} from "@codex-modules/mcp-manager";
const def: ServerDef = { name: "web", url: "https://mcp.example.com/mcp", auth: "oauth", bearerTokenEnvVar: "MCP_WEB_TOKEN", envHttpHeaders: { Authorization: "MCP_WEB_AUTH_HEADER" }, toolApprovals: { search: "prompt" },};
await plan(def, { codexHome: "/tmp/codex-home" });await addServer(def, { codexHome: "/tmp/codex-home", force: true });await patchServer("web", { startup_timeout_sec: 20 }, { codexHome: "/tmp/codex-home" });await rollback({ codexHome: "/tmp/codex-home" });ServerDef accepts either:
- stdio:
{ name, command, args?, env?, envVars?, experimentalEnvironment?, toolApprovals? } - HTTP:
{ name, url, auth?, bearerTokenEnvVar?, httpHeaders?, envHttpHeaders?, toolApprovals? }
envVars accepts strings and { name, source: "local" | "remote" } objects.
A remote-source entry requires experimentalEnvironment: "remote".
Definition files reject unknown fields and definitions that mix stdio and HTTP
transport fields.
Do not pass plaintext bearer tokens or credential-bearing static headers.
Values named bearer_token, bearer-token, or bearerToken, static
Authorization, Cookie, API-key headers, and header values beginning with an
authentication scheme such as Bearer are rejected. Store the secret in an
environment variable and use bearerTokenEnvVar or envHttpHeaders.
Current advanced patch keys follow the official Codex config reference.
environment_id is rejected with guidance to use experimental_environment;
supports_parallel_tool_calls is rejected because it is not part of the
current MCP config contract.
patchServerText(content, name, keys) is exported for fixture tests and tools
that need to preview a table patch without touching disk.
How it works
Section titled “How it works”For add and remove, the module asks the installed codex mcp command to
generate or validate the operation inside a temporary isolated CODEX_HOME.
It then mutates only the target server in the requested config. This prevents
the Codex writer from reserializing or dropping advanced keys on unrelated MCP
servers. List and get still delegate directly to the installed codex binary.
Before a mutating operation, the module hashes the expected current config and copies it into an operation-specific directory under:
<CODEX_HOME>/codex-mcp-manager-state/backups/Each pending, committed, automatically restored, or rolled-back operation is recorded in:
<CODEX_HOME>/codex-mcp-manager-state/manifest.jsonlpatchServer is intentionally narrower than a TOML rewriter. It validates the
file, inserts or replaces only the requested keys inside the selected server
or tool table, validates again, and asks codex mcp get --json to parse the
result. Quoted table identifiers, multi-line existing values, inline tool
maps, and nested [mcp_servers.<name>.tools.<tool>] tables are handled without
reserializing unrelated parts of config.toml.
Add, patch, and remove verify the expected-current hash immediately before the atomic write and validate the result through Codex afterward. A validation failure restores the verified backup automatically only when no concurrent writer has changed the result. Concurrent changes are left intact and reported instead of being overwritten.
Replacing a server intentionally replaces that server’s own table subtree; comments inside the replaced subtree are not preserved. Unrelated server tables and top-level content remain byte-preserved.
See the official Codex MCP configuration reference for the source contract used by this module.
Uninstall and rollback
Section titled “Uninstall and rollback”Removing the npm package does not edit Codex configuration:
npm uninstall @codex-modules/mcp-managerTo undo the last change made by this module:
codex-mcp-manager rollbackFor an isolated home:
codex-mcp-manager rollback --codex-home /tmp/codex-homeRollback entries are one-use. Rollback requires the manifest target to exactly
match the selected config.toml, the current hash to match the committed
result, and the canonical backup to remain inside the manager’s backup
directory. It refuses to overwrite changes made after the operation.
If the CLI is unavailable, restore manually by copying the appropriate verified
backup from <CODEX_HOME>/codex-mcp-manager-state/backups/<operation-id>/ over
<CODEX_HOME>/config.toml. The manifest at
<CODEX_HOME>/codex-mcp-manager-state/manifest.jsonl records which file each
backup belongs to.
Attribution
Section titled “Attribution”Schema mapping was informed by jtianling/mcps-manager (MIT). Writer and
backup patterns were informed by Brightwing-Systems-LLC/mcp-manager (MIT).