You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
MCP: print/headless sessions advertise elicitation and auto-accept yes/no forms without a human #4185
In print mode (senpi -p, --mode json) and in other sessions with no human UI, senpi still advertises the MCP elicitation client capability and answers server forms on the user's behalf. A form with only yes/no fields is accepted with false for every field, without anyone seeing it.
Reproduction
Configure any MCP server whose tool sends an elicitation/create form request with one boolean property (for example "Delete the branch?").
Run senpi -p "call that tool".
The server receives { "action": "accept", "content": { "<field>": false } }.
A session where no human can answer does not declare the elicitation capability, so a compliant server does not ask.
If a request arrives anyway, senpi answers decline without filling any field.
Only a human answer may produce accept.
Actual
elicitation.ts:20 declares { elicitation: {} } for every connection; modern/dispatch.ts:20,47,54 does the same on the 2026-07-28 path.
Print mode binds the extension runner's no-op UI (core/extensions/runner.ts:470-473,861-863), whose confirm resolves false and whose input/select resolve undefined.
mcp/index.ts:145-147 hands that no-op UI to MCP at before_agent_start, and elicitation.ts:99-126 treats false as the user's boolean answer, so a boolean-only (or all-optional) form returns accept.
Root cause
MCP decides "a UI exists" from the presence of a UI object, not from whether a human can answer (ctx.hasUI). The no-op UI used by print mode looks like a real UI to it.
Scope and acceptance
No elicitation capability is advertised by a print/json-mode process (legacy initialize and 2026-07-28 per-request _meta).
When the bound context has no human UI (hasUI === false) an elicitation request is answered decline and no UI primitive is called.
TUI and RPC sessions with a UI behave as today.
RED test first: a boolean-only form in a UI-less session returns accept on current main.
Pool routing across shared connections, MRTR handling and URL mode are separate follow-ups (tracked in the MCP spec-sync report).
Ruling correction (2026-10-24): an earlier note in this work said a session that cannot ask a human should answer "cancel/decline". That was wrong for decline. In MCP, decline means the user explicitly refused, so we never send it on anyone's behalf. The behavior for this issue is:
No human can answer, known when the request arrives (print/json mode, a session with no UI): the elicitation request gets a body-free processing error, and no elicitation capability is advertised in those modes in the first place.
A dialog was shown and ended without an explicit choice (dismissed, or cancelled automatically by an unattended client): cancel with no content.
Telling an unattended client's cancellation apart from a person's dismissal needs a cancellation-origin contract; that is tracked with the trusted-presenter work in #4197.
Fixed by #4196, merged as 400fce9 into main (ships with the senpi release after 24-3).
Evidence:
RED on the pre-fix code: a yes/no-only form in a print-mode session was accepted with false and no human (3 failing); after the later rework, 14 tests expecting the no-human processing error failed on the earlier head.
GREEN: 56 focused tests at the reworked head; 53 focused MCP tests at the final merge head; CI green (21 checks); exact-head static receipt green (install, check, typecheck, changes, build); review CLEAR.
Behavior: print/json processes advertise no elicitation capability; when no human can answer at request time the request gets a body-free processing error; a shown dialog that ends without a choice (human dismissal or an automated cancel) answers cancel; no path sends decline on anyone's behalf; explicit Yes/No answers keep their meaning (yes/no fields now show as a two-option pick).
Follow-ups: an explicit Decline control and re-asking on an empty required field (#4188/#4189), the cancellation-origin contract (#4197), and two minor review notes.
Activity
code-yeongyu commentedon Oct 24, 2026
Ruling correction (2026-10-24): an earlier note in this work said a session that cannot ask a human should answer "cancel/decline". That was wrong for decline. In MCP,
declinemeans the user explicitly refused, so we never send it on anyone's behalf. The behavior for this issue is:cancelwith no content.declineonly comes from an explicit Decline control (tracked in MCP elicitation: dismissing a form is reported as decline or as accepted false data instead of cancel #4188 / MCP elicitation: the form does not say which MCP server is asking and cannot be reviewed before sending #4189).Telling an unattended client's cancellation apart from a person's dismissal needs a cancellation-origin contract; that is tracked with the trusted-presenter work in #4197.
code-yeongyu commentedon Oct 24, 2026
Fixed by #4196, merged as 400fce9 into main (ships with the senpi release after 24-3).
Evidence:
falseand no human (3 failing); after the later rework, 14 tests expecting the no-human processing error failed on the earlier head.cancel; no path sendsdeclineon anyone's behalf; explicit Yes/No answers keep their meaning (yes/no fields now show as a two-option pick).Follow-ups: an explicit Decline control and re-asking on an empty required field (#4188/#4189), the cancellation-origin contract (#4197), and two minor review notes.