codex - 💡(How to fix) Fix codex exec can silently complete empty when configured MCP tools are deferred behind tool_search

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

codex exec can silently finish with no assistant message when an explicitly configured MCP tool is deferred behind tool_search.

In the failing state, the MCP server is healthy and its tool is present in the runtime registry, but it is no longer directly exposed to the model. It only appears as a deferred source in tool_search. The model then emits only a reasoning item and response.completed; Codex accepts that as a completed turn and returns no final answer (last_agent_message = null).

This is observable as an empty codex exec result even though the task's first required action is to call the configured MCP tool.

Error Message

This makes non-interactive automation brittle. A user-local plugin or ChatGPT connector can change the tool exposure mode for an unrelated task-specific MCP server. Once that happens, tasks that rely on a configured MCP tool can complete successfully from Codex's perspective while producing no output and no useful error.

Root Cause

This makes non-interactive automation brittle. A user-local plugin or ChatGPT connector can change the tool exposure mode for an unrelated task-specific MCP server. Once that happens, tasks that rely on a configured MCP tool can complete successfully from Codex's perspective while producing no output and no useful error.

Code Example

CODEX_HOME="$TMP_HOME" codex exec --json --skip-git-repo-check -C /path/to/repo \
  -c 'developer_instructions="## Title Setting Protocol:\n- MUST set a descriptive title as your FIRST action when the user makes a request\n- Use the Agentrix event broker MCP tool for this: server \\\"agentrix_event_broker\\\", tool \\\"change_title\\\"\n- This is mandatory when the tool is available; do this before any other work"' \
  -c 'mcp_servers.agentrix_event_broker.url="http://127.0.0.1:PORT/mcp/agentrix-event-broker"' \
  -c 'mcp_servers.agentrix_event_broker.bearer_token_env_var="AGENTRIX_EVENT_MCP_TOKEN"' \
  'hello'

---

{"type":"thread.started", ...}
{"type":"turn.started"}
{"type":"turn.completed", "usage": {...}}

---

{"type":"response_item","payload":{"type":"reasoning", ...}}
{"type":"event_msg","payload":{"type":"task_complete","last_agent_message":null, ...}}
RAW_BUFFERClick to expand / collapse

Summary

codex exec can silently finish with no assistant message when an explicitly configured MCP tool is deferred behind tool_search.

In the failing state, the MCP server is healthy and its tool is present in the runtime registry, but it is no longer directly exposed to the model. It only appears as a deferred source in tool_search. The model then emits only a reasoning item and response.completed; Codex accepts that as a completed turn and returns no final answer (last_agent_message = null).

This is observable as an empty codex exec result even though the task's first required action is to call the configured MCP tool.

Environment

  • codex-cli 0.133.0
  • Source tag checked: rust-v0.133.0
  • macOS
  • Auth mode: ChatGPT auth (auth_mode = "chatgpt")
  • features.apps enabled by default
  • A model/provider that supports the Responses API tool_search tool

Triggering state

The issue reproduces when all of these are true:

  1. ChatGPT auth is active, so Codex enables host-owned codex_apps.
  2. An app connector contributes many tools. In my case the GitHub connector contributed 90 tools.
  3. A bundled plugin MCP is installed/enabled. In my case: plugins/cache/openai-bundled/computer-use/1.0.799/.mcp.json
  4. A task-specific MCP server is configured, e.g. agentrix_event_broker with a change_title tool.
  5. Developer instructions require that MCP tool as the first action.
  6. Run codex exec --json.

The combined MCP tool count crosses DIRECT_MCP_TOOL_EXPOSURE_THRESHOLD = 100, causing Codex to defer MCP tools behind tool_search.

Minimal shape of the command

This uses a temporary CODEX_HOME copied from a ChatGPT-authenticated setup and then adds a task-specific MCP server via config overrides:

CODEX_HOME="$TMP_HOME" codex exec --json --skip-git-repo-check -C /path/to/repo \
  -c 'developer_instructions="## Title Setting Protocol:\n- MUST set a descriptive title as your FIRST action when the user makes a request\n- Use the Agentrix event broker MCP tool for this: server \\\"agentrix_event_broker\\\", tool \\\"change_title\\\"\n- This is mandatory when the tool is available; do this before any other work"' \
  -c 'mcp_servers.agentrix_event_broker.url="http://127.0.0.1:PORT/mcp/agentrix-event-broker"' \
  -c 'mcp_servers.agentrix_event_broker.bearer_token_env_var="AGENTRIX_EVENT_MCP_TOKEN"' \
  'hello'

Important detail: the configured MCP server is not the problem. If I remove the bundled computer-use plugin MCP from the same temp CODEX_HOME, the model directly calls agentrix_event_broker.change_title and then replies normally.

Actual behavior

With the bundled computer-use plugin present, stdout JSONL contains only:

{"type":"thread.started", ...}
{"type":"turn.started"}
{"type":"turn.completed", "usage": {...}}

The rollout/session file contains only a model reasoning item followed by task completion:

{"type":"response_item","payload":{"type":"reasoning", ...}}
{"type":"event_msg","payload":{"type":"task_complete","last_agent_message":null, ...}}

No tool_search call is emitted. No MCP tool call is emitted. No assistant message is emitted.

Expected behavior

One of these should happen instead:

  • Explicitly configured MCP servers/tools required by developer instructions remain directly visible, even when other MCP tools are deferred.
  • Or Codex should automatically make a tool_search discovery step available/required before treating the turn as complete.
  • Or codex exec should fail loudly with a diagnostic instead of returning an empty successful turn when the model completes without an assistant message and without satisfying a required tool path.

At minimum, a configured MCP server should not become effectively invisible just because unrelated app/plugin tools pushed the total MCP tool count over a threshold.

Evidence from request shape

I captured the outbound Responses request in both cases.

Without the bundled computer-use plugin:

  • Direct namespace exists: mcp__agentrix_event_broker__
  • Child tool exists: change_title
  • The model calls the MCP tool first and then replies.

With the bundled computer-use plugin:

  • The request had 17 top-level tools.
  • There were no direct MCP namespaces at all.
  • tool_search was present.
  • The tool_search description listed deferred sources including:
    • GitHub
    • agentrix_event_broker
    • computer-use
    • node_repl
  • The model did not call tool_search; it emitted only reasoning and completed.

So this is not an MCP startup/discovery failure. It is a model-visible tool exposure / deferred-tool recovery failure.

Relevant source path

From rust-v0.133.0:

  • codex-rs/codex-mcp/src/mcp/mod.rs
    • host_owned_codex_apps_enabled(config, auth) returns true when config.apps_enabled && auth.uses_codex_backend().
  • codex-rs/core/src/session/turn.rs
    • built_tools() calls list_all_tools() and then build_mcp_tool_exposure(...).
  • codex-rs/core/src/mcp_tool_exposure.rs
    • DIRECT_MCP_TOOL_EXPOSURE_THRESHOLD = 100
    • when search_tool_enabled and the deferred MCP tool count crosses the threshold, direct_tools becomes empty and all MCP tools become deferred.
  • codex-rs/core/src/tools/spec_plan.rs
    • only direct exposures are added to model-visible specs.
    • deferred tools are added to the registry and surfaced through ToolSearchHandler.

That means the configured MCP tool is technically callable after discovery, but it is not directly visible to the model. In exec, the model is still allowed to end the turn after producing only reasoning, yielding an empty result.

Why this matters

This makes non-interactive automation brittle. A user-local plugin or ChatGPT connector can change the tool exposure mode for an unrelated task-specific MCP server. Once that happens, tasks that rely on a configured MCP tool can complete successfully from Codex's perspective while producing no output and no useful error.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

One of these should happen instead:

  • Explicitly configured MCP servers/tools required by developer instructions remain directly visible, even when other MCP tools are deferred.
  • Or Codex should automatically make a tool_search discovery step available/required before treating the turn as complete.
  • Or codex exec should fail loudly with a diagnostic instead of returning an empty successful turn when the model completes without an assistant message and without satisfying a required tool path.

At minimum, a configured MCP server should not become effectively invisible just because unrelated app/plugin tools pushed the total MCP tool count over a threshold.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

codex - 💡(How to fix) Fix codex exec can silently complete empty when configured MCP tools are deferred behind tool_search