Skip to main content
Tool calling agents can execute custom functions to interact with external systems, databases, APIs, or perform computations. The agent decides when to call tools based on the user’s request and uses the results to provide better responses.

Creating a Custom Tool

To create a tool, you need to implement the agents.Tool interface:

The hastekit.NewTool Helper

If you don’t need the full control of agents.BaseTool, hastekit.NewTool turns any func(ctx, In) (Out, error) into a tool. The input JSON schema is derived from the argument struct, and arguments/results are marshalled for you:
The available options are: Both agents.BaseTool-based tools and hastekit.NewTool tools implement hastekit.Tool and can be mixed into the same Tools slice.

Tool Annotations

Tools can advertise what they do. The hints mirror MCP’s tool annotations, so hints read off an MCP server and hints declared on a local function tool are the same thing — one policy can read both.
On a agents.BaseTool-based tool, set the Annotations field directly:
MCP tools carry whatever their server declared; nothing extra is needed to pick them up.

Reading the hints back

AnnotationsOf returns nil for a tool that carries none, and the nil is usable: every Is* helper is nil-safe. Every hint is a pointer, so “nothing was said” stays distinguishable from “false was said”. Prefer the Is* helpers over reading the fields directly: they are nil-safe and apply MCP’s defaults, which are deliberately conservative — an unset DestructiveHint reads as destructive, an unset ReadOnlyHint as not read-only. A read-only tool is never destructive and is always idempotent, whatever those hints say. IsDeclaredDestructive() is the variant that asks whether the tool actually said it may destroy state, rather than assuming the worst when nothing was said. It is usually the one a hook gating tool calls wants: an unannotated tool has made no claim, and gating on the absence of a claim would put every tool written before annotations behind a prompt.
Hints are self-reported: they describe intent, not enforcement. Never let a hint from an untrusted MCP server widen what a tool is allowed to do.

Reporting Progress

A long-running tool can emit progress updates that stream to the client on the same channel as the rest of the run. Call params.ReportProgress — it is nil-safe, so tools can call it unconditionally regardless of which runtime is executing them:
Updates arrive on the run’s chunk stream as tool.progress chunks:
Progress should increase monotonically across a call; Total is optional (0 means unknown). The shape mirrors MCP’s notifications/progress, so MCP tools map their server-sent progress onto the same chunks.

Basic Example

Here’s a simple example of an agent with a custom tool: