Skip to content

Claude MCP elicitation requests are declined without an approval prompt #14878

Description

@diastidean

Reproduction

In a T3 Code Claude thread, invoke an MCP tool that requests app-access approval through elicitation/create (for example, codex-cua requesting access to Dia).

Expected: T3 shows the app-access request and waits for the user to approve or decline.

Actual: The tool immediately receives a decline; no approval card appears.

Diagnosis

ClaudeAdapterV2 registers onUserDialog but not the Claude Agent SDK's separate onElicitation callback. Without that callback, the SDK returns decline. This is distinct from the existing Codex app-access path.

Proposed narrow scope

Route approval-only MCP form elicitations through T3's existing mcp-elicitation approval UI. Return a one-time accept only after explicit approval; preserve decline/cancel and fail closed for forms requesting values or URL-mode elicitations. Cover cancellation and late responses with focused tests.

Could maintainers confirm whether this direction and scope are approved for a focused PR?

Activity

  1. juliusmarminge commented on Oct 2, 2026

    @juliusmarminge
    Member

    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 onElicitation on the Claude query.
    • Open the existing app-access card only for an approval-only form, and wait for the user. Use request.message as the detail, and for the app name use displayName, falling back to title and then serverName.
    • Return { action: "accept", content } only after an explicit accept. Fill content the way Codex's toMcpElicitationResponse does 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 decline and 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, no acceptAlways, and no Claude permission rules written. Web and mobile both offer a session grant when options is omitted, so the opened request needs explicit Cancel, Decline, and Approve options.
    • Park the wait on the session's existing pending-approval map so respondToRequest resolves it and stopping the session cancels it. The SDK callback's requestId is 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.

  2. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Oct 2, 2026
  3. diastidean commented on Oct 5, 2026

    @diastidean
    Author

    @juliusmarminge Thanks for the triage on this. I’ve rebased #14895 and added focused test and manual verification evidence. Could you or another maintainer confirm whether the narrow, one-time Claude MCP approval scope is approved under the contribution guide? Happy to adjust or close the PR if this isn’t the desired direction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions