Skip to content

guard: a server that asks for more input has not acted, so the hold goes back - #20

Open
phyxan wants to merge 1 commit into
mainfrom
fix/mrtr-input-required
Open

phyxan wants to merge 1 commit into
mainfrom
fix/mrtr-input-required

Conversation

@phyxan

@phyxan phyxan commented Oct 8, 2026

Copy link
Copy Markdown
Member

What was wrong

guard.mjs judged an upstream reply as "the call ran" unless it was a JSON-RPC error or isError: true. MCP 2026-07-28 adds a third answer to tools/call: resultType: "input_required", meaning the server wants more input before acting and the client should retry the same call under a new id. The guard committed the reserved amount on that answer and marked any approval as used, then did both again when the retry ran. Claude Code's v2 runtime already speaks 2026-07-28.

What changes

  • An input_required reply releases the hold, with the reason execution.input-required on the receipt, and leaves the approval unspent. The retry is a call of its own and is decided afresh.
  • Refusals carry resultType: "complete" when the request named a protocol revision in _meta. An older client, which has never seen the field, gets the shape it knows.
  • The stub server can now ask first (arguments.ask: true until inputResponses arrives).

Proof

Each new test was run against the unfixed guard and failed, then passed with the fix:

  • guard-proxy: one session with the ask and the retry leaves the ledger at one released and one committed; a fresh session then shows 1,900 of 5,000 spent, not 3,800. Receipts: one blocked with execution.input-required, one executed.
  • approvals: one session per step, so the test cannot pass by racing. The approval is still approved after the ask, used after the retry, and the same call again is refused as already used.
  • guard-proxy: a refusal to a 2026-07-28 client has resultType: "complete"; to an older client it has no such field.

138 tests, the execution-boundary harness and the end-to-end scenario are green from this branch.

Not in this PR

Carrying a receipt id in _meta, and the in-protocol approval flow over input_required (plan items A4 and A5). server/discover already passes through untouched.

🤖 Generated with Claude Code

…oes back

Under MCP 2026-07-28 a server may answer tools/call with resultType
"input_required" instead of acting; the client retries the same call under a
new id. The guard took that answer for a success: it committed the reserved
amount and marked any approval as used, then did both again on the retry.

Now an input_required reply releases the hold with the reason
execution.input-required, leaves the approval unspent, and the retry is
decided on its own. Refusals carry resultType "complete" when the request
named a protocol revision in _meta, so a 2026-07-28 client gets the shape it
expects and an older client sees no change.

Three tests, each shown to fail without the fix: the ledger ends with one
released and one committed reservation and the daily headroom proves it; an
approval survives the ask and is spent by the retry; a refusal carries
resultType only for a client that announced the revision.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant