Give the assistant a new tool
Let the AI assistant read or change something it can't reach today. If the action belongs on the public API, give its route in services/api/src/public/routes.ts an mcp: { expose: { name, description } } decision and skip to step 7: the assistant picks it up, and any method but get counts as a write. Otherwise, add an in-app-only tool.
- In
services/api/src/chat/, add the tool to its area's<area>-tools.ts, or create one that exports anInAppTool[]. - Give it a snake_case
nameprefixed with the area (wiki_move_page), unique across every tool, and adescriptionwritten for the model: what it returns, when to use it, and which tool to call first. - Write a zod
inputSchemawith bounded strings (.min(),.max()). The model sees it as the tool's schema, and it validates the model's input before the tool runs. - Set
mutates: trueif the tool creates, changes, deletes or runs anything. That puts it behind the user's confirmation. When unsure, choosetrue. - Write
run(caller, input)to call the area's tRPC procedure throughcaller. Don't check permissions or query the database in the tool; the procedure already enforces permissions and emits events. - If you created a file, add its array to
inAppToolsinbuildChatToolset()(services/api/src/chat/tools.ts). - Add the name to
LABELLED_TOOLSinservices/web/src/components/chat/tool-call-card.tsx, and achat.toolActions.<name>phrase ("move a wiki page") to every catalog inservices/web/src/messages/. Without it, the confirm card shows a generated label. - Add a test with a fake caller, following
services/api/tests/automation-tools.test.ts: assert the names, themutatesflags, and that bad input is rejected. - Run the checks:
pnpm --filter api test pnpm check:i18n - Add the tool to Assistant tools, with its permission.
Good to know: for a structure the model must get right, such as an automation graph, prefer one whole-object write validated before saving, so the model gets a list of problems to fix.