Codex desktop can show MCP enabled while thread tool surface omits llm-tldr/serena
Summary
Codex desktop can report MCP servers as enabled in codex mcp list while still failing to expose those tools to the model in the live thread.
In my case:
llm-tldrandserenawere configured in~/.codex/config.tomlcodex mcp listreported both asenabled- backing MCP processes were alive
- but the thread's dynamic tool state did not include either tool
This silently degraded the session because the model could not call the tools even though the runtime looked healthy.
Environment
- Codex desktop app
- app version seen in local logs:
0.118.0-alpha.2 - macOS
- auth mode in logs:
Chatgpt
Exact behavior
Config present
~/.codex/config.toml contains:
[mcp_servers."llm-tldr"][mcp_servers."serena"]
MCP list looks healthy
codex mcp list returned both entries as enabled.
Processes alive
Process list showed active tldr-mcp-contained.py processes.
But thread tool state was empty for those tools
I inspected Codex's local state DB:
select distinct name from thread_dynamic_tools order by name;
Result:
read_thread_terminal
No llm-tldr.
No serena.
And:
select count(*) from thread_dynamic_tools where name in ('llm-tldr','serena');
Result:
0
For the active workspace thread, joined dynamic-tool names were empty.
Repro evidence
Commands used:
codex mcp list
ps aux | rg 'tldr-mcp-contained|tldr-contained-daemon|codex'
sqlite3 ~/.codex/state_5.sqlite "select distinct name from thread_dynamic_tools order by name;"
sqlite3 ~/.codex/state_5.sqlite "select count(*) from thread_dynamic_tools where name in ('llm-tldr','serena');"
sqlite3 ~/.codex/state_5.sqlite "select t.id, datetime(t.updated_at,'unixepoch','localtime'), t.cwd, group_concat(d.name, ',') from threads t left join thread_dynamic_tools d on d.thread_id=t.id where t.cwd='/Users/fengning/agent-skills' group by t.id order by t.updated_at desc limit 10;"
Concrete values from the failing case:
- workspace:
/Users/fengning/agent-skills - thread id:
019cb94c-4c00-7f62-942f-8d15618d56de thread_dynamic_toolsmissing:llm-tldr,serena
Expected behavior
If Codex reports MCP tools as enabled and their backing processes are alive, the live thread should expose those tools to the model.
If that registration fails, Codex should surface a clear runtime error instead of silently degrading.
Likely fault boundary
This does not look like a launcher/config/daemon failure.
It looks like a gap between:
- MCP server enablement / process health
- and thread dynamic tool registration
Why this matters
This creates a false-green state:
- operators see
codex mcp listpass - but the model still cannot use the tool
That wastes review/QA/investigation turns and makes MCP health checks misleading.
Suggested fix
One of:
- ensure enabled MCP tools are hydrated into thread dynamic tool state at thread start
- or fail loudly when thread tool registration does not match enabled MCP state
At minimum, Codex should expose a diagnostic when codex mcp list is healthy but the current thread has no corresponding dynamic tools.
3 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Follow-up from the local mitigation side:
I merged a local guardrail in my own DX tooling that checks both:
codex mcp list~/.codex/state_5.sqliteThat guardrail is now verified on canonical
master, and it reproduces the false-green condition cleanly.Canonical verification
DX_CHECK_RUNTIME=codex ./scripts/dx-check.shnow emits:✅ MCP preflight passed (codex): llm-tldr + serena visible❌ Codex thread-surface check failed: recent thread state is missing required MCP tools:llm-tldr serenaObserved dynamic tools: <none>Diagnosis: codex mcp list can be green while the live thread tool surface is still missing llm-tldr/serena../health/mcp-doctor/check.shnow emits:- codex-cli thread tool surface: ⚠️ recent thread state missing:llm-tldr serena (observed: <none>)Direct helper output from canonical
master:This feels related to #15268 and #15508, but the extra useful invariant here is:
So even if this is the same underlying bug family,
thread_dynamic_toolsseems like a good concrete place to instrument or assert against internally.One more concrete data point from deeper desktop diagnosis:
The failing thread in my case is not a fresh thread.
From
~/.codex/state_5.sqlite:Result:
019cb94c-4c00-7f62-942f-8d15618d56de2026-03-04 06:41:51local time2026-04-03 ...vscode/Users/fengning/agent-skillsSo this is an older resumed thread, not a newly created desktop-native thread.
Separately, my
~/.codex/config.tomlbackup history showsllm-tldr/serenaMCP config present by at least2026-03-15, and multiple codex MCP rewrites happened again on2026-04-01.That makes the likely root cause more specific:
codex mcp listreflects current config/server state, but the old thread keeps its stale/empty tool surfaceThis lines up pretty closely with the general family in #15268 / #15508, but the extra local proof here is the timeline:
So my current best diagnosis is: existing/resumed threads are not getting MCP tool hydration refreshed after config/tool availability changes.