Skip to content

Custom Tools

Custom Tools

Tools are functions that agents can call during execution. Define them in agents/tools/ and reference them by name in your agent definitions.

Defining a Tool

agents/tools/search-kb.ts
import { defineTool } from '@kernova/framework';
export default defineTool({
name: 'search-kb',
description: 'Search the company knowledge base for relevant articles',
parameters: {
query: {
type: 'string',
description: 'The search query',
required: true,
},
limit: {
type: 'number',
description: 'Maximum results to return',
default: 5,
},
},
execute: async ({ query, limit }, ctx) => {
ctx.log(`Searching KB for: ${query}`);
// Your implementation here
const results = await fetch(`https://api.mycompany.com/kb/search?q=${query}&limit=${limit}`);
return await results.json();
},
});

Tool Discovery

Tools are auto-discovered from these directories:

agents/tools/ ← Primary location
tools/ ← Alternative location

Any .ts or .js file that exports a defineTool() result will be registered.

Using Tools in Agents

Reference tools by their name field:

agents/support.agent.ts
export default defineAgent({
name: 'Support',
instructions: '...',
tools: ['search-kb', 'create-ticket', 'send-email'],
});

The agent’s LLM will see the tool descriptions and decide when to call them.

Tool Parameters

Parameter types supported:

TypeDescriptionExample
stringText input"search query"
numberNumeric value5, 3.14
booleanTrue/falsetrue
arrayList of values["tag1", "tag2"]
objectNested structure{ "key": "value" }
parameters: {
query: { type: 'string', description: 'Search query', required: true },
maxResults: { type: 'number', description: 'Max results', default: 10 },
includeArchived: { type: 'boolean', description: 'Include archived items' },
tags: { type: 'array', description: 'Filter by tags', items: { type: 'string' } },
}

Tool Context

Every tool receives a context object as the second argument:

execute: async (params, ctx) => {
// Environment variables (from project secrets)
const apiKey = ctx.env.MY_API_KEY;
// Log messages (visible in monitoring)
ctx.log('Starting search...');
// User info (if available)
const userId = ctx.user?.id;
// Thread ID (`trigger_<name>` for trigger runs)
const thread = ctx.threadId;
// Whether a person asked for this, or a trigger is running on its own
if (ctx.autonomous) {
ctx.log(`Running for trigger ${ctx.trigger}`);
}
}

ctx.autonomous is true for trigger and schedule runs, and false for requests to an agent. Use it to ask more of actions nobody asked for, for example letting an agent send email when a user asks but not on its own:

execute: async ({ to, body }, ctx) => {
if (ctx.autonomous && !settings.autoSendEmail) {
return { blocked: true, error: 'Sending email on my own is turned off.' };
}
// ...
}
FieldDescription
envEnvironment variables
log(message)Log a message
user{ id } of the user the request is for, when known
threadIdConversation thread (trigger_<name> for trigger runs)
autonomoustrue for trigger and schedule runs, false for requests
triggerName of the trigger that started the run
agentRoute of the agent that called the tool

Tool Options

OptionTypeDefaultDescription
namestringrequiredUnique tool name
descriptionstringrequiredDescription shown to the LLM
parametersobjectrequiredParameter schema
executefunctionrequiredImplementation
rateLimitnumber—Max calls per minute
requiresConfirmationbooleanfalseRequire user approval before executing

Examples

Database Query Tool

export default defineTool({
name: 'query-users',
description: 'Query the user database',
parameters: {
email: { type: 'string', description: 'Filter by email' },
plan: { type: 'string', description: 'Filter by plan', enum: ['free', 'pro', 'enterprise'] },
},
execute: async ({ email, plan }) => {
return await db.users.findMany({ where: { email, plan } });
},
});

HTTP API Tool

export default defineTool({
name: 'get-weather',
description: 'Get current weather for a city',
parameters: {
city: { type: 'string', description: 'City name', required: true },
},
execute: async ({ city }, ctx) => {
const res = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${ctx.env.WEATHER_API_KEY}`
);
return await res.json();
},
});

Confirmation-Required Tool

export default defineTool({
name: 'delete-account',
description: 'Permanently delete a user account',
parameters: {
userId: { type: 'string', description: 'User ID to delete', required: true },
},
requiresConfirmation: true,
execute: async ({ userId }) => {
await db.users.delete({ where: { id: userId } });
return { deleted: true };
},
});