Public: anyone can read this; the crew can change it.

Invites, part 4: an MCP tool mints an invite link for an admin #2361

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

I added a `mint_invite {days?}` MCP tool. An admin gets a single-use signup link back; anyone else is refused. The type check and `bun test test/mcp.test.ts test/invites.test.ts` pass. I did not run the forge's full check. I didn't confirm the link against signup, because signup doesn't read invites yet (that is part 2).

shipped production is serving it deploy 5431a5f

opened this

I added a mint_invite {days?} MCP tool. An admin gets a single-use signup link back; anyone else is refused. The type check and bun test test/mcp.test.ts test/invites.test.ts pass. I did not run the forge's full check. I didn't confirm the link against signup, because signup doesn't read invites yet (that is part 2).

  • Who can mint: only a forge admin who is a person, using an admin token. A non-admin gets forbidden, and so does an admin on an rw token.
  • Result: days defaults to 7 and must be a whole number from 1 to 30; anything else is invalid. The answer is the invite's fields plus link, which has the form <origin>/signup?invite=<token>. The bare token is not returned separately. Minting goes through the same mint action as the API, so it records the same invite.minted event.
  • Shared helper: inviteLink(origin, token) in src/actions/invites.ts now builds the link for both the MCP tool and POST /api/v1/admin/invites.
  • Tests: the tool list in test/mcp.test.ts ("it speaks the protocol…") now expects mint_invite, placed just before account_role. A new test checks a non-admin and a non-admin token are refused, a bad days is rejected, an admin gets a well-formed link, and the minted invite appears in the API's invite list.
  • README: the Invites paragraph now mentions the tool.

I did not make the tool dry-run by default. Minting only creates an expiring token, and a plan step would add a round trip for no safety gain.

To try it, call tools/call with {name:"mint_invite", arguments:{days:3}} using an admin token.

🤖 Generated with Claude Code

Closes #2338

commented

I added a mint_invite {days?} MCP tool. An admin gets a single-use signup link back; anyone else is refused. The type check and bun test test/mcp.test.ts test/invites.test.ts pass. I did not run the forge's full check. I didn't confirm the link against signup, because signup doesn't read invites yet (that is part 2).

  • Who can mint: only a forge admin who is a person, using an admin token. A non-admin gets forbidden, and so does an admin on an rw token.
  • Result: days defaults to 7 and must be a whole number from 1 to 30; anything else is invalid. The answer is the invite's fields plus link, which has the form <origin>/signup?invite=<token>. The bare token is not returned separately. Minting goes through the same mint action as the API, so it records the same invite.minted event.
  • Shared helper: inviteLink(origin, token) in src/actions/invites.ts now builds the link for both the MCP tool and POST /api/v1/admin/invites.
  • Tests: the tool list in test/mcp.test.ts ("it speaks the protocol…") now expects mint_invite, placed just before account_role. A new test checks a non-admin and a non-admin token are refused, a bad days is rejected, an admin gets a well-formed link, and the minted invite appears in the API's invite list.
  • README: the Invites paragraph now mentions the tool.

I did not make the tool dry-run by default. Minting only creates an expiring token, and a plan step would add a round trip for no safety gain.

To try it, call tools/call with {name:"mint_invite", arguments:{days:3}} using an admin token.

🤖 Generated with Claude Code

Approved

Approved. mint_invite refuses everyone except a forge admin who is a person on an admin-scope token. That check is made twice: in the tool (mayBoards + scopeAllows(…, "admin")) and again in actions/invites.mint. days is validated by the same parseDays as the API, the invite.minted event comes from the shared action, and the bare token is only ever inside link. Pulling the link into inviteLink() means the API and MCP cannot drift apart. The tools/list expectation is updated. The new test fails without the tool: it checks a non-admin, an rw token and a bad days are refused, and that a minted invite shows in the API list. Locally at 272bc0f, tsc --noEmit is clean and bun test test/mcp.test.ts test/invites.test.ts is 33 pass, 0 fail.

One criterion in #2338 is not met here and cannot be: "a link that works with part 2". Part 2 (#2336) is still open and signup does not read invites, so that round-trip test belongs in #2336, using this tool's link. There is no terminal or page door for invites yet. That is part 3 (#2337), so it is not a gap in this pull.

I could not reach the preview from my sandbox, and the screenshots were not readable to me. This verdict rests on the code and the local tests.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs