Regression: non-codex_apps MCP tool calls block on app-tool approval prompt

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

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.

View original on GitHub ↗

10 Comments

github-actions[bot] contributor · 3 months ago

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

  • #15753
  • #15169
  • #15437
  • #15770

Powered by Codex Action

rebroad · 3 months ago

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:

  • It changed:
  • let approval_required = annotations.is_some_and(requires_mcp_tool_approval);
  • to let approval_required = requires_mcp_tool_approval(annotations);
  • It also changed requires_mcp_tool_approval to 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 emits RequestUserInput.

That matches the observed timeout signature in these non-interactive tests (RequestUserInput prompt 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....

rebroad · 3 months ago

I reviewed these related reports:

  • #15753
  • #15169
  • #15437
  • #15770

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-core test suites and rmcp test 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.

s1korrrr · 3 months ago

I prepared a fix for this on a public fork branch:

Repro summary:

  • Non-codex_apps MCP tool calls can hit the app-tool approval flow and emit RequestUserInput, which blocks the non-interactive codex-core / rmcp test paths listed in this issue.

Fix approaches considered:

  • A) restore the codex_apps gate at the handle_mcp_tool_call(...) call site.
  • B) add the gate inside maybe_request_mcp_tool_approval(...).

Chosen fix:

  • I kept B because it was the smallest locally provable change with the current test seams. The branch now returns early from maybe_request_mcp_tool_approval(...) unless invocation.server == CODEX_APPS_MCP_SERVER_NAME, and adds a regression test proving dangerous non-codex_apps tools skip the approval flow.

Root cause:

  • The app-tool approval helper was reachable for non-codex_apps MCP invocations, so custom MCP servers could trigger approval prompting that should have remained scoped to the apps server path.

Validation:

  • just fmt
  • git diff --check
  • cargo test -p codex-core --lib non_codex_apps_servers_skip_approval_flow_even_for_dangerous_tools -- --nocapture
  • 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

I also tried to open a PR from this branch, but GitHub rejected PR creation for my account (CreatePullRequest permission denied). If a maintainer wants this as an invited PR, I can open it immediately from the branch above.

preudomme-sys · 3 months ago

Can reproduce this with user-added stdio MCP servers in the desktop app.

Environment:

  • Codex desktop app
  • custom non-codex_apps MCP servers: Serena, mcp-local-rag
  • stdio transport

Observed behavior:

  • every MCP tool invocation can still trigger the app-tool approval prompt
  • this happens even with:
  • approval_policy = "never"
  • per-server require_approval = "never"
  • "Allow for this chat" already selected in a previous approval dialog

Expected behavior:

  • non-codex_apps MCP servers should not enter the app-tool approval flow, or at minimum chat-scoped approval should persist across subsequent tool calls in the same thread

Additional signal:

  • codex_apps / connector-backed tools do not appear to exhibit the same repeated approval behavior
  • this suggests the regression is specific to custom MCP approval routing / persistence rather than global approval policy handling

This matches the issue description: approval flow seems to be incorrectly applied to non-codex_apps MCP invocations.

s1korrrr · 3 months ago

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:

  • their repro is also a non-codex_apps stdio MCP path (Serena, mcp-local-rag)
  • the repeated approval dialog shows the app-tool approval flow is still being reached for custom MCP servers
  • the fix branch gates that flow at the approval helper so only codex_apps invocations can enter it

Prepared fix branch and compare:

That branch was validated against the original codex-core / rmcp regression 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.

etraut-openai contributor · 3 months ago

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.

s1korrrr · 3 months ago

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 -- --nocapture
  • cargo test -p codex-core --test all suite::sqlite_state::mcp_call_marks_thread_memory_mode_polluted_when_configured -- --nocapture
  • cargo test -p codex-core --test all suite::truncation::mcp_tool_call_output_not_truncated_with_custom_limit -- --nocapture

Current result in this Codex sandbox:

  • all of those now immediately print Skipping test because it cannot execute when network is disabled in a Codex sandbox. and exit ok
  • so I can no longer reproduce the old hang from this environment alone, because the issue-listed regression cases are now short-circuited before the MCP path runs here

Source check on the same commit:

So my honest status is:

  • I cannot confirm the bug is still live from this sandbox, because the listed regression tests are skipped now
  • I also cannot say the old branch is obsolete purely from source, because the specific non-codex_apps approval gate is still absent from main

If 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.

preudomme-sys · 3 months ago

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.2 is technically earlier than 0.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.

fcoury-oai contributor · 3 months ago

@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