Note
Grok responding on behalf of Julius.
Triage
Thanks for the precise diagnosis, @diastidean! Confirmed: Claude declines MCP elicitation/create because the adapter never handles it. This is a bug in the existing app-access approval path, not a new workflow.
makeClaudeAdapter in apps/server/src/provider/Layers/ClaudeAdapter.ts passes canUseTool and onUserDialog (with supportedDialogKinds: ["resume_return"]) into createQuery, but it never sets onElicitation. There's no ClaudeAdapterV2; this is the adapter in question. In @anthropic-ai/claude-agent-sdk@0.3.276, onElicitation is the callback for these requests, and when it's missing and no Elicitation hook answers first, the SDK declines. onUserDialog is a different control request and never sees them. The full-access auto-allow inside canUseTool doesn't cover them either, so the decline happens in every runtime mode. The ignored elicitation_complete system subtype isn't part of this path. It's also separate from the ACP elicitation/create handling in merged #11294.
Codex already shows this kind of request on the existing card: mcpServer/elicitation/request becomes request.opened with requestType: "mcp_elicitation_approval", which web and mobile render as mcp-elicitation. A fix should reuse that event rather than going through canUseTool or AskUserQuestion.
Your proposed narrow scope looks right to me, but it still needs a maintainer to confirm before you open a PR. Here's what that PR would cover:
- Handle
onElicitationon the Claude query. - Open the existing app-access card only for an approval-only form, and wait for the user. Use
request.messageas the detail, and for the app name usedisplayName, falling back totitleand thenserverName. - Return
{ action: "accept", content }only after an explicit accept. Fillcontentthe way Codex'stoMcpElicitationResponsedoes for a one-time"accept": approval choices meaning once/accept/approve/allow, plus defaults. If a required field still can't be filled, decline without opening a card. - Return decline and cancel as those actions. Never return
null, because the SDK treats it as "already answered out of band" and the elicitation would otherwise hang until timeout. - Fail closed with
declineand no card for URL-mode elicitations and for forms asking for a value this card can't collect. Don't open the URL. - One-time accept only: no
acceptForSession, noacceptAlways, and no Claude permission rules written. Web and mobile both offer a session grant whenoptionsis omitted, so the opened request needs explicit Cancel, Decline, and Approve options. - Park the wait on the session's existing pending-approval map so
respondToRequestresolves it and stopping the session cancels it. The SDK callback'srequestIdis not T3's approval id.
Focused adapter tests should cover these cases:
- An approval-only form waits and only accepts after
respondToRequest. - Decline and cancel map to those actions.
- URL forms and value forms decline without showing a card.
- Aborting before the listener attaches, and stopping while the card is open, both settle as cancel.
- A late response after the request is gone hits the existing unknown-request error and doesn't settle twice.
URL auth, a form renderer, and session or always grants would stay out of scope.