Regression: non-codex_apps MCP tool calls block on app-tool approval prompt
Summary
Non-codex_apps MCP tool calls (for example rmcp test servers) are entering the app-tool approval flow and emitting RequestUserInput ("Approve app tool call?"). In non-interactive tests this causes timeouts.
Repro
Run:
cargo test -p codex-core --test all -- --test-threads=1 \
suite::rmcp_client::stdio_image_responses_are_sanitized_for_text_only_model \
suite::rmcp_client::stdio_image_responses_round_trip \
suite::rmcp_client::stdio_server_propagates_whitelisted_env_vars \
suite::rmcp_client::stdio_server_round_trip \
suite::sqlite_state::mcp_call_marks_thread_memory_mode_polluted_when_configured \
suite::truncation::mcp_image_output_preserves_image_and_no_text_summary \
suite::truncation::mcp_tool_call_output_exceeds_limit_truncated_for_model \
suite::truncation::mcp_tool_call_output_not_truncated_with_custom_limit
Observed: all 8 timeout in core/tests/common/lib.rs waiting for MCP events.
With additional instrumentation in wait_for_event_with_timeout, the first/only event seen was:
RequestUserInput(RequestUserInputEvent { ... header: "Approve app tool call?", ... })
for suite::rmcp_client::stdio_server_round_trip (server is rmcp, not codex_apps).
Expected
Non-codex_apps MCP servers should not enter app-tool approval prompting. They should preserve prior non-interactive behavior in these tests.
Suspected cause
In core/src/mcp_tool_call.rs, maybe_request_mcp_tool_approval(...) is called unconditionally for MCP calls, rather than only for server == CODEX_APPS_MCP_SERVER_NAME.
Minimal fix
Gate the approval flow by server:
if server == CODEX_APPS_MCP_SERVER_NAME
&& let Some(decision) = maybe_request_mcp_tool_approval(...)
{
...
}
This made the 8 tests above pass locally again.
10 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Follow-up with exact introducing change:
The behavior change that appears to introduce this regression is commit
32c4993c8a17cb0608d747f268e3d543745de962:fix(core): default approval behavior for mcp missing annotations (#15519)Why this is the likely introducer:
let approval_required = annotations.is_some_and(requires_mcp_tool_approval);let approval_required = requires_mcp_tool_approval(annotations);requires_mcp_tool_approvalto treat missing annotations as approval-required defaults.So when MCP metadata/annotations are not found at approval-check time (e.g., metadata is
None), approval now becomes required and the flow emitsRequestUserInput.That matches the observed timeout signature in these non-interactive tests (
RequestUserInputprompt appears and tests block).There is an older related commit (f6fd4cb3f57a8fcc709541686322db689b9172ae) that removed a codex_apps-only approval gate for custom MCP servers, but the concrete regression observed here (prompting on missing metadata) aligns with 32c4993c8....
I reviewed these related reports:
They appear related at the product/UX level (approval prompts for MCP tools), but this issue adds a deterministic core test regression repro using
codex-coretest suites andrmcptest servers (not only Playwright/Desktop usage).So this report is intended to track the lower-level regression in MCP approval routing that can deadlock non-interactive flows/tests via
RequestUserInput.If maintainers prefer, I’m happy to consolidate into an existing issue and close this one.
I prepared a fix for this on a public fork branch:
Repro summary:
codex_appsMCP tool calls can hit the app-tool approval flow and emitRequestUserInput, which blocks the non-interactivecodex-core/rmcptest paths listed in this issue.Fix approaches considered:
codex_appsgate at thehandle_mcp_tool_call(...)call site.maybe_request_mcp_tool_approval(...).Chosen fix:
maybe_request_mcp_tool_approval(...)unlessinvocation.server == CODEX_APPS_MCP_SERVER_NAME, and adds a regression test proving dangerous non-codex_appstools skip the approval flow.Root cause:
codex_appsMCP invocations, so custom MCP servers could trigger approval prompting that should have remained scoped to the apps server path.Validation:
just fmtgit diff --checkcargo test -p codex-core --lib non_codex_apps_servers_skip_approval_flow_even_for_dangerous_tools -- --nocapturecargo test -p codex-core --test all -- --test-threads=1 suite::rmcp_client::stdio_image_responses_are_sanitized_for_text_only_model suite::rmcp_client::stdio_image_responses_round_trip suite::rmcp_client::stdio_server_propagates_whitelisted_env_vars suite::rmcp_client::stdio_server_round_trip suite::sqlite_state::mcp_call_marks_thread_memory_mode_polluted_when_configured suite::truncation::mcp_image_output_preserves_image_and_no_text_summary suite::truncation::mcp_tool_call_output_exceeds_limit_truncated_for_model suite::truncation::mcp_tool_call_output_not_truncated_with_custom_limitI also tried to open a PR from this branch, but GitHub rejected PR creation for my account (
CreatePullRequestpermission denied). If a maintainer wants this as an invited PR, I can open it immediately from the branch above.Can reproduce this with user-added stdio MCP servers in the desktop app.
Environment:
Observed behavior:
approval_policy = "never"require_approval = "never"Expected behavior:
Additional signal:
This matches the issue description: approval flow seems to be incorrectly applied to non-codex_apps MCP invocations.
Follow-up on the new reproduction from
@preudomme-sys: it matches the same routing bug that the prepared branch already targets.Why it lines up:
codex_appsstdio MCP path (Serena,mcp-local-rag)codex_appsinvocations can enter itPrepared fix branch and compare:
That branch was validated against the original
codex-core/rmcpregression tests, and this newer desktop/stdin repro is consistent with the same root cause rather than a separate issue.If a maintainer wants it as an invited PR, I can open it directly from that branch.
Are you able to repro this on the latest version of the CLI (0.118.0 or newer)? We've made major changes to the TUI implementation since this bug was filed, and I'm pretty sure that it has been fixed.
Fresh follow-up on current
main(e5f4d1fef59a3309339394575052c7cc1fff0996).I rechecked this in a fresh local clone and re-ran representative tests from the issue list:
cargo test -p codex-core --test all suite::rmcp_client::stdio_server_round_trip -- --nocapturecargo test -p codex-core --test all suite::sqlite_state::mcp_call_marks_thread_memory_mode_polluted_when_configured -- --nocapturecargo test -p codex-core --test all suite::truncation::mcp_tool_call_output_not_truncated_with_custom_limit -- --nocaptureCurrent result in this Codex sandbox:
Skipping test because it cannot execute when network is disabled in a Codex sandbox.and exitokSource check on the same commit:
codex-rs/core/src/mcp_tool_call.rson currentmainstill does not contain the non-codex_appsearly return from the prepared branchmain: https://github.com/openai/codex/compare/main...s1korrrr:codex:feat/andrzej_mcp-tool-approval-gatingSo my honest status is:
non-codex_appsapproval gate is still absent frommainIf it helps, I can next try to reproduce this outside the network-disabled Codex sandbox (for example with a normal local CLI/runtime path) and report back with a yes/no result against
0.118.0+. That would be the cleanest way to settle whether the TUI/runtime changes fully fixed it or whether the skip is just masking it here.I can still reproduce this on Codex desktop build 26.325.31654 (build 1272) with bundled
codex-cli 0.118.0-alpha.2.Since
0.118.0-alpha.2is technically earlier than0.118.0, I’m not sure whether this counts as “0.118.0 or newer” for the fix. Happy to share exact repro steps if useful.@preudomme-sys the fix is not merged yet. If you have time to test it, use this PR: https://github.com/openai/codex/pull/16632