Tools
Introduction
Section titled “Introduction”The kit ships no business tools. You register yours at startup, and each agent definition selects among them by name. A tool is an ordinary LangChain StructuredTool.
Registering a tool
Section titled “Registering a tool”This is how the demo API (apps/api) registers its tools:
import { createAskHumanTool, createWebSearchTool } from '@pilotoo/agent-runtime';import { PilotooModule } from '@pilotoo/server';
PilotooModule.forRoot({ env: process.env, tools: (registry) => { registry.register(createWebSearchTool(process.env)); registry.register(createAskHumanTool(), { optIn: true }); },});The callback receives the ToolRegistry (the TOOL_REGISTRY token). A plain StructuredTool[] is also accepted and registers each tool with defaults. A duplicate name throws at registration.
Without PilotooModule, build the registry yourself with createToolRegistry() and pass it to the runner.
Writing your own tool
Section titled “Writing your own tool”import { tool } from '@langchain/core/tools';import { z } from 'zod';
export const echo = tool(async ({ text }) => text, { name: 'echo', description: 'Repeats the text back.', schema: z.object({ text: z.string() }),});Register it with registry.register(echo), then let an agent use it:
runtime: tools: [echo]Making a tool opt-in
Section titled “Making a tool opt-in”Pass { optIn: true } for a tool that is dangerous or only some agents should have, such as one that pauses a run to ask the user.
registry.register(createAskHumanTool(), { optIn: true });| In the agent definition | The agent gets |
|---|---|
tools: null |
Every registered tool except opt-in ones. |
tools: [a, b] |
Exactly the named tools, opt-in included. |
tools: [] |
No registered tool. |
An unregistered name is warned about and skipped.
Built-in tools (ls, read_file, execute, task and the rest) are separate: an agent gets exactly the ones its builtinTools list names. See Agents.
Calling your own API as the user
Section titled “Calling your own API as the user”A tool that reads or changes your app’s data should act with the permissions of the person chatting, not with a service account. createHostFetch(config) returns a fetch that adds the caller’s Authorization header, so your API authorizes each call as that caller.
import { tool } from '@langchain/core/tools';import { createHostFetch } from '@pilotoo/agent-runtime';import { z } from 'zod';
export const getOrder = tool( async ({ id }, config) => { const hostFetch = createHostFetch(config); const response = await hostFetch(`https://api.example.com/orders/${id}`); return response.text(); }, { name: 'get_order', description: 'Reads one order.', schema: z.object({ id: z.string() }), },);callerOf(config) returns the calling identity when the tool needs the user or tenant itself. The caller travels in LangGraph’s per-invocation context. It is never written to state, the checkpoint or the stream.
Ready-made tools
Section titled “Ready-made tools”| Tool | What it does |
|---|---|
createWebSearchTool(env) |
A web_search tool (Tavily). The key is read only from TAVILY_API_KEY in the env you pass; without one the call fails with a readable error. |
createAskHumanTool() |
An ask_human tool that pauses the run until the user answers. Register it with { optIn: true }. |
Both are exported by @pilotoo/agent-runtime.
Options
Section titled “Options”registry.register(tool, options?):
| Option | Default | Meaning |
|---|---|---|
optIn |
false |
Only agents that name the tool get it. |
Next steps
Section titled “Next steps”- Let an agent edit configuration with config tools.
- Declare tools that run in the user’s browser: client tools.
- Schedule recurring prompts with the opt-in schedule tools.