tool

package module
v0.0.0-...-e209eb6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 5, 2026 License: MIT Imports: 12 Imported by: 0

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

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

func NewContext(ctx context.Context) *Context

NewContext creates a Context from a standard context.Context.

func (*Context) Get

func (c *Context) Get(key string) (any, bool)

Get retrieves a metadata value.

func (*Context) Metadata

func (c *Context) Metadata() map[string]any

Metadata returns a copy of all metadata.

func (*Context) Set

func (c *Context) Set(key string, value any)

Set stores a metadata value.

func (*Context) WithCancel

func (c *Context) WithCancel() (*Context, context.CancelFunc)

WithCancel returns a derived Context with a cancel function.

func (*Context) WithTimeout

func (c *Context) WithTimeout(d time.Duration) (*Context, context.CancelFunc)

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

func ChainFormatters(formatters ...Formatter) Formatter

ChainFormatters applies formatters in sequence.

func SummaryHeaderFormatter

func SummaryHeaderFormatter() Formatter

SummaryHeaderFormatter prepends a one-line summary header to the result.

func TruncateFormatter

func TruncateFormatter(maxLen int) Formatter

TruncateFormatter returns a Formatter that truncates content to maxLen characters.

type FormatterFunc

type FormatterFunc func(toolName string, result *Result) (string, error)

FormatterFunc adapts a function to the Formatter interface.

func (FormatterFunc) Format

func (f FormatterFunc) Format(toolName string, result *Result) (string, error)

type Handler

type Handler[I, O any] interface {
	Execute(ctx context.Context, input I) (O, error)
}

Handler processes tool input and produces output.

type HandlerFunc

type HandlerFunc[I, O any] func(ctx context.Context, input I) (O, error)

HandlerFunc is the function adapter for Handler. It allows using a plain function where a Handler is expected.

func (HandlerFunc[I, O]) Execute

func (f HandlerFunc[I, O]) Execute(ctx context.Context, input I) (O, error)

Execute implements Handler.

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

type MetricEntry struct {
	ToolName string
	Duration time.Duration
	Err      error
}

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

type Middleware func(Callable) Callable

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) Call

func (r *Registry) Call(ctx *Context, name string, input json.RawMessage) (*Result, error)

Call invokes a tool by name with raw JSON input.

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) Get

func (r *Registry) Get(name string) (Callable, bool)

Get retrieves a tool by name.

func (*Registry) Len

func (r *Registry) Len() int

Len returns the number of registered tools.

func (*Registry) List

func (r *Registry) List() []Definition

List returns the definitions of all registered tools.

func (*Registry) Names

func (r *Registry) Names() []string

Names returns the names of all registered tools.

func (*Registry) Register

func (r *Registry) Register(t Callable) error

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 ErrorResult

func ErrorResult(content string) *Result

ErrorResult creates an error Result.

func JSONResult

func JSONResult(v any) (*Result, error)

JSONResult creates a Result from a JSON-serializable value. Both Output (raw JSON) and Content (JSON string) are set.

func MustJSONResult

func MustJSONResult(v any) *Result

MustJSONResult is like JSONResult but panics on error.

func TextResult

func TextResult(content string) *Result

TextResult creates a Result with text content only.

func (*Result) SetMeta

func (r *Result) SetMeta(key string, value any)

SetMeta sets a metadata key-value pair.

func (*Result) Text

func (r *Result) Text() string

Text returns the human-readable content. Falls back to Output if Content is empty.

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

func (t *Tool[I, O]) AsCallable() Callable

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]) Execute

func (t *Tool[I, O]) Execute(ctx context.Context, input I) (O, error)

Execute runs the tool with typed input.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL