Adding a Built-In Tool
This guide walks through adding a new built-in tool that the LLM can call during tool-loop execution.
-
Create a file in
src/tools/functionCalls/. -
Export a class that extends
BaseTool. -
Define the required members:
name— tool identifier (must be unique across all registered tools)description— what the tool does (shown to the LLM in the context)category— grouping label for logging and UIparameters— JSON Schema describing tool argumentsexecute()— async method that runs the tool and returns a result string
-
Define optional members when needed:
requiresFeatureFlag— gate the tool behind a server config togglerequiresPermissions— restrict to certain Discord permission levelsrequiresFollowUp— signal that the tool result needs a follow-up generation turn
-
Forward
context.abortSignalto every HTTP call the tool makes:- Pass
signal: context.abortSignalto rawfetchcalls. - Pass
externalSignal: context.abortSignaltosafeDownloadcalls. - Pass it through any helper option that accepts an
AbortSignal.
This gives
/bot killtrue HTTP-level cancellation. Without it, the underlying request keeps running even after the turn is stopped. - Pass
-
The tool is auto-discovered by
toolInitializer.tsat startup — no manual registration is needed.
Notes on Feature-Gated Tools
Section titled “Notes on Feature-Gated Tools”If the tool should be toggled by a server config field, see
docs/guides/adding-feature-flag-tool.md for the full flag-mapping steps.
Quality Gate
Section titled “Quality Gate”bun run check # TypeScript strict modebun run lint # Biome formattingThen test the tool by prompting the bot in a way that triggers it and checking the tool-loop logs.
Related Docs
Section titled “Related Docs”docs/pipelines/tool-loop/— how tools are dispatched and results assembleddocs/guides/adding-feature-flag-tool.md— gating a tool behind a feature flag