fix(server): Claude MCP forms that ask for values open a question card instead of being declined #35998
main fromrefs/pull/35998/head·opened View on GitHubProblem#
On Claude threads, an MCP elicitation form with a required value field (string, number, enum, multi-select) is declined in a few milliseconds without any UI, and the MCP server is told the user refused. Forms that gate an action on a typed confirmation code cannot be used at all. ACP providers already show the same forms as a question card.
Change#
When the approval card cannot answer a form (claudeMcpElicitationAcceptance returns undefined), the Claude adapter now parses it with the ACP adapter's parseElicitationForm and opens the existing user_input question card. Headers are prefixed with the MCP server's name so the card is not mistaken for Claude's own question. Answers go back typed as accept; dismissal, abort or a missing required answer return cancel, as on ACP.
Unchanged: empty confirms, allow-style approvals (session grants) and forms with only optional fields stay on the approval card with Decline. URL mode still declines.
Supporting changes: parseElicitationForm is exported from @t3tools/provider-acp; it now falls back to the question header when the message is blank, since question must be non-empty. The AskUserQuestion wait is moved into a shared helper used by both paths.
Scope#
Fixes #35996. The approval-card scope from #14878 / #21903 is kept as is; this only covers forms that are declined today.
Verification#
Reproduced with a test MCP server that sends one form per elicitation type against desktop 0.1.9 and
main(0a333a69): every form with a required value was declined in 2–9 ms with no card.With this change, built from source and driven through real Claude turns: string, string formats, integer, number, enum (plain, titled, legacy), multi-select and combined forms opened a question card, and the server received typed content, e.g.
{"code":713321}for a required integer,{"toppings":["olives","cheese"]}for a bounded multi-select. Empty confirm, optional-only forms and the allow/deny form still opened the approval card. URL mode still declined.New adapter tests (value form accepted with typed content and server-prefixed header; missing required answer cancels; abort withdraws the card and rejects a late answer) fail on
mainand pass here. A routing test pins allow/deny forms to the approval card.vp run check:changed: format, lint, knip, typecheck (41 packages) and tests pass.Screenshots (web, same MCP tool and prompt; a required integer field with "Type NNNNNN to confirm." in its description):
Before ( main)After: card After: result Before: no card, the tool gets
decline. After: the question card names the MCP server, and the typed code reaches the server as{"code":558814}.Not checked: mobile rendering of the question card for these forms, Codex (
CodexMcpElicitation.tsstill declines value forms).
Implemented with Claude Opus 5.5 in Claude Code via T3 Code; reviewed by Claude Opus 5.5 and Claude Fable 5.2 subagents.
- 1
github-actions[bot](GitHub profile, opens in a new tab) added labelsvouch:unvouched
naxvog(GitHub profile, opens in a new tab) linked this to fix #35996
tanstack-source added labelsbug
github-actions[bot](GitHub profile, opens in a new tab) added labelssize:L
Approvability#
Verdict: Approved at
172d9a2Macroscope's review found this PR approvable — Supported Claude MCP forms now open question cards and return the user's submitted answers rather than being declined. Existing cancellation handling and schema-based answer conversion limit the change to interactive requests.
You can add or adjust custom eligibility rules. [Learn more](https://docs.macroscope.com/approvability#example-custom-eligibility-rules).No actionable comments were generated in the recent review.
Walkthrough
-
Claude MCP form elicitations that parse into questions now use user-input requests. Submitted answers return as form content; aborted requests or answers that produce no content return a cancel response. The shared request lifecycle also handles
AskUserQuestion, and blank form messages now fall back to question headers.Changes: MCP form elicitation
Layer / File(s) Summary Form question text and export
packages/provider-acp/src/server/elicitationForm.ts,packages/provider-acp/src/server/elicitationForm.test.ts,packages/provider-acp/package.jsonQuestion text falls back from the trimmed description to the trimmed message, then to the question header. The package exports the parser, and a test covers blank messages. Shared user-input request lifecycle
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.tsA helper publishes a user-input request, waits for answers or cancellation, removes pending state, and cancels withdrawn requests. AskUserQuestionuses this helper.Claude MCP form routing
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts,apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.tsEligible form elicitations use user-input questions and return parsed content after answers. Tests cover value answers, approval choices, missing required answers, and unaskable forms. Estimated code review effort: 3 (Moderate) | ~20 minutes
Change: Bug fix · Severity of issue fixed: Medium
Sequence Diagram(s)#
sequenceDiagram participant MCPServer participant ClaudeAdapterV2 participant parseElicitationForm participant awaitUserInputRequest participant User MCPServer->>ClaudeAdapterV2: Send form elicitation ClaudeAdapterV2->>parseElicitationForm: Parse requested schema parseElicitationForm-->>ClaudeAdapterV2: Return questions ClaudeAdapterV2->>awaitUserInputRequest: Publish questions and wait awaitUserInputRequest->>User: Show user-input request User-->>awaitUserInputRequest: Submit answers awaitUserInputRequest-->>ClaudeAdapterV2: Return answers ClaudeAdapterV2-->>MCPServer: Return parsed form contentPossibly related PRs
- pingdotgg/t3code#21903: Adds Claude MCP elicitation handling on the approval card; this change routes supported value forms to user-input questions.
- pingdotgg/t3code#33575: Changes responses for elicitation forms that cannot be shown; this change prompts for supported values and returns cancel when answers are unavailable.
Suggested labels:
bugfixSuggested reviewers:
juliusmarminge
Merge Risk:
Minimal · up to766fc-
Claude MCP forms that need typed values now open a question card, and the entered answers are returned to the server. Dismissal or a missing answer cancels the request. No remaining concrete merge-blocking risk was found. Mobile rendering and live Claude-provider behavior were not verified.
Pre-merge checks:
4-
Check name Status Explanation Title check The title names the Claude MCP elicitation change: forms that need values now open a question card instead of being declined. The changeset matches this: the Claude adapter parses required-value forms… Description check The description follows the repository template. It has Problem, Change, Scope, and Verification sections. The Problem section gives the expected behavior and reproduction environment (desktop 0.1.9 a… Linked Issues check [ #35996] Requirement: Claude MCP form elicitations with required value fields must open a question card and return the typed value, not decline without a prompt. The change summary for ClaudeAdapterV2…Out of Scope Changes check Each changed area connects to #35996. ClaudeAdapterV2.ts adds the form routing and moves the AskUserQuestion wait into the shared helper, which the new path also uses. `packages/provider-acp/package.j…Full details: Title check
-
Explanation
The title names the Claude MCP elicitation change: forms that need values now open a question card instead of being declined. The changeset matches this: the Claude adapter parses required-value forms with parseElicitationForm and opens the user_input card, and the tests cover typed acceptance, missing-answer cancellation, and abort. The title follows the conventional commit format used in the repository template. It is slightly long at 96 characters, but it is specific and accurate.
Full details: Description check
-
Explanation
The description follows the repository template. It has Problem, Change, Scope, and Verification sections. The Problem section gives the expected behavior and reproduction environment (desktop 0.1.9 and main 0a333a69). The Change section explains the fix and the supporting export and fallback changes. The Scope section links issue
#35996and states the approval-card boundary. The Verification section lists focused adapter tests, the vp run check:changed results, before/after screenshots, and items not checked (mobile rendering, Codex). It also names the model and harness used, as the template asks. The template's intro comment is unfilled, which is expected for a template comment and does not count against the description.
Full details: Linked Issues check
-
Explanation
[
#35996] Requirement: Claude MCP form elicitations with required value fields must open a question card and return the typed value, not decline without a prompt. The change summary for ClaudeAdapterV2.ts states that whenclaudeMcpElicitationAcceptancecannot answer and the mode is not URL, the adapter callsparseElicitationForm, builds auser_inputrequest with the server label in each question header, and returnsacceptwith parsed content. It returnscancelwhen the request is aborted or no content can be produced. The sharedawaitUserInputRequesthelper handles waiting and cleanup. Test summaries cover typed string acceptance, allow/deny routing to the approval card, and missing-answer cancellation. Forms with an existing acceptance path keep it. URL mode still declines, which matches the issue scope (#33211). The incremental diff since the previous reviewed heads did not return file content. The assessment therefore relies on the change summary and the prior whole-PR review, which both support the routing. No unmet coding requirement was found.
Full details: Out of Scope Changes check
-
Explanation
Each changed area connects to
#35996. ClaudeAdapterV2.ts adds the form routing and moves the AskUserQuestion wait into the shared helper, which the new path also uses.packages/provider-acp/package.jsonadds the./server/elicitationFormexport so the Claude adapter can importparseElicitationForm.elicitationForm.tsnow falls back to the next question header when the message is blank, becausequestionmust be non-empty. This also changes ACP parsing for blank messages only, and a test covers it. The test changes in ClaudeAdapterV2.test.ts and elicitationForm.test.ts support the objective. The incremental diff did not show file content, so this result relies on the change summary and the whole-PR review. No unrelated change was found.
-
Finishing Touches
-
Generate unit tests (beta)
-
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info-
Run configuration-
- Source mode: Auto
- Repository: pingdotgg/t3code
- Branch: main
- File: .coderabbit.config.ts
- Commit: 43ce395
- Layers: Repository TS config
- Review profile: CHILL
- Plan: Advanced
- Run ID:
2897c2ff-072c-4923-b4a4-fb68e618b84b
Commits-
Reviewing files that changed from the base of the PR and between 14b8aa7b72d8083b48ddbc24e4d0ef09c258e00a and 766fc584e3d1ef7a1d5df55d8abad02d8bd1f233.
Files selected for processing: 2-
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.tsapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.
-
-
Additional comments: 1-
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts-11757-11757: 1
-
11757-11757: Minor · Data Integrity & Integration · Quick win **Unverified finding** Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.Attach the form request to an existing parent node.
buildApprovalRequestArtifactsderivesparentNodeIdfrom this syntheticnativeItemIdat Lines 8311-8316. This branch never creates that node, so the emitted request has an absent parent in the execution graph.Pass
parentNodeId: context.input.rootNodeId, as the control-request path does at Lines 11572-11574, or attach the request to the actual eliciting tool node.
-
Review info-
Run configuration-
- Source mode: Auto
- Repository: pingdotgg/t3code
- Branch: main
- File: .coderabbit.config.ts
- Commit: 43ce395
- Layers: Repository TS config
- Review profile: CHILL
- Plan: Advanced
- Run ID:
c6a4579f-6bf5-4553-a1f9-69a8c67095d7
Commits-
Reviewing files that changed from the base of the PR and between 43ce39595bfcea4af4b99b1fa77c7e1a73256da2 and 172d9a2d41f3b12fdab514b25bd9136e4668fc69.
Files selected for processing: 5-
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.tsapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.tspackages/provider-acp/package.jsonpackages/provider-acp/src/server/elicitationForm.test.tspackages/provider-acp/src/server/elicitationForm.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
-
-
The automated pipeline stopped on this pull request and will not merge it:
the fix is written and reviewed, but proving it needs something no worker has:
- Permanent blocker: the reproduction needs a working Claude provider session; first-party Claude access is listed as unavailable on this VM. Replay speaks generic ACP and cannot exercise Claude's MCP elicitation callback. Immediate decline on base, the question card and typed accept response on head, and dismissal/stop cancellation remain unverified.
It stays open for a person to finish or close.
Sign in to join this conversation.
macroscopeapp[bot](GitHub profile, opens in a new tab) approved these changes