Tools
defineTool, Zod schemas, error handling, dynamic tool dispatch.
Canonical contract:
packages/sdk/docs.md—defineTool+Agent.create({ tools }).
A tool is a typed function the agent can call. The SDK validates inputs against a Zod schema, surfaces errors as tool_result isError: true (never throws into the loop), and converts the schema to JSON Schema for the provider.
Define a tool
import { defineTool } from "@theokit/sdk";
import { z } from "zod";
const getWeather = defineTool({
name: "get_weather",
description: "Look up the current weather in a given city.",
inputSchema: z.object({
city: z.string().describe("City name, e.g. 'Brasília'"),
}),
async execute({ city }) {
return await fetch(`https://api.weather.com/${city}`).then((r) => r.text());
},
});Pass tools: [getWeather] to Agent.create and the agent decides when to call it based on the user prompt + tool description.
Error handling
execute can throw — the SDK catches and converts to a tool_result isError: true block that the agent receives as part of the conversation. The loop continues; the agent typically retries or apologizes.
async execute({ city }) {
if (city.length === 0) throw new Error("City is required");
// The agent sees: { type: "tool_result", isError: true, content: "City is required" }
}This is intentional — see docs.md and ADR D89.
Repair: malformed model output
If the model emits a malformed tool call (extra whitespace in JSON, missing trailing brace, etc.), the SDK applies 3 idempotent repairs before giving up. See ADR D87. You don't configure this — it's automatic.
Dynamic tool dispatch (subagents / toolsets)
Agents can expose tools that aren't all available at once. The Toolset primitive lets you group tools and check availability per turn:
const agent = await Agent.create({
// ...
tools: [
{ name: "web", tools: [search, fetch] },
{ name: "shell", tools: [run, ls] },
],
});See docs.md for the full Toolset / Subagent surface.
MCP tools
Tools can also come from MCP servers (Model Context Protocol). MCP servers expose tools via stdio or HTTP and the SDK consumes them transparently. See MCP.