community packages.
dotnet add package Anthropic --version 10.*
Verify installation by checking the project file. Ensure no transitive dependencies pull in Anthropic.SDK or tryAGI.Anthropic unless explicitly required for migration.
Step 2: Client Registration and Configuration Binding
Register the client in the DI container with environment-driven configuration. The SDK automatically resolves ANTHROPIC_API_KEY from the environment, but production systems should bind to a strongly-typed configuration object.
public class ClaudeApiOptions
{
public string ApiKey { get; set; } = string.Empty;
public string DefaultModel { get; set; } = "claude-sonnet-4-20250514";
public int MaxTokens { get; set; } = 4096;
public double Temperature { get; set; } = 0.7;
public int TimeoutSeconds { get; set; } = 30;
}
// Program.cs or Startup.cs
builder.Services.Configure<ClaudeApiOptions>(builder.Configuration.GetSection("Claude"));
builder.Services.AddHttpClient<IClaudeGateway, ClaudeGateway>()
.ConfigureHttpClient((sp, client) =>
{
var opts = sp.GetRequiredService<IOptions<ClaudeApiOptions>>().Value;
client.Timeout = TimeSpan.FromSeconds(opts.TimeoutSeconds);
})
.AddPolicyHandler(Policy<HttpResponseMessage>
.Handle<HttpRequestException>()
.OrResult(msg => !msg.IsSuccessStatusCode)
.WaitAndRetryAsync(3, retry => TimeSpan.FromSeconds(Math.Pow(2, retry))));
Step 3: Gateway Implementation with Streaming Support
Wrap the official client in an abstraction layer to isolate vendor-specific types and enable testing. This gateway handles both synchronous completion and streaming token delivery.
public interface IClaudeGateway
{
Task<string> CompleteAsync(string prompt, CancellationToken ct = default);
IAsyncEnumerable<string> StreamAsync(string prompt, CancellationToken ct = default);
}
public class ClaudeGateway : IClaudeGateway
{
private readonly AnthropicClient _client;
private readonly ClaudeApiOptions _options;
public ClaudeGateway(IOptions<ClaudeApiOptions> options)
{
_options = options.Value;
_client = new AnthropicClient();
}
public async Task<string> CompleteAsync(string prompt, CancellationToken ct = default)
{
var request = new MessageRequest
{
Model = _options.DefaultModel,
MaxTokens = _options.MaxTokens,
Temperature = _options.Temperature,
Messages = new List<MessageEntry>
{
new() { Role = MessageRole.User, Text = prompt }
}
};
var response = await _client.Messages.CreateAsync(request, ct);
return response.Content?.FirstOrDefault()?.Text ?? string.Empty;
}
public async IAsyncEnumerable<string> StreamAsync(string prompt, [EnumeratorCancellation] CancellationToken ct = default)
{
var request = new MessageRequest
{
Model = _options.DefaultModel,
MaxTokens = _options.MaxTokens,
Temperature = _options.Temperature,
Messages = new List<MessageEntry>
{
new() { Role = MessageRole.User, Text = prompt }
}
};
await foreach (var delta in _client.Messages.CreateStreamingAsync(request, ct))
{
if (delta.Content?.FirstOrDefault() is { } chunk && !string.IsNullOrEmpty(chunk.Text))
{
yield return chunk.Text;
}
}
}
}
Architecture Decisions and Rationale
- Abstraction Layer (
IClaudeGateway): Isolates vendor types (AnthropicClient, MessageRequest) behind an application-specific contract. This enables mock testing, future vendor swapping, and centralized telemetry injection without scattering SDK references across business logic.
- Configuration Binding: Maps
appsettings.json to ClaudeApiOptions. This decouples runtime behavior from code, allowing environment-specific tuning (e.g., lower MaxTokens for staging, higher for production) without recompilation.
- Resilience Pipeline: Integrates Polly for exponential backoff. The official SDK handles basic HTTP retries, but production workloads require configurable retry counts, jitter, and circuit breakers to prevent cascade failures during Anthropic API throttling.
- Streaming Abstraction: Returns
IAsyncEnumerable<string> instead of raw SDK delta objects. This normalizes the consumption pattern for UI components or downstream processors, hiding SDK-specific payload structures.
- Cancellation Propagation: All async methods accept
CancellationToken. This prevents orphaned HTTP connections when clients disconnect or timeouts trigger, preserving thread pool resources.
Pitfall Guide
1. Ignoring Beta Status and Breaking Changes
Explanation: The official SDK ships as version 10+ in beta. Minor version updates may introduce breaking changes to request/response models or method signatures.
Fix: Pin the package version in your project file (<PackageReference Include="Anthropic" Version="10.2.0" />). Use a Directory.Packages.props file for centralized version management. Subscribe to the SDK's GitHub releases for migration notes.
2. Hardcoding Credentials or Bypassing Environment Resolution
Explanation: The SDK automatically reads ANTHROPIC_API_KEY from the environment. Hardcoding keys or using custom auth headers bypasses this mechanism and creates secret management debt.
Fix: Rely on the SDK's built-in environment resolution. In containerized or cloud environments, inject the key via secret managers (Azure Key Vault, AWS Secrets Manager) and map it to the ANTHROPIC_API_KEY environment variable at runtime.
3. Blocking Async Calls in Request Pipelines
Explanation: Calling .Result or .Wait() on SDK methods in ASP.NET Core or Blazor contexts causes thread pool starvation and deadlocks, especially under streaming workloads.
Fix: Propagate async/await through the entire call stack. Use IAsyncEnumerable for streaming and await foreach for consumption. Never block on async boundaries in web applications.
4. Mismanaging Token Budgets and Context Windows
Explanation: Claude models enforce strict token limits. Failing to count input tokens or truncate conversation history leads to 400 Bad Request errors or silent truncation.
Fix: Implement a token counter before request submission. Use libraries like Microsoft.ML.Tokenizers or Anthropic's official tokenizer to estimate payload size. Implement dynamic history truncation or chunking strategies for long conversations.
5. Overlooking Streaming Latency vs. Throughput Trade-offs
Explanation: Streaming reduces time-to-first-token (TTFT) but increases connection overhead. Blindly enabling streaming for all endpoints wastes resources on batch or offline processing.
Fix: Route streaming only to latency-sensitive paths (UI chat, real-time dashboards). Use synchronous CompleteAsync for background jobs, ETL pipelines, or batch inference. Configure separate HTTP clients with different timeout and concurrency limits.
6. Namespace Collision During Migration
Explanation: Transitioning from Anthropic.SDK or tryAGI.Anthropic to the official Anthropic package can cause type resolution conflicts if both are referenced transitively.
Fix: Audit dependencies with dotnet list package. Explicitly remove community packages. Use fully qualified type names during migration if temporary coexistence is required. Verify build output for duplicate type warnings.
7. Missing Rate Limit and Backpressure Handling
Explanation: Anthropic enforces request-per-minute and token-per-minute quotas. The SDK does not automatically queue or throttle requests beyond basic HTTP retries.
Fix: Implement a concurrency limiter (e.g., SemaphoreSlim or Channel<T>) for high-throughput services. Monitor x-ratelimit-* response headers and implement adaptive throttling. Use a message queue (RabbitMQ, Azure Service Bus) for bursty workloads.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Real-time chat UI | Official SDK + Streaming | Minimizes TTFT, improves perceived responsiveness | Higher connection overhead, but lower latency cost |
| Batch document processing | Official SDK + Sync + Queue | Prevents thread pool exhaustion, enables retry isolation | Lower compute cost, higher queue infrastructure cost |
| Legacy .NET Framework app | Official SDK (.NET Standard 2.0) | Enables modern AI capabilities without runtime migration | Zero migration cost, full API parity |
| Multi-vendor AI strategy | Abstraction layer (IClaudeGateway) | Decouples business logic from vendor-specific types | Higher initial dev cost, lower long-term lock-in risk |
Configuration Template
// appsettings.Production.json
{
"Claude": {
"ApiKey": "",
"DefaultModel": "claude-sonnet-4-20250514",
"MaxTokens": 4096,
"Temperature": 0.7,
"TimeoutSeconds": 45
},
"Resilience": {
"MaxRetryAttempts": 3,
"BaseDelaySeconds": 2,
"CircuitBreakerThreshold": 5
}
}
// DI Registration (Program.cs)
builder.Services.Configure<ClaudeApiOptions>(builder.Configuration.GetSection("Claude"));
builder.Services.Configure<ResilienceOptions>(builder.Configuration.GetSection("Resilience"));
builder.Services.AddHttpClient<IClaudeGateway, ClaudeGateway>()
.ConfigureHttpClient((sp, client) =>
{
var opts = sp.GetRequiredService<IOptions<ClaudeApiOptions>>().Value;
client.Timeout = TimeSpan.FromSeconds(opts.TimeoutSeconds);
})
.AddPolicyHandler((sp, _) =>
{
var resOpts = sp.GetRequiredService<IOptions<ResilienceOptions>>().Value;
return Policy<HttpResponseMessage>
.Handle<HttpRequestException>()
.OrResult(msg => (int)msg.StatusCode >= 500 || msg.StatusCode == HttpStatusCode.TooManyRequests)
.WaitAndRetryAsync(resOpts.MaxRetryAttempts, retry => TimeSpan.FromSeconds(Math.Pow(resOpts.BaseDelaySeconds, retry)));
});
Quick Start Guide
- Install the package: Run
dotnet add package Anthropic --version 10.* in your project directory.
- Set the environment variable: Export
ANTHROPIC_API_KEY=sk-ant-api03-... in your shell or configure it in your IDE's run configuration.
- Register the client: Add
builder.Services.AddHttpClient<IClaudeGateway, ClaudeGateway>(); to your DI setup.
- Inject and call: Resolve
IClaudeGateway in your service and invoke CompleteAsync("Your prompt here") or StreamAsync("Your prompt here").
- Verify telemetry: Check application logs for request latency and token consumption. Adjust
MaxTokens and TimeoutSeconds based on workload characteristics.