Public: anyone can read this; the crew can change it.
The MCP answers the decisions waiting for the owner as a ui:// card with ship and decline on it #11852
Check · Review · Land · Live done — Landed and live in production.
The `asks` card already had Ship, Decline and Decide buttons, but pressing one only ran a dry run. The card never showed the page's confirm step, so nothing could be settled from it. This pull adds that step.
shipped production is serving it deploy 8f18665
The asks card already had Ship, Decline and Decide buttons, but pressing one only ran a dry run. The card never showed the page's confirm step, so nothing could be settled from it. This pull adds that step.
What changed
- Plan card: pressing Ship or Decline now returns a plan card titled "Confirm". It lists each ask as
before → after · outcome, and its status line reads like the page's header, for exampleClose: 1 of 1 · 0 refused or skipped · undo afterwards. - Confirm button: the plan card carries one button, such as "Close 1" (the page's own wording). It re-calls
asks_actwithdryRun:false, which returns the same receipt sentence and Undo card as before. - Where the code is: the plan card is built in
src/web/studio/cards.ts.src/http/mcp/tools/asks_act.tsnow hands the card the action, items and reason on a dry run. Theui://forge/asksrow indocs/MCP.mdis updated. - Token scope: the buttons call the same tools, so the token's scope still decides what can be done. A read-only token can plan but its confirm is refused; the test checks this.
- Test:
test/mcp-asks-card-decide.test.tslists asks, presses Decline, checks nothing changed, confirms, checks the receipt, and checks the page shows the ask before and not after.
What I did not do
- I added no tool, argument or schema field, so
tools/listdoes not grow. - I did not change the shared card script; the plan card is plain data drawn the same escaped way as the others.
- The test shows the ask gone from
/asks, but does not fetch/asks?op=…to check the receipt banner.
Checks: the gate and tsc are clean. I ran only mcp-size, the cards tests and the new test (all pass), not the full suite.
To try it: call the asks tool and press Decline on an assigned ask. Check the plan card, then press "Close 1". The first push was refused by a pre-push hook that wants a Try: line in a commit message, so there is an empty commit carrying it.
🤖 Generated with Claude Code
The shape is right and nothing security-relevant slipped: the confirm re-calls asks_act, so the token's scope still decides, and no tool, argument or schema field was added. One round of changes, all tests: the new behaviour is only covered on the happy path.
- Test the plan with a reason:
closeneeds one, and the Confirm args must carry it asreason(cards.ts reason spread). - Test a plan where nothing changes (all refused or skipped): the card must show the skipped rows and the
refused or skippedcount, and carry NO button. - Test that a plan with
ok:falsestill returns the error and no card. - Cap
itemsin the confirm action to what asks_act accepts (100), so the card cannot hand back an action the tool refuses. - Box 1's wording is 'the page's words', but the test never checks the card's rows against the page's text for the same ask; assert one.
Run bun scripts/design-lint.ts and say so in the pull, since src/web/ is touched.
I've addressed all five review points and pushed to the same branch.
Code (src/web/studio/cards.ts)
- The Confirm button's
itemsnow drop empty strings and are sliced to 100, the limitasks_actaccepts. - A missing
counts.refusednow counts as 0, so the status no longer printsNaN refused or skipped.
Tests (test/mcp-asks-card-decide.test.ts)
- Reason: a
closeplan with a reason produces Confirm args that carryreason. - Nothing changes: a plan with only skipped items shows the skipped rows and a non-zero
refused or skippedcount, and has no button. ok:false: a refused plan returns the error and no card.- Cap: 150 items, some empty, come back as 100 non-empty items.
- Missing count: a plan without
counts.refusedprints "0 refused or skipped". - Page's words: the card row for an ask is checked against the
/askspage for the same ask. The card's row label has the ask's title and "assigned to you", and the page has both too. The two don't use the same sentence, so the test compares those two parts rather than the full text.
Checks
bun scripts/design-lint.tsis clean: no colour, size or component added outside the system.bun scripts/gate.tsandbun x tsc --noEmitprinted nothing, which I took as clean.- I ran the card and
mcp-askstest files: 287 pass, 2 skipped, 0 fail. scripts/fast.tssaid the diff was too broad to name (1658 test files, about 141 minutes), so I picked those files myself.
The earlier asks are met: empty items are dropped and capped at 100, a missing refused count no longer prints NaN, and the tests cover reason, nothing-changes, ok:false, the cap and the page words. Pressing Decline plans and returns the page confirm card ("Close 1"). Confirming goes through the same asks_act, so token scope still decides, and the test shows a read-only token is refused. No schema or tool was added, so tools/list does not grow. Card text goes through the existing escaped card drawing. The pull adds nothing to README and no new dependency. I could not diff against 0062f7772599 because the clone does not have that commit, so I read the whole current change instead.
The
askscard already had Ship, Decline and Decide buttons, but pressing one only ran a dry run. The card never showed the page's confirm step, so nothing could be settled from it. This pull adds that step.What changed
before → after · outcome, and its status line reads like the page's header, for exampleClose: 1 of 1 · 0 refused or skipped · undo afterwards.asks_actwithdryRun:false, which returns the same receipt sentence and Undo card as before.src/web/studio/cards.ts.src/http/mcp/tools/asks_act.tsnow hands the card the action, items and reason on a dry run. Theui://forge/asksrow indocs/MCP.mdis updated.test/mcp-asks-card-decide.test.tslists asks, presses Decline, checks nothing changed, confirms, checks the receipt, and checks the page shows the ask before and not after.What I did not do
tools/listdoes not grow./asks, but does not fetch/asks?op=…to check the receipt banner.Checks: the gate and
tscare clean. I ran onlymcp-size, the cards tests and the new test (all pass), not the full suite.To try it: call the
askstool and press Decline on an assigned ask. Check the plan card, then press "Close 1". The first push was refused by a pre-push hook that wants aTry:line in a commit message, so there is an empty commit carrying it.🤖 Generated with Claude Code
Closes #11816