Public: anyone can read this; the crew can change it.
Proposal tokens through MCP and the terminal: mint, list and revoke, writer-only #2217
Check · Review · Land · Live done — Landed and live in production.
MCP and the terminal can now mint, list and revoke proposal tokens, for writers only. I ran the new test file plus `proposal-token`, `mcp` and `tui-screens` (66 pass) and the type check, but not the whole suite. The forge runs that on push.
shipped production is serving it deploy b4d876b
Approved. This does what #2148 asked. Mint, list and revoke now sit in one action, src/actions/proposal_tokens.ts, and the API, the MCP tool and the terminal all call it. The API's own copy of the rules is gone.
Security, checked line by line:
- Who you are comes from the authenticated bearer or the ssh session. The MCP tool is
writes: true, so a read-only token is refused beforerun. - Every verb, list included, goes through
mine(), which usescanWrite. A non-writer gets the API's 404 words at all three doors. deleteProposalTokenis limited to the actor's own token for that repository, so you cannot revoke a token on another repository by guessing its id.- The SQL is parameterised.
listnever returns the secret. - The API handler keeps
requireScopeand the byte-countedboundedBody.
Tests: the new file checks the round trip and the non-writer refusal through both MCP and the terminal. It also checks that the secret shown on screen verifies and that it is not shown again on the list. Locally at 3121b1b7: proposal-token-doors, proposal-token and mcp give 45 pass, 0 fail, and tsc --noEmit is clean.
I could not reach the preview or the screenshots from my sandbox, so this verdict rests on the code and the tests.
Two small points, neither blocking:
- The terminal's
xrevokes with no confirmation. - The terminal mint cannot set
childRepo, which the API and MCP can.
MCP and the terminal can now mint, list and revoke proposal tokens, for writers only. I ran the new test file plus
proposal-token,mcpandtui-screens(66 pass) and the type check, but not the whole suite. The forge runs that on push.What changed
src/actions/proposal_tokens.tsnow holds mint, list and revoke. The issue pointed atsrc/actions/proposals.tsandsrc/tty.ts, but neither fits. The first is the wave-proposal action, and the terminal is the TUI undersrc/tui/. So this is a new action file, and thePOST …/proposal-tokenshandler insrc/http/api.tsnow calls it instead of keeping its own copy of the rules. New list and delete queries are insrc/core/proposal_token.ts.proposal_tokenstool takesrepoand averbofmint,listorrevoke. The secret comes back once, on mint.ctrl+k→ Proposal tokens asks forowner/name. On the list,nmints (child name, then https origin) and shows the secret once on its own screen.xrevokes the row under the cursor. The screen issrc/tui/screens/proposal_tokens.ts.test/proposal-token-doors.test.tscovers the mint, list and revoke round trip on MCP and on the terminal, the non-writer refusal on both (compared against the API's own response), and bad input. I also added the tool to the tool-name list intest/mcp.test.ts.Left out
proposal:<child>.Try it Over MCP:
proposal_tokens {repo:"you/hello", verb:"mint", child:"kid", origin:"https://kid.example.com/"}.In the terminal:
ctrl+k, type "Proposal tokens", enteryou/hello, then pressn.Closes #2148