Repository navigation
fix(app): show MCP elicitation forms in desktop and web - #58305
Conversation
MCP servers can ask for input during a tool call. The TUI renders those forms, but desktop and web only showed question and web search forms, so the tool call waited until the server gave up. Add an elicitation dock that renders the form's boolean, number, string, choice, multiselect, and URL fields, and pick up forms both from the session tree and from the Location (elicitations are owned by the "global" sentinel session). Replies and cancellations go through the data layer so global forms carry their Location.
There was a problem hiding this comment.
The change looks sound. I read the diff against the client data layer and the core MCP elicitation handler. The new tests in packages/app/src/session/requests pass (26), and tsc -b in packages/app passes. I couldn't run it live: that needs the web app in a browser plus a backend and an MCP server that sends elicitations, so I'm relying on your demo for the end-to-end path.
Public API: unchanged. The change only touches the app UI, adds one i18n key and updates Storybook mocks.
No blocking concerns. Replies and cancels go through data.session.form.reply/cancel, which already add the Location for global forms. The server checks required fields and value types, and any error it returns shows as a toast. One known limit, already listed as out of scope and the same as in the TUI: while an elicitation is pending, blocked hides the prompt box in every session of that Location, not only the session that triggered it.
There was a problem hiding this comment.
The new commit is a lint cleanup and changes no behaviour, apart from trimming free-text answers before sending, which is harmless because the server still rejects a blank required field. Values now live in one store split by field type, the message is decoded with an Effect schema, and the text-input type comes from a format lookup checked against FormStringField["format"]. On the new head, the tests in src/session/requests pass (26), tsc -b in packages/app passes, and oxlint on the changed directory reports no errors. I still couldn't run the dock live; your demo covers that. The first review raised no blocking points. Public API: unchanged.
Problem
An MCP server can ask the user for input partway through a tool call (
elicitation/create). Since #58184 the TUI shows these forms, but the desktop and web app only render forms of kindquestionandwebsearch.provider. In the app the form never appears, and the tool call waits until the server gives up.App support was originally added in #35064, lost in the #35080 revert, and not reapplied when #35115 restored the core side.
Change
SessionElicitationDock. Shows the form title, the server's message, and one control per field: a switch for booleans, number inputs for number and integer fields, a select for string choices, text inputs for free strings, checkboxes for multiselect, and the URL with an Open button for URL-mode elicitations. Submit replies and Dismiss (or Escape) cancels."global"sentinel session, so the model also lists and syncs global forms for the session's Location. Replies and cancellations go throughdata.session.form.reply/cancel, which add the Location header for global forms.session.elicitation.selectPlaceholder. Everything else reuses existing keys.form.reply/cancel.Demo
mcp-elicitation-web-final.mp4
This is the production web build against a local backend. A stdio MCP server's
deploytool asks forapprove(boolean),environment(staging | production) andnotes(string). The server received:{ "action": "accept", "content": { "approve": true, "environment": "production", "notes": "Ships the MCP elicitation dock." } }Out of scope
"global"session, so an elicitation shows in every session view for that Location.Verification
bun test … ./src/session/requestsinpackages/app: 26 pass, including a new case formcp-elicitationforms insessionFormRequest.bun run typecheckinpackages/appandbun run checkat the root: pass, 34/34 tasks.