Skip to content

Custom Tools

Tools are structured functions that an LLM can call. The SDK uses Zod schemas to validate inputs and serialize parameters for model providers.

Use defineTool to describe inputs and return types. The SDK will validate parameters before execution and expose a JSON schema to the model.

import { defineTool } from '@dreadnode/agents';
import { z } from 'zod';
export const enrichIndicator = defineTool({
name: 'enrich_indicator',
description: 'Look up an indicator in a local cache.',
parameters: z.object({
indicator: z.string().describe('IP, domain, or hash to enrich'),
}),
execute: async ({ indicator }) => ({ indicator, verdict: 'unknown' }),
});

createToolkit turns a list of tools into a keyed map with helpers for execution and schema generation.

import { createToolkit } from '@dreadnode/agents';
import { enrichIndicator } from './tools/enrichIndicator';
const toolkit = createToolkit([enrichIndicator]);
const schemas = toolkit.toSchema();
console.log(schemas);

Capabilities ship tool definitions in capability.yaml. Use wrapTool and wrapCapability to convert capability tool defs into AI SDK tool() instances that can be merged into your tool map.

import { loadCapability, wrapCapability, wrapTool } from '@dreadnode/agents';
async function main(): Promise<void> {
const loaded = await loadCapability('./capabilities/threat-hunting');
const wrapped = wrapCapability(loaded);
const firstTool = loaded.manifest.tools?.[0];
if (firstTool) {
const single = wrapTool(firstTool, loaded);
console.log('Wrapped tool:', (single as Record<string, unknown>)._capName);
}
console.log(`Wrapped ${wrapped.tools.length} tools from ${wrapped.name}.`);
}
main().catch((error) => {
console.error(error);
process.exit(1);
});
  • Keep inputs small and explicit (prefer enums or constrained strings).
  • Return structured objects, not raw strings.
  • Fail fast with clear error messages when preconditions are not met.