install deletes foreign tables placed between the managed MCP markers in ~/.codex/config.toml (0.10.4 → 0.11.0 upgrade)
Version
- Installed by
install.sh:codebase-memory-mcp 0.11.0(upgrading from0.10.4) - Codex CLI 0.154.0 / Codex Desktop present on the same machine
Platform
macOS 15 (Darwin 24.6.0), Intel (darwin-amd64)
Install channel
GitHub release archive via install.sh (bash ~/.local/bin/install.sh, installs to ~/.local/bin)
Binary variant
standard
What happened, and what did you expect?
codebase-memory-mcp install (run by install.sh during the 0.10.4 → 0.11.0 upgrade) rewrote the managed # >>> codebase-memory-mcp MCP >>> … # <<< codebase-memory-mcp MCP <<< region of ~/.codex/config.toml and deleted every table that other software had placed between those two markers: [mcp_servers.node_repl] (+ its .env table), [desktop], and all four [marketplaces.*] tables. ~/.codex/config.toml went from 5,841 bytes to 4,371 bytes. Codex Desktop lost its marketplaces, desktop settings and its bundled node_repl MCP server. Nothing was printed for Codex; the install summary reported mcp: ~/.codex/config.toml as if it had succeeded.
How the foreign tables got inside the managed region: the 0.10.4 install had appended its block at the end of the file, so the closing marker was the last line. Codex Desktop later appended its own tables, and the trailing comment line stayed last (apparently its TOML editor treats a trailing comment as attached to the document end — I did not verify that in Codex itself), which left the closing marker below Codex's tables. From that point on, the region between the markers contained one cbm-owned table and four foreign ones.
Expected: the installer should never remove a table it did not write. cbm_toml_upsert_managed_block should fail closed (as toml_find_markers already does for orphan markers, #1558) when the span between its markers contains any table header other than [mcp_servers.codebase-memory-mcp] — or, better, replace only the owned table and keep the rest of the span verbatim. The user-added key inside the owned table (startup_timeout_sec = 90, needed because the MCP server takes >10 s to start on a 1.5 GB index) was also dropped; preserving unknown keys, or at least printing a note, would be kinder.
Reproduction
The real-world trigger was simply bash ~/.local/bin/install.sh on a machine whose ~/.codex/config.toml had foreign tables between the cbm markers (state shown in the diff below).
Sandboxed steps that should reproduce it without touching a real Codex configuration (HOME, CODEX_HOME, TMPDIR and the cbm cache/runtime dirs all point at a temp directory). Caveat: I could not execute this on the affected machine — with a live cbm daemon running, install stops with activation could not reserve exclusive access; no activation was committed, because the version-cohort locks live in /private/tmp/cbm-daemon-<uid>/ and are account-wide rather than under CBM_CACHE_DIR/CBM_RUNTIME_DIR. It should run on a machine (or CI job) with no daemon. The evidence for the bug is the real diff and the code path, both below.
sandbox="$(mktemp -d)"
mkdir -p "$sandbox/.codex" "$sandbox/bin" "$sandbox/cache" "$sandbox/runtime" "$sandbox/tmp"
cat > "$sandbox/.codex/config.toml" <<'EOF'
model = "gpt-5"
[mcp_servers.codegraph]
command = "codegraph"
args = ["serve", "--mcp"]
# >>> codebase-memory-mcp MCP >>>
[mcp_servers.codebase-memory-mcp]
command = "/tmp/old/codebase-memory-mcp"
args = []
startup_timeout_sec = 90
[mcp_servers.node_repl]
args = []
command = "/Applications/ChatGPT.app/Contents/Resources/cua_node/bin/node_repl"
startup_timeout_sec = 120
[desktop]
followUpQueueMode = "steer"
[marketplaces.openai-codex]
source_type = "git"
source = "https://github.com/openai/codex-plugin-cc.git"
# <<< codebase-memory-mcp MCP <<<
EOF
cp "$sandbox/.codex/config.toml" "$sandbox/before.toml"
HOME="$sandbox" CODEX_HOME="$sandbox/.codex" XDG_CONFIG_HOME="$sandbox/.config" TMPDIR="$sandbox/tmp" \
CBM_CACHE_DIR="$sandbox/cache" CBM_RUNTIME_DIR="$sandbox/runtime" \
codebase-memory-mcp install -y --dir="$sandbox/bin" --clients=codex
diff "$sandbox/before.toml" "$sandbox/.codex/config.toml"Expected result (matches what happened for real): [mcp_servers.node_repl], [desktop] and [marketplaces.openai-codex] are gone; only the regenerated [mcp_servers.codebase-memory-mcp] table remains between the markers.
Logs
Diff of the real file, before → after the upgrade (values in node_repl.env elided, $HOME written as ~). Lines 133–178 were replaced by the single env_vars line:
133,178c139
< startup_timeout_sec = 90
<
< [mcp_servers.node_repl]
< args = []
< command = "/Applications/ChatGPT.app/Contents/Resources/cua_node/bin/node_repl"
< startup_timeout_sec = 120
<
< [mcp_servers.node_repl.env]
< NODE_REPL_NATIVE_PIPE_CONNECT_TIMEOUT_MS = "…"
< … (12 more env keys)
<
< [desktop]
< followUpQueueMode = "steer"
< external-agent-import-sync-item-types = "all"
< external-agent-import-sync-enabled = true
< conversationDetailMode = "STEPS_PROSE"
< sansFontSize = 14
< codeFontSize = 13
< ambient-suggestions-enabled = true
<
< [marketplaces.openai-bundled]
< source_type = "local"
< source = "~/.codex/.tmp/bundled-marketplaces/openai-bundled"
<
< [marketplaces.openai-primary-runtime]
< source_type = "local"
< source = "~/.cache/codex-runtimes/codex-primary-runtime/plugins/openai-primary-runtime"
<
< [marketplaces.claude-plugins-official]
< source_type = "git"
< source = "https://github.com/anthropics/claude-plugins-official.git"
<
< [marketplaces.openai-codex]
< source_type = "git"
< source = "https://github.com/openai/codex-plugin-cc.git"
---
> env_vars = ["CBM_CACHE_DIR", "CBM_RUNTIME_DIR"]Installer output for the Codex section (no warning about the removed tables):
Codex CLI:
mcp: ~/.codex/config.toml
instructions: ~/.codex/AGENTS.md (managed activation pointer)
...
hooks: SessionStart + SubagentStart (dynamic graph context)Where it happens: src/cli/config_toml_edit.c, cbm_toml_upsert_managed_block() copies existing[0 .. begin_line.start), appends the fresh managed block, then copies existing[end_line.full_end ..). Everything between the two marker lines is discarded, and toml_managed_block_conflicts() only inspects the text outside that span, so foreign tables inside it are neither detected nor preserved.
Project scale (if relevant)
Not index related. (For context: the machine's main index is 567 k nodes / 707 k edges, 1.5 GB.)
Related observations from the same upgrade (not the main report)
~/.codex/hooks.json: the twohook-augmententries had been edited locally to prefixCBM_HOOK_DEADLINE_MS=20000(see next bullet). The installer treated them as foreign, preserved them, and appended its own canonicalSessionStartandSubagentStartgroups, so Codex ended up with each hook twice. Matching on the binary path +hook-augmenttoken, rather than the exact command string, would avoid the duplicate.- Feature ask:
hook-augmenthas a built-in 2000 ms deadline (only overridable via theCBM_HOOK_DEADLINE_MSenv var). On this 1.5 GB index a hook query takes 4–6 s even with a warm daemon, so every hook silently exited empty (127deadline_exceededlines inlogs/hook-augment-timeouts.log) until the shims were hand-edited — which then makes them "not ours" for the installer. Aconfig set hook_deadline_mssetting honoured by the managed shims would avoid the whole conflict.
Confirmations
- I searched existing issues (#570, #1432 and #1558 are about hook duplication / orphan markers; none cover foreign tables inside the managed MCP region).
- The sandboxed steps above do not modify a real Codex configuration; the real-file diff was taken from a snapshot made two minutes before the upgrade and the file has since been restored by hand.
Source: DeusData/codebase-memory-mcp