# Tool Use — Go For conceptual overview (tool definitions, tool choice, tips), see [shared/tool-use-concepts.md](../../shared/tool-use-concepts.md). ## Tool Use ### Tool Runner (Beta — Recommended) **Beta:** The Go SDK provides `BetaToolRunner` for automatic tool use loops via the `toolrunner` package. ```go import ( "context" "fmt" "log" "github.com/anthropics/anthropic-sdk-go" "github.com/anthropics/anthropic-sdk-go/toolrunner" ) // Define tool input with jsonschema tags for automatic schema generation type GetWeatherInput struct { City string `json:"city" jsonschema:"required,description=The city name"` } // Create a tool with automatic schema generation from struct tags weatherTool, err := toolrunner.NewBetaToolFromJSONSchema( "get_weather", "Get current weather for a city", func(ctx context.Context, input GetWeatherInput) (anthropic.BetaToolResultBlockParamContentUnion, error) { return anthropic.BetaToolResultBlockParamContentUnion{ OfText: &anthropic.BetaTextBlockParam{ Text: fmt.Sprintf("The weather in %s is sunny, 72°F", input.City), }, }, nil }, ) if err != nil { log.Fatal(err) } // Create a tool runner that handles the conversation loop automatically runner := client.Beta.Messages.NewToolRunner( []anthropic.BetaTool{weatherTool}, anthropic.BetaToolRunnerParams{ BetaMessageNewParams: anthropic.BetaMessageNewParams{ Model: anthropic.ModelClaudeOpus4_8, MaxTokens: 16000, Messages: []anthropic.BetaMessageParam{ anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("What's the weather in Paris?")), }, }, MaxIterations: 5, }, ) // Run until Claude produces a final response message, err := runner.RunToCompletion(context.Background()) if err != nil { log.Fatal(err) } // RunToCompletion returns *BetaMessage; content is []BetaContentBlockUnion. // Narrow via AsAny() switch — note the Beta-namespace types (BetaTextBlock, // not TextBlock): for _, block := range message.Content { switch block := block.AsAny().(type) { case anthropic.BetaTextBlock: fmt.Println(block.Text) } } ``` **Key features of the Go tool runner:** - Automatic schema generation from Go structs via `jsonschema` tags - `RunToCompletion()` for simple one-shot usage - `All()` iterator for processing each message in the conversation - `NextMessage()` for step-by-step iteration - Streaming variant via `NewToolRunnerStreaming()` with `AllStreaming()` ### Manual Loop For fine-grained control over the agentic loop, define tools with `ToolParam`, check `StopReason`, execute tools yourself, and feed `tool_result` blocks back. This is the pattern when you need to intercept, validate, or log tool calls. Derived from `anthropic-sdk-go/examples/tools/main.go`. ```go package main import ( "context" "encoding/json" "fmt" "log" "github.com/anthropics/anthropic-sdk-go" ) func main() { client := anthropic.NewClient() // 1. Define tools. ToolParam.InputSchema uses a map, no struct tags needed. addTool := anthropic.ToolParam{ Name: "add", Description: anthropic.String("Add two integers"), InputSchema: anthropic.ToolInputSchemaParam{ Properties: map[string]any{ "a": map[string]any{"type": "integer"}, "b": map[string]any{"type": "integer"}, }, }, } // ToolParam must be wrapped in ToolUnionParam for the Tools slice tools := []anthropic.ToolUnionParam{{OfTool: &addTool}} messages := []anthropic.MessageParam{ anthropic.NewUserMessage(anthropic.NewTextBlock("What is 2 + 3?")), } for { resp, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{ Model: anthropic.ModelClaudeSonnet4_6, MaxTokens: 16000, Messages: messages, Tools: tools, }) if err != nil { log.Fatal(err) } // 2. Append the assistant response to history BEFORE processing tool calls. // resp.ToParam() converts Message → MessageParam in one call. messages = append(messages, resp.ToParam()) // 3. Walk content blocks. ContentBlockUnion is a flattened struct; // use block.AsAny().(type) to switch on the actual variant. toolResults := []anthropic.ContentBlockParamUnion{} for _, block := range resp.Content { switch variant := block.AsAny().(type) { case anthropic.TextBlock: fmt.Println(variant.Text) case anthropic.ToolUseBlock: // 4. Parse the tool input. Use variant.JSON.Input.Raw() to get the // raw JSON — block.Input is json.RawMessage, not the parsed value. var in struct { A int `json:"a"` B int `json:"b"` } if err := json.Unmarshal([]byte(variant.JSON.Input.Raw()), &in); err != nil { log.Fatal(err) } result := fmt.Sprintf("%d", in.A+in.B) // 5. NewToolResultBlock(toolUseID, content, isError) builds the // ContentBlockParamUnion for you. block.ID is the tool_use_id. toolResults = append(toolResults, anthropic.NewToolResultBlock(block.ID, result, false)) } } // 6. Exit when Claude stops asking for tools if resp.StopReason != anthropic.StopReasonToolUse { break } // 7. Tool results go in a user message (variadic: all results in one turn) messages = append(messages, anthropic.NewUserMessage(toolResults...)) } } ``` **Key API surface:** | Symbol | Purpose | |---|---| | `resp.ToParam()` | Convert `Message` response → `MessageParam` for history | | `block.AsAny().(type)` | Type-switch on `ContentBlockUnion` variants | | `variant.JSON.Input.Raw()` | Raw JSON string of tool input (for `json.Unmarshal`) | | `anthropic.NewToolResultBlock(id, content, isError)` | Build `tool_result` block | | `anthropic.NewUserMessage(blocks...)` | Wrap tool results as a user turn | | `anthropic.StopReasonToolUse` | `StopReason` constant to check loop termination | | `anthropic.ToolUnionParam{OfTool: &t}` | Wrap `ToolParam` in the union for `Tools:` | --- ## Anthropic-Defined Tools Version-suffixed struct names with `Param` suffix. `Name`/`Type` are `constant.*` types — zero value marshals correctly, so `{}` works. Wrap in `ToolUnionParam` with the matching `Of*` field. Web search and code execution are server-executed; bash and text editor are client-executed (you handle the `tool_use` locally — see `shared/tool-use-concepts.md`). ```go Tools: []anthropic.ToolUnionParam{ {OfWebSearchTool20260209: &anthropic.WebSearchTool20260209Param{}}, {OfBashTool20250124: &anthropic.ToolBash20250124Param{}}, {OfTextEditor20250728: &anthropic.ToolTextEditor20250728Param{}}, {OfCodeExecutionTool20260120: &anthropic.CodeExecutionTool20260120Param{}}, }, ``` Also available: `WebFetchTool20260209Param`, `ToolSearchToolBm25_20251119Param`, `ToolSearchToolRegex20251119Param`. For the advisor and memory tools, use `BetaAdvisorTool20260301Param` / `BetaMemoryTool20250818Param` in the beta namespace on `client.Beta.Messages.New`. ### Advisor tool (beta) Server-side — no tool_result round-trip. The advisor model must be ≥ the executor (top-level) model; invalid pairs return 400. ```go response, err := client.Beta.Messages.New(ctx, anthropic.BetaMessageNewParams{ Model: anthropic.ModelClaudeSonnet4_6, MaxTokens: 4096, Tools: []anthropic.BetaToolUnionParam{ {OfAdvisorTool20260301: &anthropic.BetaAdvisorTool20260301Param{ Model: anthropic.ModelClaudeOpus4_8, }}, }, Messages: []anthropic.BetaMessageParam{ /* ... */ }, Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaAdvisorTool2026_03_01}, }) ``` ---