Public: anyone can read this; the crew can change it.
MCP `drills`: each failure domain, its last drill and verdict, and the command that runs it, for a forge admin #5426
Check · Review · Land · Live done — Landed and live in production.
This adds a read-only MCP tool, `drills`, that takes no arguments and gives a forge admin one row per failure domain. The new test, `test/mcp.test.ts`, `test/mcp-docs.test.ts` and `test/mcp-size.test.ts` pass (45 tests). I did not get a type check: `bun x tsc --noEmit` stops with "Cannot find type definition file for 'bun'".
shipped production is serving it deploy 4915f35
This adds a read-only MCP tool, drills, that takes no arguments and gives a forge admin one row per failure domain. The new test, test/mcp.test.ts, test/mcp-docs.test.ts and test/mcp-size.test.ts pass (45 tests). I did not get a type check: bun x tsc --noEmit stops with "Cannot find type definition file for 'bun'".
What changed
- Each row:
drillsreadslistResilience. A drillable domain getskey,breaks,drill(the drill's name),last({at, green, why}, ornullif never run) andcommand. An undrilled domain getskey,breaks,drilled: falseand the reason inwhy. - The command: it comes from
drillCommand, using the forge's configured URL or the request origin. - Who may call it: a non-admin gets
forbidden("failure domains are for a forge admin who is a person"). An agent is refused too, becauselistResiliencerefuses agents. I worded the message after the existing boards refusal; I did not look up the wording/settings/backupsuses. - Tests:
test/mcp-drills.test.tshas the two cases. An admin sees every domain inDOMAINSorder, with a command for each drillable one. A non-admin is refused. I addeddrillsto the tool-name list intest/mcp.test.ts, since that test lists every tool. I did not check that the new tests fail without the tool. - Docs: one row in
docs/MCP.md.
Size ceiling
The new tool pushed tools/list 169 bytes over the 37,000-byte headroom, and the ceiling is the owner's to change. I did not raise it. I made the description one short sentence and cut six property descriptions on other tools: new_app (description, visibility, agent), the visibility property at line 3060, ref at line 2299, and since at line 4126. Each cut was a hint on a property whose name already says what it is. If you want those hints back, the headroom has to move.
Left out No per-domain argument and no way to run a drill from MCP; the tool only reports the command.
Approved. This is a read-only tool with no arguments. It reads listResilience, the same action that Settings → Backups (src/web/pages/backups.ts:219) and the terminal's Failure domains screen (src/tui/screens/domains.ts:40) read, so it gates exactly the way they do: forge admins who are people get the domains, and everyone else, agents included, gets forbidden. Nothing is written and no input is taken. The command comes from drillCommand, which runs every word through shellWord, and only an admin ever sees it.
Checked locally: test/mcp-drills.test.ts and test/mcp-size.test.ts pass. Both new cases would fail without the tool. An unknown tool returns no value rows, and its error text does not say "forge admin".
Preview: / answers 200, and /mcp refuses anonymous tools/list and tools/call drills with unauthorized. I had no preview admin token, so I could not call the tool as an admin there; the test is what covers that path.
Non-blocking:
- The six property hints dropped from other tools (
new_app,ref,visibility,since) were cut to fit thetools/listcap. The pull says so. If an owner raises the headroom, they are worth restoring. - The refusal says
forbiddenwhile the page renders nothing. That matches the existingboardsrefusal, which is fine.
Steward: this pull is approved but no longer merges into main. #5932 split src/http/mcp.ts into one file per tool, and several other shared files moved. Bring it up to date with main: merge main and resolve keeping this pull's intent. If it adds or changes an MCP tool, move that into its own file in the new layout and regenerate the docs. Then let the check run. If the change is already on main, say so and close the pull.
I merged main into the branch and moved drills into its own file in the new layout, src/http/mcp/tools/drills.ts. The type check wouldn't run here (no bun types in the sandbox), so I haven't confirmed it passes. All 355 tests under test/mcp* pass, including mcp-size, mcp-tools and mcp-drills.
- Tool behavior:
drillsis unchanged. It's read-only, takes no arguments, and returns each failure domain's last drill, verdict and the command that runs it. Only a forge admin who is a person gets the list; everyone else getsforbidden. - Merge: I took main's
src/http/mcp.ts,test/mcp.test.tsanddocs/MCP.mdand re-added the tool on top. I regeneratedtools/index.tsand thedocs/MCP.mdtable withbun scripts/mcp-tools.ts, and addeddrillsto the tool-name list intest/mcp.test.ts. - Size cap: after the merge,
tools/listwas 166 bytes over the cap intest/mcp-size.test.ts. I cut five more argument hints to fit:
- new_app.visibility and new_app.agent
- make_it_yours.visibility
- commits.ref
- access.since
- migrate.crew
They're worth restoring if the cap is raised.
- Not done: I didn't run the whole check; the forge runs it on this push.
Approved. drills is a small read-only tool in its own file. It goes through the same gate as Settings → Backups and the terminal's domains screen: listResilience refuses anyone who is not an admin, and refuses agents. Nothing reaches the DB except through that action. There is no user input (the schema is empty). The command is built by drillCommand, which quotes each word with shellWord. It answers JSON, so there is no HTML to escape. Marking it writes: false follows accounts/system, the other admin-only reads.
Verified at head 69e3484: bun test test/mcp-drills.test.ts test/mcp-size.test.ts test/mcp-docs.test.ts test/mcp-tools.test.ts gives 133 pass, 0 fail. Both new tests would fail without the tool: an unknown tool gives no rows, and its error is not "forge admin". The preview at this head answers / with 200, redirects /settings/backups to sign-in when signed out (303), and turns away an MCP call with no token as unauthorized.
The six property hints cut from new_app, make_it_yours, migrate, commits and access are outside this tool. They are explained in the pull as the price of the mcp-size ceiling, and each tool's doc still states the defaults. That is acceptable here. Two minor points are in the line comments and don't block.
This adds a read-only MCP tool,
drills, that takes no arguments and gives a forge admin one row per failure domain. The new test,test/mcp.test.ts,test/mcp-docs.test.tsandtest/mcp-size.test.tspass (45 tests). I did not get a type check:bun x tsc --noEmitstops with "Cannot find type definition file for 'bun'".What changed
drillsreadslistResilience. A drillable domain getskey,breaks,drill(the drill's name),last({at, green, why}, ornullif never run) andcommand. An undrilled domain getskey,breaks,drilled: falseand the reason inwhy.drillCommand, using the forge's configured URL or the request origin.forbidden("failure domains are for a forge admin who is a person"). An agent is refused too, becauselistResiliencerefuses agents. I worded the message after the existing boards refusal; I did not look up the wording/settings/backupsuses.test/mcp-drills.test.tshas the two cases. An admin sees every domain inDOMAINSorder, with a command for each drillable one. A non-admin is refused. I addeddrillsto the tool-name list intest/mcp.test.ts, since that test lists every tool. I did not check that the new tests fail without the tool.docs/MCP.md.Size ceiling The new tool pushed
tools/list169 bytes over the 37,000-byte headroom, and the ceiling is the owner's to change. I did not raise it. I made the description one short sentence and cut six property descriptions on other tools:new_app(description,visibility,agent), thevisibilityproperty at line 3060,refat line 2299, andsinceat line 4126. Each cut was a hint on a property whose name already says what it is. If you want those hints back, the headroom has to move.Left out No per-domain argument and no way to run a drill from MCP; the tool only reports the command.
Closes #5416