DEV Community

Cover image for I Built the Missing Claude AI SDK for .NET 8 — And It's Now on NuGet
Shatrughna Ambhore
Shatrughna Ambhore

Posted on

I Built the Missing Claude AI SDK for .NET 8 — And It's Now on NuGet

Every major AI provider has a proper .NET SDK.

OpenAI has one. Azure OpenAI has one. Even smaller providers have community SDKs.

But Anthropic's Claude — arguably one of the most capable AI models available right now — had nothing. Just developers hand-rolling HttpClient wrappers, copy-pasting JSON serialization code, and reinventing retry logic across every project.

So I built ClaudeAI.DotNet — an enterprise-grade .NET 8 SDK that makes Claude feel like a first-class citizen in the .NET ecosystem.

And as of today, it's live on NuGet. 🎉

dotnet add package ClaudeAI.DotNet
Enter fullscreen mode Exit fullscreen mode

🤔 Why Did This Need to Exist?

Here's what integrating Claude looked like before this SDK:

// The painful way — what everyone was doing
var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", apiKey);
client.DefaultRequestHeaders.Add("anthropic-version", "2023-06-01");

var payload = new
{
    model = "claude-sonnet-4-5",
    max_tokens = 1024,
    messages = new[] { new { role = "user", content = prompt } }
};

var json = JsonSerializer.Serialize(payload);
var response = await client.PostAsync(
    "https://api.anthropic.com/v1/messages",
    new StringContent(json, Encoding.UTF8, "application/json"));

var result = await response.Content.ReadAsStringAsync();
// Now parse the JSON manually... 😩
Enter fullscreen mode Exit fullscreen mode

No DI support. No retry logic. No streaming abstractions. No structure whatsoever.

Here's what it looks like with ClaudeAI.DotNet:

// The clean way
builder.Services.AddClaudeAI(options =>
{
    options.ApiKey = config["Claude:ApiKey"];
    options.Model = ClaudeModel.Sonnet;
});

// In your service
public async Task<string> AskAsync(string question)
    => await _claude.SendAsync(question);
Enter fullscreen mode Exit fullscreen mode

🏗️ Architecture

The SDK is built on a clean 8-layer architecture:

ClaudeAI.DotNet/
├── Configuration/     ← ClaudeOptions, ClaudeModel constants
├── Models/            ← Request, Response, Message, StreamChunk DTOs
├── Core/              ← HTTP pipeline, retry logic, SSE streaming
├── Skills/            ← ISkill, BaseSkill, built-in skill library
├── Commands/          ← ClaudeCommand, ClaudeCommandResult (CQRS)
├── Tools/             ← [ClaudeTool] attribute, ToolRegistry
├── Services/          ← IClaudeClient, ClaudeClient, RequestBuilder
└── Extensions/        ← AddClaudeAI(), ClaudeAIBuilder (DI)
Enter fullscreen mode Exit fullscreen mode

Each layer has a single responsibility. The AI layer is completely isolated from your business logic — meaning you could swap Claude for another provider with minimal changes.


🎯 Key Features

1. Fluent DI Registration

One-line setup that integrates naturally with ASP.NET Core:

builder.Services.AddClaudeAI(options =>
{
    options.ApiKey = builder.Configuration["Claude:ApiKey"];
    options.Model = ClaudeModel.Sonnet;   // or ClaudeModel.Opus
    options.MaxTokens = 4096;
    options.SystemPrompt = "You are a helpful assistant.";
})
.WithSkill<SummarizationSkill>()
.WithSkill<CodeReviewSkill>()
.WithTools<MyWeatherTools>();
Enter fullscreen mode Exit fullscreen mode

Or bind directly from appsettings.json:

builder.Services.AddClaudeAI(builder.Configuration);
Enter fullscreen mode Exit fullscreen mode
{
  "Claude": {
    "ApiKey": "your-key-here",
    "Model": "claude-sonnet-4-5",
    "MaxTokens": 4096
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Built-in Skills System

The Skills system is the most novel part of the SDK. A Skill is a reusable module that encapsulates a system prompt and optional prompt transformation — letting you standardize AI behaviour across your application.

// Use built-in skills
var summary  = await _claude.SendWithSkillAsync(longText, new SummarizationSkill());
var review   = await _claude.SendWithSkillAsync(myCode,   new CodeReviewSkill());
var french   = await _claude.SendWithSkillAsync(text,     new TranslationSkill("French"));
var sentiment = await _claude.SendWithSkillAsync(feedback, new SentimentAnalysisSkill());
Enter fullscreen mode Exit fullscreen mode

Built-in skills included:

Skill What it does
SummarizationSkill Concise, structured summaries
CodeReviewSkill Detailed review with ratings
TranslationSkill Translate to any language
SentimentAnalysisSkill Returns structured JSON sentiment
DocumentationSkill Generates XML doc comments

Building a custom skill is trivial:

public class GrammarCorrectionSkill : BaseSkill
{
    public override string Name => "GrammarCorrection";

    public override string GetSystemPrompt() =>
        """
        You are an expert editor. Correct grammar, spelling, and punctuation.
        Preserve the author's voice. Return only the corrected text.
        """;

    public override string TransformPrompt(string userPrompt) =>
        $"Please correct the following:\n\n{userPrompt}";
}

// Register and use it
builder.Services.AddClaudeAI(opt => ...)
    .WithSkill<GrammarCorrectionSkill>();

var corrected = await _claude.SendWithSkillAsync(draftText, new GrammarCorrectionSkill());
Enter fullscreen mode Exit fullscreen mode

3. Real Streaming Support

Full SSE (Server-Sent Events) streaming via IAsyncEnumerable<StreamChunk>:

await foreach (var chunk in _claude.StreamAsync("Write a blog post about .NET 8"))
{
    if (!chunk.IsFinal)
        Console.Write(chunk.Delta);  // tokens arrive in real-time
}
Enter fullscreen mode Exit fullscreen mode

Works perfectly with SignalR for real-time UIs, or just terminal output.


4. Multi-turn Conversations

var history = new List<ClaudeMessage>
{
    ClaudeMessage.User("My name is Shatrughna and I work as a Software Developer."),
    ClaudeMessage.Assistant("Nice to meet you!"),
    ClaudeMessage.User("What kind of engineering challenges might I face?")
};

var reply = await _claude.ChatAsync(history);
Enter fullscreen mode Exit fullscreen mode

5. CQRS Command Pattern

Perfect for teams already using MediatR or clean architecture:

var result = await _claude.ExecuteAsync(new ClaudeCommand
{
    Prompt = "Analyze this customer review...",
    Skill = new SentimentAnalysisSkill(),
    MaxTokensOverride = 256,
    ModelOverride = ClaudeModel.Opus  // use a stronger model just for this command
});

if (result.IsSuccess)
{
    Console.WriteLine(result.Content);
    Console.WriteLine($"Model  : {result.Model}");
    Console.WriteLine($"Tokens : {result.Usage?.TotalTokens}");
    Console.WriteLine($"Skill  : {result.SkillUsed}");
}
else
{
    _logger.LogError("Claude failed: {Error}", result.ErrorMessage);
}
Enter fullscreen mode Exit fullscreen mode

6. Function Calling with [ClaudeTool]

Attribute-based tool registration — Claude can call your methods directly:

public class MyTools
{
    [ClaudeTool("get_stock_price", "Returns the current stock price for a ticker symbol")]
    public async Task<string> GetStockPriceAsync(string ticker)
    {
        var price = await _stockApi.GetPriceAsync(ticker);
        return $"{ticker}: ${price}";
    }

    [ClaudeTool("get_weather", "Returns current weather for a city")]
    public async Task<string> GetWeatherAsync(string city)
    {
        return await _weatherService.GetCurrentAsync(city);
    }
}

// Register
builder.Services.AddClaudeAI(opt => opt.ApiKey = "...")
    .WithTools<MyTools>();
Enter fullscreen mode Exit fullscreen mode

7. Fluent Per-Request Builder

When you want to override behaviour for a single request:

var result = await _claude
    .With()
    .Skill<CodeReviewSkill>()
    .SendAsync(mySourceCode);
Enter fullscreen mode Exit fullscreen mode

🔄 Retry & Resilience

Built-in exponential backoff retry — configured via options:

options.MaxRetries = 3;       // retry up to 3 times
options.TimeoutSeconds = 120; // 2 minute timeout
Enter fullscreen mode Exit fullscreen mode

On transient HTTP failures, the SDK automatically retries with delays of 2s, 4s, 8s before giving up.


✅ Testing

The SDK is designed for testability. IClaudeClient is a proper interface — easy to mock in unit tests:

var mockClaude = new Mock<IClaudeClient>();
mockClaude
    .Setup(x => x.SendAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
    .ReturnsAsync("Mocked response");

var service = new MyService(mockClaude.Object);
var result = await service.ProcessAsync("test input");

result.Should().NotBeNullOrEmpty();
Enter fullscreen mode Exit fullscreen mode

🚀 Getting Started in 2 Minutes

dotnet add package ClaudeAI.DotNet
Enter fullscreen mode Exit fullscreen mode
// Program.cs
builder.Services.AddClaudeAI(opt =>
{
    opt.ApiKey = builder.Configuration["Claude:ApiKey"];
});

// YourService.cs
public class YourService
{
    private readonly IClaudeClient _claude;
    public YourService(IClaudeClient claude) => _claude = claude;

    public async Task<string> SummarizeAsync(string text)
        => await _claude.SendWithSkillAsync(text, new SummarizationSkill());
}
Enter fullscreen mode Exit fullscreen mode

That's literally it.


📦 What's Next

Here's the roadmap I'm working toward:

  • [ ] Agent workflows — multi-step autonomous task execution
  • [ ] Prompt template versioning
  • [ ] RAG (Retrieval-Augmented Generation) skill
  • [ ] Embeddings support
  • [ ] Azure Functions integration
  • [ ] Source generator for automatic tool binding
  • [ ] OpenTelemetry tracing + token usage metrics
  • [ ] ClaudeAI.DotNet.MediatR integration package

🔗 Links


If this SDK saves you time or solves a real problem for you — a ⭐ on GitHub goes a long way for an open source maintainer. And if you find a bug or want a feature, PRs are very welcome!

Happy to answer any questions about the architecture or design decisions in the comments. 👇


Built with ❤️ by Shatrughna | Pune, India 🇮🇳

Top comments (0)