Codex desktop can show MCP enabled while thread tool surface omits llm-tldr/serena

Open 💬 3 comments Opened Apr 3, 2026 by fengning-starsend
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

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-tldr and serena were configured in ~/.codex/config.toml
  • codex mcp list reported both as enabled
  • 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_tools missing: 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 list pass
  • but the model still cannot use the tool

That wastes review/QA/investigation turns and makes MCP health checks misleading.

Suggested fix

One of:

  1. ensure enabled MCP tools are hydrated into thread dynamic tool state at thread start
  2. 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.

View original on GitHub ↗

3 Comments

github-actions[bot] contributor · 4 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #15268
  • #15508
  • #15267

Powered by Codex Action

fengning-starsend · 4 months ago

Follow-up from the local mitigation side:

I merged a local guardrail in my own DX tooling that checks both:

  1. codex mcp list
  2. Codex thread dynamic tool state in ~/.codex/state_5.sqlite

That guardrail is now verified on canonical master, and it reproduces the false-green condition cleanly.

Canonical verification

DX_CHECK_RUNTIME=codex ./scripts/dx-check.sh now emits:

  • ✅ MCP preflight passed (codex): llm-tldr + serena visible
  • ❌ Codex thread-surface check failed: recent thread state is missing required MCP tools:llm-tldr serena
  • Observed 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.sh now emits:

  • - codex-cli thread tool surface: ⚠️ recent thread state missing:llm-tldr serena (observed: <none>)

Direct helper output from canonical master:

{"status": "fail", "reason": "missing_required_thread_tools", "state_db": "/Users/fengning/.codex/state_5.sqlite", "cwd": "/Users/fengning/agent-skills", "thread_id": "019d546d-fe2c-7183-a18c-eb9562dd3d53", "thread_age_hours": 0.0, "required": ["llm-tldr", "serena"], "observed": [], "missing": ["llm-tldr", "serena"]}

This feels related to #15268 and #15508, but the extra useful invariant here is:

  • Codex can report MCP enabled
  • while thread dynamic tool registration is empty for the current workspace thread

So even if this is the same underlying bug family, thread_dynamic_tools seems like a good concrete place to instrument or assert against internally.

fengning-starsend · 4 months ago

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:

select id, datetime(created_at,'unixepoch','localtime'), datetime(updated_at,'unixepoch','localtime'), source, cwd
from threads
where id='019cb94c-4c00-7f62-942f-8d15618d56de';

Result:

  • thread id: 019cb94c-4c00-7f62-942f-8d15618d56de
  • created: 2026-03-04 06:41:51 local time
  • updated: 2026-04-03 ...
  • source: vscode
  • cwd: /Users/fengning/agent-skills

So this is an older resumed thread, not a newly created desktop-native thread.

Separately, my ~/.codex/config.toml backup history shows llm-tldr / serena MCP config present by at least 2026-03-15, and multiple codex MCP rewrites happened again on 2026-04-01.

That makes the likely root cause more specific:

  • the thread predates the MCP config that should expose these tools
  • Codex is not rehydrating/rebinding MCP-backed dynamic tools into this existing/resumed thread
  • codex mcp list reflects current config/server state, but the old thread keeps its stale/empty tool surface

This lines up pretty closely with the general family in #15268 / #15508, but the extra local proof here is the timeline:

  • old thread
  • newer MCP config
  • no thread rebind

So my current best diagnosis is: existing/resumed threads are not getting MCP tool hydration refreshed after config/tool availability changes.