# Tool Use — C# For conceptual overview (tool definitions, tool choice, tips), see [shared/tool-use-concepts.md](../../shared/tool-use-concepts.md). ## Tool Use ### Defining a tool `Tool` (NOT `ToolParam`) with an `InputSchema` record. `InputSchema.Type` is auto-set to `"object"` by the constructor — don't set it. `ToolUnion` has an implicit conversion from `Tool`, triggered by the collection expression `[...]`. ```csharp using System.Text.Json; using Anthropic.Models.Messages; var parameters = new MessageCreateParams { Model = Model.ClaudeSonnet4_6, MaxTokens = 16000, Tools = [ new Tool { Name = "get_weather", Description = "Get the current weather in a given location", InputSchema = new() { Properties = new Dictionary { ["location"] = JsonSerializer.SerializeToElement( new { type = "string", description = "City name" }), }, Required = ["location"], }, }, ], Messages = [new() { Role = Role.User, Content = "Weather in Paris?" }], }; ``` Derived from `anthropic-sdk-csharp/src/Anthropic/Models/Messages/Tool.cs` and `ToolUnion.cs:799` (implicit conversion). See [shared tool use concepts](../../shared/tool-use-concepts.md) for the loop pattern. ### Converting response content to the follow-up assistant message When echoing Claude's response back in the assistant turn, **there is no `.ToParam()` helper** — manually reconstruct each `ContentBlock` variant as its `*Param` counterpart. Do NOT use `new ContentBlockParam(block.Json)`: it compiles and serializes, but `.Value` stays `null` so `TryPick*`/`Validate()` fail (degraded JSON pass-through, not the typed path). ```csharp using Anthropic.Models.Messages; Message response = await client.Messages.Create(parameters); // No .ToParam() — reconstruct per variant. Implicit conversions from each // *Param type to ContentBlockParam mean no explicit wrapper. List assistantContent = []; List toolResults = []; foreach (ContentBlock block in response.Content) { if (block.TryPickText(out TextBlock? text)) { assistantContent.Add(new TextBlockParam { Text = text.Text }); } else if (block.TryPickThinking(out ThinkingBlock? thinking)) { // Signature MUST be preserved — the API rejects tampering assistantContent.Add(new ThinkingBlockParam { Thinking = thinking.Thinking, Signature = thinking.Signature, }); } else if (block.TryPickRedactedThinking(out RedactedThinkingBlock? redacted)) { assistantContent.Add(new RedactedThinkingBlockParam { Data = redacted.Data }); } else if (block.TryPickToolUse(out ToolUseBlock? toolUse)) { // ToolUseBlock has required Caller; ToolUseBlockParam.Caller is optional — don't copy it assistantContent.Add(new ToolUseBlockParam { ID = toolUse.ID, Name = toolUse.Name, Input = toolUse.Input, }); // Execute the tool; collect ONE result per tool_use block — the API // rejects the follow-up if any tool_use ID lacks a matching tool_result. string result = ExecuteYourTool(toolUse.Name, toolUse.Input); toolResults.Add(new ToolResultBlockParam { ToolUseID = toolUse.ID, Content = result, }); } } // Follow-up: prior messages + assistant echo + user tool_result(s) List followUpMessages = [ .. parameters.Messages, new() { Role = Role.Assistant, Content = assistantContent }, new() { Role = Role.User, Content = toolResults }, ]; ``` `ToolResultBlockParam` has no tuple constructor — use the object initializer. `Content` is a string-or-list union; a plain `string` implicitly converts. --- ## Structured Output ```csharp OutputConfig = new OutputConfig { Format = new JsonOutputFormat { Schema = new Dictionary { ["type"] = JsonSerializer.SerializeToElement("object"), ["properties"] = JsonSerializer.SerializeToElement( new { name = new { type = "string" } }), ["required"] = JsonSerializer.SerializeToElement(new[] { "name" }), }, }, }, ``` `JsonOutputFormat.Type` is auto-set to `"json_schema"` by the constructor. `Schema` is `required`. --- ## Anthropic-Defined Tools Web search, bash, text editor, and code execution are Anthropic-defined tools with built-in schemas. 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`). Type names are version-suffixed; constructors auto-set `name`/`type`. **Wrap each in `new ToolUnion(...)` explicitly.** ```csharp Tools = [ new ToolUnion(new WebSearchTool20260209()), new ToolUnion(new ToolBash20250124()), new ToolUnion(new ToolTextEditor20250728()), new ToolUnion(new CodeExecutionTool20260120()), ], ``` Also available: `new ToolUnion(new WebFetchTool20260209())`, `new ToolUnion(new MemoryTool20250818())`. `WebSearchTool20260209` optionals: `AllowedDomains`, `BlockedDomains`, `MaxUses`, `UserLocation`. --- ## Tool Runner (Beta) The C# SDK provides a `BetaToolRunner` for automatic tool execution loops. Define tools with raw JSON schemas, and the runner handles the API call → tool execution → result feedback loop. ```csharp using Anthropic.Models.Beta.Messages; // Define tools and create params as shown in the Tool Use section above, // but using the beta namespace types (BetaToolUnion, etc.) var runner = client.Beta.Messages.ToolRunner(betaParams); await foreach (BetaMessage message in runner) { foreach (var block in message.Content) { if (block.TryPickText(out var text)) { Console.WriteLine(text.Text); } } } ``` ---