Section 1 — Official Anthropic C# SDK Fundamentals

Summary — what this page covers The foundation for everything on Day 2: install and wire up the official Anthropic NuGet package, make single- and multi-turn calls, build a ClaudeService abstraction, manage conversation history, and apply prompt caching and Polly retries. Get to a working API call within the first 20 minutes. Pair with Lab 1.

9:00 – 10:30 AM · 90 min — 45 min lecture/demo + 45 min lab

Learning objectives

  • Install/configure the official Anthropic NuGet package in ASP.NET Core
  • Register AnthropicClient via DI with the options pattern
  • Make async single-turn and multi-turn calls
  • Design a ClaudeService abstraction following .NET best practices
  • Manage conversation history across HTTP requests (IDistributedCache)
  • Apply prompt caching to cut cost on repeated system prompts
  • Handle errors, rate limits, transient failures with Polly
  • Understand IChatClient from Microsoft.Extensions.AI

Content

Block 1A — SDK landscape & setup (≈20 min)

The .NET SDK landscape. Use the official Anthropic package (v12.x) — it's the one Anthropic maintains and the one this workshop targets. Community options exist (Anthropic.SDK, Claudia) and are fine, but the official package is the reference. ⚠️ Watch the name: the old tryAGI.Anthropic v3.x is a different lineage — don't pull it by mistake.

Install & setup. Add the package, store the key in user-secrets (never commit it), bind it with the options pattern, and register the client in DI:

dotnet add package Anthropic
using Anthropic;

// Bind Anthropic:ApiKey from configuration/user-secrets via the options pattern,
// then register the client. AnthropicClient reads ANTHROPIC_API_KEY by default,
// or set it explicitly from your bound options.
builder.Services.AddSingleton(new AnthropicClient {
    ApiKey = builder.Configuration["Anthropic:ApiKey"]
});

Register AnthropicClient as a Singleton. It holds an HttpClient internally — registering it Scoped or Transient creates a new socket per request and leads to socket exhaustion under load. This is the same reason you use IHttpClientFactory elsewhere. Your IClaudeService wrapper can be Scoped; the client underneath is Singleton.

Block 1B — First API calls (≈35 min)

Single-turn message call. Everything goes through client.Messages.Create: pick a model, set a max-tokens ceiling, pass the messages. Read response.Usage for the token counts (and cost):

using Anthropic.Models.Messages;

var response = await client.Messages.Create(new MessageCreateParams {
    Model = Model.ClaudeSonnet4_6,        // default for app features; Haiku for cheap, Opus for hard
    MaxTokens = 1024,
    Messages = [new() { Role = Role.User, Content = "Recommend a book like Dune." }],
});

// ContentBlock is a union — filter to the text blocks
foreach (var text in response.Content.Select(b => b.Value).OfType<TextBlock>())
    Console.WriteLine(text.Text);

Console.WriteLine($"in: {response.Usage.InputTokens}, out: {response.Usage.OutputTokens}");

System prompts & multi-turn. The system prompt is your assistant's constructor — it scopes the assistant to BookTracker once, up front. The API is stateless, so multi-turn means you keep the history: IClaudeService maps your stored history → a message list and appends the new user turn on every call.

Prompt caching. Mark stable, reused content (the system prompt, RAG context, few-shot examples) with CacheControlEphemeral and pay up to ~90% less for the cached input on repeat calls:

System = new List<TextBlockParam> {
    new() { Text = longSystemPrompt, CacheControl = new CacheControlEphemeral() },
},

Cache when the prefix is stable and ≥ ~1,000 tokens and shared across requests. Verify it's working by reading response.Usage.CacheCreationInputTokens (first call) then CacheReadInputTokens (subsequent calls).

Block 1C — Error handling & chat endpoint (≈25 min)

Polly retry. Wrap calls in a retry policy for 429 (rate limit) and 529 (overloaded) — both are transient and safe to retry with exponential backoff. (The SDK also retries 429/5xx automatically with a default of 2 retries; add Polly when you want custom backoff or jitter.)

Wire /api/chat. A Minimal API endpoint that takes a user message + session id, loads history from IDistributedCache, calls IClaudeService, persists the new turn, and returns the reply.

IChatClient from Microsoft.Extensions.AI. The official SDK also plugs into the provider-agnostic IChatClient abstraction — useful if you want to swap providers or use the broader Microsoft.Extensions.AI tooling. We use the native AnthropicClient directly today for full access to caching, tools, and streaming.

Demos referenced here

  • First working call · Multi-turn with persisted history · Prompt-cache token counts (show CacheCreationInputTokens then CacheReadInputTokens). [Scripts in _instructor/demo-plan.md.]

→ Continue to Lab 1.