mcp-server: exec/patch approval elicitations default to Denied for MCP-compliant responses
What issue are you seeing?
codex mcp-server deserializes elicitation responses as flat structs (ExecApprovalResponse { decision } / PatchApprovalResponse { decision }) instead of MCP ElicitResult ({action, content}).
A spec-compliant MCP client responds to exec/patch approval elicitations with:
{"action": "accept", "content": {}}
(content is empty because the requested_schema sent by codex is {"type":"object","properties":{}} — no declared fields.)
Deserialization fails because the response lacks a top-level decision field, so the server falls back to ReviewDecision::Denied. Net effect: every MCP-compliant approval is silently denied, and the user's "accept" never takes effect.
There is a self-documented TODO acknowledging the non-conformance:
https://github.com/openai/codex/blob/main/codex-rs/mcp-server/src/exec_approval.rs
// TODO(mbolin): ExecApprovalResponse does not conform to ElicitResult. See:
// - https://github.com/modelcontextprotocol/modelcontextprotocol/blob/f962dc1780fa5eed7fb7c8a0232f1fc83ef220cd/schema/2025-06-18/schema.json#L617-L636
// - https://modelcontextprotocol.io/specification/draft/client/elicitation#protocol-messages
// It should have "action" and "content" fields.
The same pattern exists in patch_approval.rs.
The bug is also baked into the server's own integration tests, which respond with the flat shape (codex-rs/mcp-server/tests/suite/codex_tool.rs:138-146 and :306-314). So MCP-compliant clients cannot currently drive those approvals successfully, and the test suite does not exercise the spec shape.
What steps can reproduce the bug?
- Start
codex mcp-serverand connect from any MCP client that implements theelicitation/createrequest/response per the MCP 2025-06-18 spec (Claude Code's Elicitation hook is a convenient example). - Open a codex session with
sandbox=read-only,approval-policy=on-request. - Prompt codex to run a command that requires escalation, e.g.
date > /tmp/x.txt. - codex-mcp sends
elicitation/createto the client. - Client responds with spec-compliant
{"action": "accept", "content": {}}(or withcontent.decision = "approved"since codex's Rust enum serializes to snake_case; both should work in principle, neither does today). - Observed: codex reports "rejected by you" and refuses to run the command.
- Expected: codex runs the command.
Verified against main at fe7c959e90d46abb8311e4a0b369e6cb32bf337e (2026-04-16).
What is the expected behavior?
The server should parse MCP ElicitResult semantics correctly:
action: "accept"→ treat asApproved(optionally promote toApprovedForSessionifcontent.decision == "approved_for_session")action: "decline"→ treat asDeniedaction: "cancel"→ treat asAbort(or nearest non-approval state)content.decisionshould be read only when present AND the server has a reason to support richer decisions than a generic MCP client can produce
That is: action is the primary protocol signal, content is a capability extension.
Two orthogonal design notes:
- The current
requested_schemais{"type":"object","properties":{}}, which tells generic clients "no content expected." If codex wants richer decisions (approved_for_session, policy amendments), it should encode that inrequested_schemarather than assuming clients will invent adecisionfield. - The flat deserialization defaulting to
Deniedon any parse failure is a silent-fail path that masks this bug from operators. A warning log on the deserialize failure path would have surfaced it earlier.
Additional information
Commit verified: fe7c959e90d46abb8311e4a0b369e6cb32bf337e (main, 2026-04-16).
Affected files:
codex-rs/mcp-server/src/exec_approval.rs:41-48— flatExecApprovalResponsestructcodex-rs/mcp-server/src/exec_approval.rs:127-135— silent deny on deserialize failurecodex-rs/mcp-server/src/patch_approval.rs:38-40— flatPatchApprovalResponsecodex-rs/mcp-server/src/patch_approval.rs:126-130— silent deny on deserialize failure
Tests currently encode the non-compliant shape:
codex-rs/mcp-server/tests/suite/codex_tool.rs:138-146codex-rs/mcp-server/tests/suite/codex_tool.rs:306-314
So a fix likely wants to update tests to use spec-compliant ElicitResult shape alongside the server-side change.
Related issues (adjacent, not duplicates):
- #11816 —
codex mcp-serverhangs indefinitely when command approval requires elicitation but MCP client cannot answer - #13405 — Elicitation support (codex as MCP client side)
This issue is specifically about the server side responding to elicitation-based approvals from a compliant MCP client.
Context: Found while building an auto-approval hook on the client side. The hook emits spec-compliant ElicitResult responses (verified against MCP 2025-06-18 schema); codex denies every one. Happy to supply MCP wire captures, additional repro detail, or test cases if that helps maintainers.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗