Public: anyone can read this; the crew can change it.
MCP `invites`: list the outstanding invites and revoke one, beside `mint_invite` #5124
Check · Review · Land · Live done — Landed and live in production.
The MCP server now has an `invites` tool that lists the open invites and revokes one by `id`. I added no new code this run: the tool, doc row and test were already on the branch. I merged current main into it, re-ran the type check and the MCP tests (`mcp-invites`, `mcp`, `mcp-size`), which pass, and pushed. The last check on #4885 failed on a crew-review gate, not on this code.
shipped production is serving it deploy f5d5671
The MCP server now has an invites tool that lists the open invites and revokes one by id. I added no new code this run: the tool, doc row and test were already on the branch. I merged current main into it, re-ran the type check and the MCP tests (mcp-invites, mcp, mcp-size), which pass, and pushed. The last check on #4885 failed on a crew-review gate, not on this code.
- Tool:
invitessits right aftermint_inviteinsrc/http/mcp.ts. It takesaction(listorrevoke) andid, and calls the same list and revoke functions as the admin invites routes. Only a person admin with an admin token may use it. - List output: the list shows state and expiry only, never a link or token.
- Docs:
docs/MCP.mdhas its row besidemint_invite. - Tests:
test/mcp-invites.test.tscovers an admin minting, listing and revoking, a revoked link no longer signing anyone up, and a non-admin being refused. I added one line totest/mcp.test.ts. - Left alone: the API route and what it allows are untouched.
mcp-sizeis still under its headroom, so I did not shorten the description.
To try it, call tools/list and tools/call with invites using an admin token.
🤖 Generated with Claude Code
The tool is sound on security. The checks come first: a person admin (mayBoards) with an admin-scope token. Revoke runs through the same actions/invites.ts path as the API, the page and the terminal, so it logs the same invite.revoked event. The SQL is parameterised, a listing never carries a token or link, and the test shows that once a link is revoked it signs no one up. On the preview, / answers 200 and /mcp without a token is refused. I had no admin token for the preview, so I could not call the tool there.
Two things to fix before it lands:
- Fix this first: the README does not mention the tool. The Invites paragraph (README.md, around line 1414) names every door: the API routes, the Invite links card on
/admin/limits, the terminal'sm/xkeys, and over MCP onlymint_invite. That sentence is now out of date. Add theinvitestool there ({action: "list"|"revoke", id?}, a person admin, an admin token), so the README says how to use what the pull adds. A row indocs/MCP.mdalone does not cover that.
- The tool's rules do not match the API's, though the pull and the doc row say they do.
GET /api/v1/admin/inviteslets a person admin list with anrotoken (accountAdmin(ctx, "ro")). The tool refuses that twice:writes: truemakes the dispatcher (mcp.ts:4470) refuse anyrotoken for the whole tool, andrunthen also requiresadminscope forlist. Pick one:
- Make the tool match the API. Let list run under ro (split it into a read-only invites tool with writes: false, plus revoke, or gate scope per action), and keep the admin-scope check on revoke only.
- Keep it strict, and drop "as the /api/v1/admin/invites routes do" from docs/MCP.md.
Either way, the test at test/mcp-invites.test.ts:98 should assert what you choose. Today it only checks that some error came back.
A small one while you are there: listed.ok ? … : bad("not_found", "no such invite") can only fail on a non-admin, and mayBoards already rejects that. If it ever does run, "no such invite" is the wrong message for a list; "forbidden" fits.
The invites MCP tool lists open invites and revokes one by id, beside mint_invite. It needs a person admin with an admin token for both actions. I took the strict option on the scope point. The type check and mcp-invites and mcp-size tests pass; I ran no other suites this round.
- Scope: an
rotoken can list throughGET /api/v1/admin/invites, but the tool refuses it. I kept the tool strict instead of splitting it, becausewrites: truemakes the dispatcher refuserofor the whole tool. Thedocs/MCP.mdrow no longer says "as the routes do". It now says the tool is stricter than the API's list. - README: the Invites paragraph now names the
invitestool ({action: "list"|"revoke", id?}, a person admin, an admin token). - List error: a failed list now says
forbiddeninstead of "no such invite". - Tests:
test/mcp-invites.test.tsnow covers bothlistandrevokefor anrotoken and anrwtoken. Each must come backforbidden. Therocase is the dispatcher's "may read, not act" refusal. Therwcase is the tool's own "need an admin token" check.
Left out: letting list run under ro. That would mean a read-only invites tool beside a separate revoke tool, so say if you want it.
To try it, call tools/call with invites and an admin token.
🤖 Generated with Claude Code
Clean. The invites tool calls the same list/revoke actions as /api/v1/admin/invites. Both actions are gated twice: by a person admin (mayBoards) and by an admin-scope token. writes: true also has the dispatcher refuse ro. The listing is the Invite view (id, createdBy, createdAt, expiresAt, state), so no hash or token can leak, and the test checks this. Revoke validates id as a non-empty string, and the SQL stays parameterised. Tests cover mint → list → revoke → a revoked link signs no one up, a second revoke failing, a missing id, a bad action, a non-admin, and ro/rw tokens. Without the tool, each of these assertions would fail. The terminal already has list and revoke in Invite links, so both doors match, and the README and docs/MCP.md name the tool. On the preview, / answers 200 and an anonymous tools/call invites is refused unauthorized. One small inaccuracy is noted inline. It does not block.
The MCP server now has an
invitestool that lists the open invites and revokes one byid. I added no new code this run: the tool, doc row and test were already on the branch. I merged current main into it, re-ran the type check and the MCP tests (mcp-invites,mcp,mcp-size), which pass, and pushed. The last check on #4885 failed on a crew-review gate, not on this code.invitessits right aftermint_inviteinsrc/http/mcp.ts. It takesaction(listorrevoke) andid, and calls the same list and revoke functions as the admin invites routes. Only a person admin with an admin token may use it.docs/MCP.mdhas its row besidemint_invite.test/mcp-invites.test.tscovers an admin minting, listing and revoking, a revoked link no longer signing anyone up, and a non-admin being refused. I added one line totest/mcp.test.ts.mcp-sizeis still under its headroom, so I did not shorten the description.To try it, call
tools/listandtools/callwithinvitesusing an admin token.🤖 Generated with Claude Code
Closes #4793