Documentation
¶
Overview ¶
Package tool provides type-safe tool definitions, auto-wiring from typed functions, a concurrent-safe registry, and middleware composition.
A tool is: name + description + input schema + output schema + execute function. Schemas are auto-generated from Go types using github.com/kbukum/gokit/schema.
Quick start — create a tool from a typed function:
type SearchInput struct {
Query string `json:"query" jsonschema:"required,description=Search text"`
}
type SearchOutput struct {
Items []Item `json:"items"`
}
searchTool := tool.FromFunc("search", "Search content", doSearch)
Register and use:
registry := tool.NewRegistry()
registry.Register(searchTool.AsCallable())
result, err := registry.Call(ctx, "search", json.RawMessage(`{"query":"hello"}`))
Index ¶
- type Annotations
- type BatchCall
- type BatchResult
- type Callable
- type Context
- type Definition
- type FilterOption
- type Formatter
- type FormatterFunc
- type Handler
- type HandlerFunc
- type InMemoryMetrics
- type MetricEntry
- type MetricsCollector
- type Middleware
- func Chain(middlewares ...Middleware) Middleware
- func WithLogging(logger *slog.Logger) Middleware
- func WithMetrics(collector MetricsCollector) Middleware
- func WithRecover() Middleware
- func WithResultLimit(maxBytes int) Middleware
- func WithRetry(cfg RetryConfig) Middleware
- func WithTimeout(d time.Duration) Middleware
- func WithValidation() Middleware
- type Registry
- func (r *Registry) Call(ctx *Context, name string, input json.RawMessage) (*Result, error)
- func (r *Registry) CallBatch(ctx *Context, calls []BatchCall) []BatchResult
- func (r *Registry) Filter(opts ...FilterOption) []Definition
- func (r *Registry) Get(name string) (Callable, bool)
- func (r *Registry) Len() int
- func (r *Registry) List() []Definition
- func (r *Registry) Names() []string
- func (r *Registry) Register(t Callable) error
- func (r *Registry) Search(query string) []Definition
- type Result
- type RetryConfig
- type Tool
- func FromFunc[I, O any](name, description string, fn func(ctx context.Context, input I) (O, error)) *Tool[I, O]
- func FromFuncInputOnly[I, O any](name, description string, fn func(ctx context.Context, input I) (O, error)) *Tool[I, O]
- func FromProvider[I, O any](def Definition, p provider.RequestResponse[I, O]) *Tool[I, O]
- func NewTool[I, O any](def Definition, handler Handler[I, O]) *Tool[I, O]
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Annotations ¶
type Annotations struct {
// Title is a human-readable display name.
Title string `json:"title,omitempty"`
// ReadOnlyHint indicates the tool does not modify external state.
ReadOnlyHint *bool `json:"readOnlyHint,omitempty"`
// DestructiveHint indicates the tool may perform destructive operations.
DestructiveHint *bool `json:"destructiveHint,omitempty"`
// IdempotentHint indicates the tool can be called repeatedly with the same result.
IdempotentHint *bool `json:"idempotentHint,omitempty"`
// OpenWorldHint indicates the tool interacts with external entities.
OpenWorldHint *bool `json:"openWorldHint,omitempty"`
// Category groups tools for filtering.
Category string `json:"category,omitempty"`
// Tags are searchable labels.
Tags []string `json:"tags,omitempty"`
// ExecutionHint tells the frontend how to handle the tool result.
// "ui" — tool only validates/extracts params; frontend drives the action.
// "backend" — tool executes a real operation; result is authoritative (default).
// "hybrid" — tool executes backend AND frontend should refresh/navigate.
// An empty string is treated as "backend" — the most common case for
// server-resident tools — so producers don't have to set it explicitly.
ExecutionHint string `json:"executionHint,omitempty"`
}
Annotations provides metadata hints about a tool's behavior. Field names align with the MCP tool specification (2025-11-25).
type BatchCall ¶
type BatchCall struct {
Name string `json:"name"`
ID string `json:"id"`
Input json.RawMessage `json:"input"`
}
BatchCall represents a single tool invocation in a batch.
type BatchResult ¶
type BatchResult struct {
ID string `json:"id"`
Result *Result `json:"result,omitempty"`
Err error `json:"error,omitempty"`
}
BatchResult pairs a batch call with its result.
type Callable ¶
type Callable interface {
// Definition returns the tool's metadata.
Definition() Definition
// Validate checks the input against the tool's input schema.
Validate(input json.RawMessage) schema.ValidationResult
// Call executes the tool with JSON input and returns a structured Result.
Call(ctx *Context, input json.RawMessage) (*Result, error)
}
Callable is the type-erased interface for tools that can be stored in heterogeneous collections (registries). It accepts raw JSON input and returns a structured Result.
func Apply ¶
func Apply(c Callable, middlewares ...Middleware) Callable
Apply wraps a Callable with the given middleware chain.
type Context ¶
type Context struct {
context.Context
// RequestID identifies the overall request (e.g., agent turn).
RequestID string
// ToolUseID identifies this specific tool invocation.
ToolUseID string
// MaxResultSize limits result content size in bytes. Zero means unlimited.
MaxResultSize int
// contains filtered or unexported fields
}
Context carries execution metadata through the tool call chain. It embeds context.Context for cancellation, deadlines, and values, and adds tool-specific fields (request ID, tool use ID, metadata).
Use NewContext to create one from a standard context.Context.
func Background ¶
func Background() *Context
Background creates a Context from context.Background().
func NewContext ¶
NewContext creates a Context from a standard context.Context.
func (*Context) WithCancel ¶
func (c *Context) WithCancel() (*Context, context.CancelFunc)
WithCancel returns a derived Context with a cancel function.
func (*Context) WithTimeout ¶
WithTimeout returns a derived Context with a timeout and its cancel function.
type Definition ¶
type Definition struct {
// Name is the unique tool identifier.
Name string `json:"name"`
// Description explains what the tool does.
Description string `json:"description"`
// InputSchema is a standard JSON Schema describing the input.
InputSchema schema.JSON `json:"inputSchema"`
// OutputSchema is a standard JSON Schema describing the output (optional).
OutputSchema schema.JSON `json:"outputSchema,omitempty"`
// Annotations holds MCP-aligned metadata hints.
Annotations *Annotations `json:"annotations,omitempty"`
// ReadOnly indicates the tool does not modify external state.
// Read-only tools can be executed concurrently in batch operations.
ReadOnly bool `json:"readOnly,omitempty"`
// Destructive indicates the tool may perform irreversible operations.
Destructive bool `json:"destructive,omitempty"`
// Timeout is the default timeout for this tool. Zero means no default.
Timeout time.Duration `json:"-"`
// MaxResultSize limits result content size in bytes. Zero means unlimited.
MaxResultSize int `json:"maxResultSize,omitempty"`
}
Definition describes a tool in MCP-aligned format. It uses standard JSON Schema for input/output descriptions.
type FilterOption ¶
type FilterOption func(*filterConfig)
FilterOption configures tool filtering.
func WithCategory ¶
func WithCategory(cat string) FilterOption
WithCategory filters tools by category annotation.
func WithExecutionHint ¶
func WithExecutionHint(hint string) FilterOption
WithExecutionHint filters tools by execution hint annotation.
func WithTags ¶
func WithTags(tags ...string) FilterOption
WithTags filters tools that have all specified tags.
type Formatter ¶
type Formatter interface {
// Format transforms the result content. The tool name is provided for context.
Format(toolName string, result *Result) (string, error)
}
Formatter transforms a tool Result's content before it is sent to the LLM. This allows tools to return structured data while presenting it to the model in a more readable form (markdown tables, summaries, etc.).
var MarkdownTableFormatter Formatter = FormatterFunc(formatMarkdownTable)
MarkdownTableFormatter formats JSON array results as markdown tables. Non-array results are returned as-is.
func ChainFormatters ¶
ChainFormatters applies formatters in sequence.
func SummaryHeaderFormatter ¶
func SummaryHeaderFormatter() Formatter
SummaryHeaderFormatter prepends a one-line summary header to the result.
func TruncateFormatter ¶
TruncateFormatter returns a Formatter that truncates content to maxLen characters.
type FormatterFunc ¶
FormatterFunc adapts a function to the Formatter interface.
type HandlerFunc ¶
HandlerFunc is the function adapter for Handler. It allows using a plain function where a Handler is expected.
type InMemoryMetrics ¶
type InMemoryMetrics struct {
// contains filtered or unexported fields
}
InMemoryMetrics is a simple in-memory metrics collector for testing.
func (*InMemoryMetrics) CallCount ¶
func (m *InMemoryMetrics) CallCount(toolName string) int
CallCount returns total calls for the given tool name.
func (*InMemoryMetrics) Entries ¶
func (m *InMemoryMetrics) Entries() []MetricEntry
Entries returns a copy of all recorded metrics.
func (*InMemoryMetrics) ErrorCount ¶
func (m *InMemoryMetrics) ErrorCount(toolName string) int
ErrorCount returns total error calls for the given tool name.
func (*InMemoryMetrics) RecordCall ¶
func (m *InMemoryMetrics) RecordCall(toolName string, duration time.Duration, err error)
RecordCall implements MetricsCollector.
type MetricEntry ¶
MetricEntry records a single tool call metric.
type MetricsCollector ¶
type MetricsCollector interface {
// RecordCall records a completed tool call.
RecordCall(toolName string, duration time.Duration, err error)
}
MetricsCollector gathers tool execution metrics. Implementations can export to Prometheus, OpenTelemetry, etc.
type Middleware ¶
Middleware wraps a Callable with additional behavior.
func Chain ¶
func Chain(middlewares ...Middleware) Middleware
Chain composes multiple middlewares. The first middleware in the list is the outermost wrapper (executed first).
func WithLogging ¶
func WithLogging(logger *slog.Logger) Middleware
WithLogging returns middleware that logs tool calls.
func WithMetrics ¶
func WithMetrics(collector MetricsCollector) Middleware
WithMetrics returns middleware that records call count, latency, and error count per tool name.
func WithRecover ¶
func WithRecover() Middleware
WithRecover returns middleware that recovers from panics in tool handlers.
func WithResultLimit ¶
func WithResultLimit(maxBytes int) Middleware
WithResultLimit returns middleware that truncates result content exceeding the given byte limit.
func WithRetry ¶
func WithRetry(cfg RetryConfig) Middleware
WithRetry returns middleware that retries failed tool calls with exponential backoff and jitter.
func WithTimeout ¶
func WithTimeout(d time.Duration) Middleware
WithTimeout returns middleware that enforces a timeout on tool calls.
func WithValidation ¶
func WithValidation() Middleware
WithValidation returns middleware that validates input against the tool's schema before executing. Invalid input returns an error Result without calling the underlying tool.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry manages a collection of callable tools. It is concurrent-safe for reads and writes.
func NewRegistry ¶
func NewRegistry() *Registry
NewRegistry creates an empty tool registry.
Example ¶
ExampleNewRegistry shows how to register tools into a registry, then look them up by name (the typical pattern for agent / MCP integration).
package main
import (
"context"
"fmt"
"github.com/kbukum/gokit/tool"
)
// AddInput is a tiny demo input type used by the example.
type AddInput struct {
A int `json:"a"`
B int `json:"b"`
}
// AddOutput is the matching output type.
type AddOutput struct {
Sum int `json:"sum"`
}
func main() {
add := tool.FromFunc("add", "Add two integers",
func(_ context.Context, in AddInput) (AddOutput, error) {
return AddOutput{Sum: in.A + in.B}, nil
},
)
reg := tool.NewRegistry()
if err := reg.Register(add.AsCallable()); err != nil {
fmt.Println("register:", err)
return
}
// Retrieve by name and execute through the Callable interface.
got, ok := reg.Get("add")
fmt.Println("registered:", ok, "name:", got.Definition().Name, "tools:", reg.Len())
}
Output: registered: true name: add tools: 1
func (*Registry) CallBatch ¶
func (r *Registry) CallBatch(ctx *Context, calls []BatchCall) []BatchResult
CallBatch executes multiple tool calls. Read-only tools run concurrently; non-read-only tools run serially. Results are returned in the same order as the input calls.
func (*Registry) Filter ¶
func (r *Registry) Filter(opts ...FilterOption) []Definition
Filter returns definitions matching the given options.
func (*Registry) List ¶
func (r *Registry) List() []Definition
List returns the definitions of all registered tools.
func (*Registry) Register ¶
Register adds a tool to the registry. Returns an error if a tool with the same name already exists.
func (*Registry) Search ¶
func (r *Registry) Search(query string) []Definition
Search returns definitions matching a keyword query against name and description.
type Result ¶
type Result struct {
// Output is the structured JSON output for programmatic use.
Output json.RawMessage `json:"output,omitempty"`
// Content is a human-readable string for LLM consumption.
// If empty, Output is used as the content string.
Content string `json:"content,omitempty"`
// IsError indicates the tool encountered an error.
IsError bool `json:"is_error,omitempty"`
// Metadata carries additional data (timing, token count, etc.).
Metadata map[string]any `json:"metadata,omitempty"`
}
Result is the structured output of a tool execution. It separates structured output (for programmatic use) from human-readable content (for LLM consumption).
func JSONResult ¶
JSONResult creates a Result from a JSON-serializable value. Both Output (raw JSON) and Content (JSON string) are set.
func MustJSONResult ¶
MustJSONResult is like JSONResult but panics on error.
func TextResult ¶
TextResult creates a Result with text content only.
type RetryConfig ¶
type RetryConfig struct {
// MaxAttempts is the total number of attempts (including the initial call).
// Defaults to 3 if zero.
MaxAttempts int
// BaseDelay is the initial delay before the first retry.
// Defaults to 100ms if zero.
BaseDelay time.Duration
// MaxDelay caps the backoff delay.
// Defaults to 5s if zero.
MaxDelay time.Duration
// ShouldRetry determines if an error is retryable.
// If nil, all errors are retried.
ShouldRetry func(err error) bool
}
RetryConfig controls retry behavior.
type Tool ¶
type Tool[I, O any] struct { Def Definition // contains filtered or unexported fields }
Tool is a typed, executable capability with auto-generated schemas.
func FromFunc ¶
func FromFunc[I, O any](name, description string, fn func(ctx context.Context, input I) (O, error)) *Tool[I, O]
FromFunc creates a Tool from a typed function, auto-generating input and output JSON Schemas from the function's type parameters.
This is the primary way to create tools — minimal boilerplate:
searchTool := tool.FromFunc("search", "Search content", doSearch)
Where doSearch is: func(ctx context.Context, in SearchInput) (SearchOutput, error)
Example ¶
ExampleFromFunc shows the most common way to build a typed tool from a plain Go function. Schema generation and JSON (de)serialization are handled by FromFunc — your function works in real Go types.
package main
import (
"context"
"fmt"
"github.com/kbukum/gokit/tool"
)
// AddInput is a tiny demo input type used by the example.
type AddInput struct {
A int `json:"a"`
B int `json:"b"`
}
// AddOutput is the matching output type.
type AddOutput struct {
Sum int `json:"sum"`
}
func main() {
add := tool.FromFunc("add", "Add two integers",
func(_ context.Context, in AddInput) (AddOutput, error) {
return AddOutput{Sum: in.A + in.B}, nil
},
)
out, err := add.Execute(context.Background(), AddInput{A: 2, B: 3})
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(out.Sum)
}
Output: 5
func FromFuncInputOnly ¶
func FromFuncInputOnly[I, O any](name, description string, fn func(ctx context.Context, input I) (O, error)) *Tool[I, O]
FromFuncInputOnly creates a Tool that auto-generates only the input schema. Use when the output type is dynamic or not useful for schema generation (e.g., map[string]any).
func FromProvider ¶
func FromProvider[I, O any](def Definition, p provider.RequestResponse[I, O]) *Tool[I, O]
FromProvider creates a Tool from an existing provider.RequestResponse. The Definition must be provided since providers don't carry schema metadata.
func NewTool ¶
func NewTool[I, O any](def Definition, handler Handler[I, O]) *Tool[I, O]
NewTool creates a Tool with an explicit Definition and Handler.
func (*Tool[I, O]) AsCallable ¶
AsCallable converts a typed Tool[I,O] into a Callable by adding JSON marshaling/unmarshaling around the typed handler.
func (*Tool[I, O]) AsProvider ¶
func (t *Tool[I, O]) AsProvider() provider.RequestResponse[I, O]
AsProvider converts a typed Tool into a provider.RequestResponse. This bridges the tool system with the provider middleware stack (resilience, caching, tracing, etc.).
func (*Tool[I, O]) Definition ¶
func (t *Tool[I, O]) Definition() Definition
Definition returns the tool's definition.
func (*Tool[I, O]) WithAnnotations ¶
func (t *Tool[I, O]) WithAnnotations(a Annotations) *Tool[I, O]
WithAnnotations returns a copy of the tool with the given annotations.