[URGENT] Stable IDE extension ships a prerelease CLI with under-development "Code mode" silently active, breaking any long-running MCP calls and burning tokens while waiting
What version of the IDE extension are you using?
26.616.31447 (bundled codex-cli 0.142.0-alpha.1)
What subscription do you have?
Plus
Which IDE are you using?
VS Code
What platform is your computer?
Microsoft Windows NT 10.0.26100.0 x64
What issue are you seeing?
The just-released, non-prerelease VS Code extension 26.616.31447 bundles codex-cli 0.142.0-alpha.1 and routes MCP tools through code-mode exec cells even though I never enabled or opted into code mode. This silently changes the execution contract in four serious ways:
- an under-development mode is activated without consent
- the model now controls whether a call remains alive
- merely waiting for long-running mcp call return consumes tokens and context
- the only available mitigation depends on an undocumented Codex-generated namespace that MCP servers cannot control.
In detail:
1. Under-development code mode is activated without opt-in
The published Codex sample configuration describes code mode as "under development and off by default." At the time of the failure, my config did not contain [features.code_mode], the affected binary reported code_mode ... false in codex features list, and the installed IDE extension was a regular release rather than a prerelease. Nevertheless, the affected IDE thread exposed MCP tools only through code-mode exec.
The extension itself is not marked as a preview or prerelease, but it bundles a CLI whose codex-package.json version is 0.142.0-alpha.1; OpenAI's corresponding rust-v0.142.0-alpha.1 release is explicitly classified as a prerelease. The IDE does not offer a separate bundled-CLI channel selection, so this prerelease executable arrived through the regular extension without any user opt-in.
Codex PR #25031 indicates that model-catalog tool_mode metadata can select code_mode or code_mode_only and override local feature flags. If that mechanism caused this behavior, an under-development execution mode with materially different tool-lifetime and token semantics was activated remotely without user consent, a visible migration warning, or an obvious local control that takes precedence.
2. MCP call lifetime is now controlled by the model for all MCPs by default
Previously, a direct MCP call remained blocked in the runtime until the tool completed, the configured tool_timeout_sec expired, the transport failed, or the user cancelled it. The model had no decision point while the call was pending.
In code mode, exec yields while the nested MCP call is still healthy. After every yield, the model must choose whether to issue another wait or terminate: true. The MCP server cannot set yield_time_ms, cannot prevent termination, and cannot tell the code-mode runtime that a multi-hour duration is expected. The configured MCP timeout remains only an upper bound; it no longer gives the call an uninterrupted runtime wait up to that deadline. Correct call lifetime now depends on the model repeatedly making the right scheduling decision without enough information to do so reliably.
To be precise, tool_timeout_sec itself is still user-configured and host-enforced as a maximum; it is not selected by the model. What the model now controls is each code-mode yield_time_ms and the decision to call terminate: true. There is no MCP server option that binds those decisions to tool_timeout_sec or prevents an earlier model-authored termination.
3. Waiting is now a token-consuming model loop
The initial exec yield defaults to 10 seconds unless the model writes a different yield_time_ms pragma. Every subsequent wait interval is also selected by the model and independently defaults to 10 seconds. Each yield produces a model-visible tool output such as Script running with cell ID ..., triggers another model inference, and appends both the wait call and its output to the conversation history.
A 10-hour MCP call using the 10-second defaults can therefore create roughly 3,600 additional model turns and 3,600 persistent call/output pairs. The old direct-MCP path used no model turns or intermediate transcript tokens while the call was pending. The new path can consume token/rate limits, grow the context until compaction or exhaustion, and expose the healthy call to thousands of unnecessary model-authored termination decisions.
This looks designed for interactive orchestration, but long-running blocking tools were not handled safely.
4. The mitigation depends on an unstable, under-documented, internal mcp___ prefix / namespace.
The only deterministic mitigation currently available is features.code_mode.direct_only_tool_namespaces. It requires an exact model-visible namespace such as mcp__ask_human, but that is not a name published by the MCP server. Codex derives it from the client-side [mcp_servers.<name>] key, replaces unsupported characters, adds an internally generated mcp__ prefix, and then performs an exact string match. The MCP server cannot discover the resulting name, request direct execution, or declare that its calls are intentionally long-running.
This derivation is not defined in the MCP documentation as a stable contract. The official sample contains an example value but does not explain how users or server authors should determine it. The current Codex source calls mcp__ a historical prefix, and the affected CLI exposes a separate under-development non_prefixed_mcp_tool_names feature. A naming change could therefore silently stop the mitigation from matching.
Requiring every user to reverse-engineer and maintain a client-generated namespace is not a safe design for preserving tool-call lifetime. This must either remain runtime-controlled by the configured timeout and explicit user cancellation, or Codex needs a documented, discoverable, stable configuration contract and an MCP-level mechanism through which a server can declare the required execution behavior.
Concrete failure reproducer
The concrete reproducer was ask-human - a human-in-the-loop MCP that is intentionally designed to keep a tool call pending as many times as the user needs before providing the answer. It makes all four problems easy to observe, though none is specific to that tool: the same code-mode control path can affect any long-running MCP operation, such as a deployment, data import, remote job, or approval workflow.
In the example given, the MCP tool and Codex MCP client were both configured with explicit 86400-second timeouts. The user did not answer or cancel the prompt, and the MCP tool did not return a timeout or error.
After an intermediate 240-second wait yielded Script running with cell ID 6, the model issued a second wait call with terminate: true. This cancelled the still-valid MCP call and the agent then repeated the question as ordinary chat text.
The VS Code UI displayed Tool returned no content for the nested MCP call even though the rollout recorded that the exec cell was still running.
Sanitized rollout timeline (UTC):
14:35:45 exec starts tools.mcp__ask_human__ask_human(...)
14:35:55 exec yields: Script running with cell ID 6
14:35:58 wait({ cell_id: "6", yield_time_ms: 240000 })
14:39:58 wait yields: Script running with cell ID 6
14:40:07 wait({ cell_id: "6", terminate: true, ... })
14:40:07 Script terminated
14:40:19 agent ends turn and repeats the unanswered question
There is no mcp_tool_call_end event for the failed call. The explicit terminate: true call is model-authored in the rollout; it was not caused by a user action or MCP timeout.
What steps can reproduce the bug?
- Configure a stdio MCP server with any tool that legitimately runs longer than 4 minutes. A human-approval tool like this one is a convenient reproducer, but the operation can also be a long-running job with deterministic completion.
- Set
tool_timeout_secto a long value, for example:
``toml``
[mcp_servers.example]
command = "..."
tool_timeout_sec = 86400
- Start a new Codex IDE thread using extension
26.616.31447, bundled CLI0.142.0-alpha.1, and GPT-5.5 with xhigh reasoning. - Prompt Codex in a way so that it calls the long-running MCP tool. In this configuration Codex invokes it through code mode.
- Do not complete or cancel the MCP operation for at least 4 minutes.
Anyway, this appears to be model-dependent rather than a deterministic runtime timeout. In a second 0.142.0-alpha.1 session, answering within 58 seconds succeeded after two model-authored 30-second wait calls. That successful call still required extra model turns solely to keep waiting. The core regression is that every code-mode yield both consumes another inference and creates another decision point at which the model can terminate a healthy nested tool.
What is the expected behavior?
An under-development execution mode must not be silently enabled in a regular IDE release when the user has not opted in and the local feature state reports it as disabled. If remote model metadata can select code mode, that behavior and its changed tool-execution semantics need to be documented, visible, and subject to a reliable user-controlled override.
A safety-critical override must not depend on reverse-engineering an undocumented generated namespace. Codex should either preserve direct runtime-managed behavior automatically for pending MCP calls, provide a stable and discoverable client configuration keyed by the configured MCP server name, or support server-declared execution requirements through the protocol.
Codex must not terminate a nested MCP call solely because an exec/wait interval yielded while the call is still running. It should preserve the call until one of these occurs:
- the nested tool completes;
- the configured MCP timeout actually expires;
- the user explicitly cancels; or
- the transport reports a real failure.
Long-running nested tool calls should ideally remain runtime-managed rather than requiring repeated model decisions. At minimum, a running-cell yield should be unambiguously represented to both the model and UI as an intermediate state, not Tool returned no content.
No model inference should be required merely because a tool is still running, and runtime status updates should not accumulate as model-visible conversation history. The configured MCP timeout should remain the host-side deadline unless the user cancels or a real failure occurs.
Additional information
- The official sample config calls code mode "under development and off by default," but the affected thread used it while the local feature state reported it disabled. PR #25031 documents model-catalog tool-mode selection overriding feature flags, which appears to explain the otherwise unexpected activation.
- With extension
26.609.30741/codex-cli 0.140.0-alpha.2, Ask Human was invoked as a direct MCP call. Recorded calls remained pending and then completed successfully after the user responded 65, 88, and 171 minutes later, without an intervening model decision about whether to terminate them. - Extension
26.616.30709also bundlescodex-cli 0.142.0-alpha.1, but I did not reproduce the failure on that exact extension build. The affected run was on26.616.31447. - Neither the MCP server nor
tool_timeout_seccontrols code-modeyield_time_ms. The model authors the optional initialexecpragma and every subsequentwaitcall; omitted values independently default to 10 seconds. - The affected
0.142.0-alpha.1binary contains the under-developmentfeatures.code_mode.direct_only_tool_namespacesoption, which can keep a configured MCP namespace out ofexec. This is a useful per-client mitigation for known special cases, but the MCP server cannot request it, users must identify and configure each namespace manually, and it does not fix the generic long-running MCP execution regression. - After adding
direct_only_tool_namespaces = ["mcp__ask_human"]and restarting the IDE session, the same tool was invoked directly, remained pending for 298.6 seconds, and completed successfully without a code-mode yield,waitcall, or intervening model decision. This confirms that the mitigation works in the affected build while also demonstrating that correct lifetime now depends on per-client knowledge of the generated namespace. - MCP progress notifications are not a mitigation: a server can report that work is still pending when the client supplies a progress token, but those notifications do not control code-mode yields, prevent
terminate: true, or bind the cell lifetime totool_timeout_sec. - The MCP server's own 86400-second deadline was shown to the user and had not expired.
- A local workaround is to clarify agent instructions that
Script running with cell ID ...is not a tool timeout and must not triggerterminate: true. - I also rolled back and pinned extension version
26.609.30741.
A full session ID and raw rollout can be supplied privately
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗