[BUG] form-mode MCP elicitation auto-declines in interactive TUI — server-initiated form is treated as "print mode"
Preflight Checklist
- [x] I have searched existing issues — closest matches are #48164 (URL-mode, different mode) and #51785 (docs about print/SDK mode). This is a distinct bug: form-mode elicitation auto-declines even in interactive TUI, after the v2.1.117 print/SDK fix.
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code (
2.1.144)
What's Wrong?
Claude Code 2.1.144, running interactively (TUI, human at the keyboard), receives a perfectly valid elicitation/create request with mode: "form" from a connected MCP server during a tool call — and auto-declines without ever rendering a dialog. Claude Code's own MCP debug log labels the situation "Elicitation request received in print mode", even though there is no -p/--print/headless flag in play.
The docs at https://code.claude.com/docs/en/mcp#respond-to-mcp-elicitation-requests state:
Claude Code displays an interactive dialog and passes your response back to the server. No configuration is required on your side: elicitation dialogs appear automatically when a server requests them.
In practice no dialog appears, the tool call returns the server-side "elicitation declined" error, and there is no way for the user to grant feedback through native MCP — only the server's chat-fallback path works.
Setup
- Claude Code:
2.1.144 (Claude Code) - Platform: macOS Darwin 24.6.0
- Invocation: interactive TUI session (
claudestarted normally, no--print, no Agent SDK) - MCP server: Orcho MCP v1.27.1 — but the bug reproduces with any MCP server that sends
elicitation/createwithmode: "form"during a tool call. A minimal stdio probe using the officialmcpPython SDK is attached below as a smoke comparison. - Server-side capability advertisement (from server's
initializerequest payload):elicitation.formcapability is registered via the FastMCP Python SDK; the server only attemptsctx.elicit(...)when the client'sinitializecapabilities includeelicitation.form. So the server entered the elicitation branch — meaning Claude Code advertised the capability to the server.
Reproduction
- Connect Claude Code to an MCP server that exposes a tool which, when called without an optional argument, requests it via
elicitation/createwithmode: "form"(the Orchoorcho_phase_handoff_decideretry_feedback flow is a real-world example). - Run that tool from the TUI without supplying the optional argument, so the server-side elicitation path triggers.
- Observe: tool call fails immediately, no dialog appears.
- Inspect
~/Library/Caches/claude-cli-nodejs/<project>/mcp-logs-<server>/*.jsonlfor the most recent session.
Smoke comparison — same server, same args, generic Python MCP client with elicitation handler:
# /tmp/probe.py — generic MCP client that handles the same elicitation/create
async def elicit_handler(ctx, params):
return mcp_types.ElicitResult(
action="accept",
content={"feedback": "test feedback"},
)
async with ClientSession(read, write, elicitation_callback=elicit_handler) as session:
await session.initialize()
decide = await session.call_tool("orcho_phase_handoff_decide", arguments={...})
# Returns successfully with feedback persisted.
That run succeeds end-to-end. The exact same server binary, called from Claude Code TUI, fails with auto-decline.
Observed Log Evidence
From Claude Code's MCP debug log (2026-05-25T22-01-19-486Z.jsonl), exact line on receipt of the server request:
{
"debug": "Elicitation request received in print mode: {\"method\":\"elicitation/create\",\"params\":{\"mode\":\"form\",\"message\":\"Explain what the reviewer should reconsider…\",\"requestedSchema\":{\"type\":\"object\",\"properties\":{\"feedback\":{\"type\":\"string\",\"title\":\"Feedback\",\"description\":\"…\",\"minLength\":1}},\"required\":[\"feedback\"]}}}",
"timestamp": "2026-05-25T22:01:57.807Z",
"sessionId": "f328d477-2d6b-48a8-8c71-5b6df9af68da",
"cwd": "/Users/smartgamma/www/orcho"
}
Immediately followed by the tool-error frame (server-side error, after Claude Code returned action != "accept"):
{
"error": "Error executing tool orcho_phase_handoff_decide: retry_feedback requires feedback. Native MCP elicitation was decline by the client; ask the user for feedback in chat and retry with args.feedback.",
"timestamp": "2026-05-25T22:01:57.837Z"
}
The 30 ms gap between the two lines confirms no interactive prompt was shown — the round-trip happened entirely inside the MCP client.
Expected Behavior
Per the docs, an interactive TUI session should render a form dialog for the user (the JSON Schema is well-formed with a single string feedback field, minLength: 1), collect input, and return it as {action: "accept", content: {feedback: "<user text>"}}.
Actual Behavior
- No dialog shown.
- Claude Code internally labels the request as "print mode" — even though TUI is active and the user is present.
- Client auto-responds with a non-accept action (looks like
declinefrom the server-side error wording). - The tool call fails; server's documented chat-fallback path becomes the only option.
Hypothesis
Mid-tool-call elicitation arriving inside an assistant turn is being routed through the same code path as --print mode, where there is no UI surface to render a dialog. The v2.1.117 fix mentioned in #51785 addressed auto-cancelling during print/SDK connection setup, but the auto-declining-mid-turn case in TUI still seems to fall through to the headless branch.
Impact
High for MCP server authors who want to use the documented mid-tool elicitation feature. Currently every form-mode elicitation issued during a tool call has to be paired with a manual chat-fallback path, doubling implementation work and breaking the "no configuration required on your side" promise in the docs.
Workaround
Server-side: ship a chat-fallback (server returns a clear error with retry instructions, client/agent asks the user via normal chat tools like AskUserQuestion, then retries the same tool call with the argument supplied). Functional but adds boilerplate to every server.
Related Issues
- #48164 — URL-mode elicitation unsupported (different mode, similar shape)
- #51785 — Docs missing print/SDK guidance (different scope: that issue is about documenting print/SDK behavior; this one is that TUI itself behaves as if it were print mode)
- #56243 — Cowork-specific elicitation cancelled (closed/duplicate, different surface)
Showing cached comments. Read the full discussion on GitHub ↗
5 Comments
Found 1 possible duplicate issue:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Not a duplicate of #48164. The bot matched on the
elicitation/createsubstring, but the two reports cover different MCP modes with different failure shapes and different documented contracts:| | #48164 | #62319 (this issue) |
|---|---|---|
| Mode |
url|form|| Client response | Explicit RPC error
-32602: Client does not support URL-mode elicitation requests| Silent non-accept; internal log labels it"Elicitation request received in print mode"|| Documented contract | URL-mode is not promised as supported anywhere in the Claude Code docs | Form-mode is explicitly documented at https://code.claude.com/docs/en/mcp#respond-to-mcp-elicitation-requests as automatic: "Claude Code displays an interactive dialog … No configuration is required on your side" |
| Surface | Any transport / setup | Interactive TUI (no
--print, no Agent SDK) on 2.1.144, after the v2.1.117 print/SDK fix referenced in #51785 |Fixing #48164 (adding URL-mode support) would not fix this — form-mode is already advertised, the request reaches the client cleanly, and the client itself routes it to the headless code path instead of rendering the documented TUI dialog.
I can confirm the same failure shape with a different private local MCP server and a minimal boolean form schema.
Environment:
2.1.170 (Claude Code)26.5.1, Darwin25.5.0, arm64-p/ Agent SDKThe server only enters the native elicitation path after seeing
clientCapabilities?.elicitation?.form; otherwise it returns a normal chat-fallback instruction. The requested schema was a simple form-mode boolean:Claude Code logged the request as print mode in an interactive session:
The tool failed immediately afterward because the elicitation result was not accepted. The gap from elicitation receipt to failure was about 8 ms, so no human interaction could have occurred. A second attempt one minute later had the same Elicitation request received in print mode log and failed within about 15 ms.
The server-side chat fallback then succeeded with the same tool once the confirmation was supplied explicitly in the tool arguments, so the MCP server and tool were otherwise healthy. The broken part was specifically native form-mode elicitation being routed through the print/headless path instead of presenting a usable dialog.
Confirming this on the latest claude-code builds (2.1.185–2.1.195) from the other side of the wire — a production MCP gateway/proxy that gates state-changing tool calls behind a server-initiated form-mode
elicitation/create. Same root signature as @abecker93's repro (interactive session, request logged as "print mode", non-accept with no human in the loop), plus a second observable variant on the newest builds that I don't think is captured yet.Two distinct failure shapes from the same misroute
(a) Fast auto-decline (~8–100 ms) — matches @abecker93 / @Evgenas above.
claudeCLI, default renderer, all permission modes tried.createConfluencePageconfirmation resolved as cancel in ~106 ms — far too fast for a human, no dialog ever shown.--debugmode. Whatever selects the "print mode" path appears to differ when debug is on — likely a useful clue for narrowing the detection logic.(b) Full no-response timeout (~5 min) — the variant I think is new here, on the latest release.
claude-code/2.1.195 (cli), interactive session.createConfluencePageattempts, each ~5–6 min apart, all resolved as a server-side timeout because the client returned no JSON-RPC response at all — not accept, not decline, nothing — for the full elicitation window.So in older builds the misrouted-to-"print mode" request comes back as a fast decline (a); on 2.1.195 the same misroute presents as the client silently dropping the request entirely (b). Both leave the server holding a confirmation that no human ever saw.
Server/proxy side is clean — verified against a reference client
To rule out our side, I replayed the identical form-mode
elicitation/create(single booleanconfirmed,required: ["confirmed"]) through MCP Inspector: it renders the dialog correctly, and accept → the gated write reaches the backend end-to-end. Same request, correct client → works. So the request shape is spec-valid and the divergence is purely in the claude-code TUI elicitation path, consistent with this issue.Capability shape
Per #69555, the client advertises
"elicitation": {}(post ~2.1.108), which per the 2025-11-25 spec still means form-mode supported — and indeed the request reaches the client cleanly rather than erroring. So this isn't a missing-capability problem (that's the separate url-mode track in #48164); it's an advertised-and-accepted form-mode elicitation being routed to the headless/"print mode" handler in an interactive session.Summary for triage
| | shape (a) | shape (b) |
|---|---|---|
| builds seen | ≤ ~2.1.17x | 2.1.195 (latest) |
| surface | interactive TUI | interactive TUI |
| client response | non-accept in ~8–100 ms | none (server times out ~5 min) |
| dialog rendered? | no | no |
| payload | small, untruncated still fails | small, untruncated still fails |
| transport | up | up (concurrent reads succeed) |
Happy to provide more captures. The
--debug-only success in (a) seems like the highest-leverage thread to pull on.Follow-up after inspecting the bundled handler in 2.1.195 — two concrete findings that I think explain both the "auto-decline" shape in this issue and the no-response-timeout variant I reported above, and that point at small, self-contained fixes.
1. The
"received in print mode"log is emitted unconditionally — it's a mislabel, not a mode checkThe server-
elicitation/createrequest handler that logsElicitation request received in print mode: …logs that string at registration time, ungated — there's noisPrintMode/headless guard around it. So seeing it in a fully interactive session (as @abecker93 and @Evgenas did) is not evidence the session is in--print; it just means that session bound the control-protocol / SDK elicitation handler instead of the interactive TUI one. The label has almost certainly been sending triage in the wrong direction — it reads like a print-mode problem when the real signal is "this session bound the wrong handler."2. The control-protocol elicitation forward has no timeout — its sibling does
There appear to be (at least) two server-elicitation handler implementations:
sendRequest({subtype:"elicitation", …})) and, on any error, returns{action:"cancel"}.The control-protocol forward has no client-side timeout. The sibling user-dialog method on the same transport does — it arms a
setTimeoutthat injects acancelledresponse after a bounded interval. The elicitation forward omits that timer entirely.That single asymmetry explains both observed shapes:
catch → {action:"cancel"}returns in milliseconds = the ~8–100 ms "instant auto-decline" in this issue.Suggested fixes (smallest first)
Happy to share more captures (versions, payload sizes, timings). The behavioral repro is in my comment above; these two findings are just what the bundled handler shows about why it presents as it does.