Custom Tools
Register your own tools so the AI can call them
Custom tools let you expose any function in your application to the AI. If your app can do it — search a database, fetch from an API, trigger a workflow — you can make it a tool.
The useTool hook
import { useTool } from "@intentctrl/react";
import { z } from "zod";
export function SearchWidget() {
useTool({
id: "search_knowledge_base",
description: "Search the knowledge base for relevant articles",
inputSchema: z.object({
query: z.string(),
maxResults: z.number().min(1).max(20).optional(),
}),
handler: async ({ query, maxResults }) => {
const results = await searchApi(query, maxResults ?? 5);
return results;
},
});
return <div>Search widget</div>;
}Every time the user sends a message, all registered tools are sent to the AI as available functions.
Tool contract
Prop
Type
Writing good descriptions
The description is the most important part of a custom tool. It's how the AI decides whether to use it and how to fill in the parameters.
Bad: "Search function"
Good: "Search the product catalog by name or SKU. Returns matching products with their price, stock level, and category."
Bad: "Get user data"
Good: "Retrieve user profile information by user ID. Use this when the user asks about their account details, settings, or personal information."
Lifecycle
Tools registered with useTool() are scoped to the component that registers them:
- Mounted — tool is available to the AI
- Unmounted — tool is removed automatically
- Multiple instances — if two components register the same tool ID, it stays active until both are unmounted
This means a tool for a settings panel only exists while the settings panel is open. A tool for a dashboard widget only exists while that widget is visible.
Example: Fetching data
import { useTool } from "@intentctrl/react";
import { z } from "zod";
export function OrderLookup() {
useTool({
id: "get_order_status",
description:
"Get the current status of an order by order ID. Returns delivery date, tracking number, and current status.",
inputSchema: z.object({
orderId: z.string(),
}),
handler: async ({ orderId }) => {
const response = await fetch(`/api/orders/${orderId}`);
return response.json();
},
});
return null;
}Last updated on