MCP elicitation: auto-approve sends content={} even when requested schema requires fields (follow-on to #9053 / PR #9196)

Open 💬 1 comment Opened May 19, 2026 by michaelfeiertag

What variant of Codex are you using?

Codex Desktop macOS (CLI 0.131.0-alpha.9, bundle 26.513.31313)

What issue are you seeing?

When a tool is configured with approval_mode = "approve" (e.g. after the user
picks "always allow" in the desktop UI), Codex auto-accepts any
elicitation/create request that tool issues. It sends:

{ "action": "accept", "content": {} }

…regardless of whether the requested schema has required fields. For schemas
that do require fields, MCP servers reject the response and the tool call
fails.

This is a follow-on to #9053. PR #9196 fixed content: None by hardcoding
Some(json!({})), which works for empty-properties approval prompts but not
for schemas with required fields. The same json!({}) literal was then copied
into codex-rs/codex-mcp/src/elicitation.rs's make_sender auto-approve
branch, propagating the limitation.

Steps to reproduce

  1. Run any MCP server that exposes a tool which calls ctx.elicit with a

schema that declares required properties — e.g. FastMCP's dict-of-options
syntax, which generates:

``json
{
"type": "object",
"properties": {
"value": {
"type": "string",
"oneOf": [
{"const": "a", "title": "A"},
{"const": "b", "title": "B"}
]
}
},
"required": ["value"]
}
``

  1. In ~/.codex/config.toml, set the tool to auto-approve:

``toml
[mcp_servers.<server>.tools.<tool>]
approval_mode = "approve"
``

(Equivalent to picking "always allow" in the Desktop UI.)

  1. Trigger the tool. Codex's auto-approve path returns

{action: "accept", content: {}}.

  1. The MCP server (FastMCP 3.3.x in our case) rejects it with:

> Elicitation response missing required 'value' field.

…because content.value is not populated.

Captured payload (verified)

Server-side log of the actual elicitation round-trip:

[elicit/req] is_raw=True schema={"type":"object","properties":{"value":{"oneOf":[{"const":"use_rewrite","title":"Proceed with compliant version"},{"const":"modify_rewrite","title":"Modify the rewrite"},{"const":"cancel","title":"Cancel"}],"type":"string","title":"Compliance choice","description":"Choose how Codex should proceed."}},"required":["value"]}
[elicit/resp] action=accept content={}

Expected behavior

For a schema with required fields, auto-approve should not silently fabricate
an accept response. Options I see (any of which would be spec-conformant):

  • Return action: "decline" (no content needed).
  • Skip auto-approval entirely when requested_schema.required is non-empty

and prompt the user instead.

  • Surface a structured error so the server can fall back to a non-elicit code

path.

Pointer to source

codex-rs/codex-mcp/src/elicitation.rsmake_sender's auto-approve branch:

if mcp_permission_prompt_is_auto_approved(...) && can_auto_accept_elicitation(&elicitation) {
    return Ok(ElicitationResponse {
        action: ElicitationAction::Accept,
        content: Some(serde_json::json!({})),
        meta: None,
    });
}

can_auto_accept_elicitation already gates on
requested_schema.properties.is_empty() on main, but the observed behavior
in 0.131.0-alpha.9 fires this branch for non-empty schemas — either the check
is bypassed in the alpha build, or another reviewer/auto-approve path in the
desktop bundle reaches the same json!({}) literal.

Why this matters

MCP elicitation is the standard way for a tool to surface a multi-choice
prompt to the user. When users do the normal thing ("always allow this tool"),
the gate silently breaks.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗