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 (2)

Collapse
 
alanmercer profile image
Alan Mercer

This is exactly the kind of infrastructure piece that accelerates the whole AI ecosystem.

The pattern I'm seeing: every major LLM needs language-specific SDKs that feel NATIVE to that language's ecosystem, not just HTTP wrappers.

What makes a great AI SDK vs a mediocre one:

  1. Streaming support as first-class — not an afterthought. .NET devs expect async/await patterns
  2. Type safety — strongly typed request/response models, not dynamic JSON
  3. Tool/function calling built-in — this is where agents live. If your SDK doesn't make tool use elegant, agents won't be built on it
  4. Error handling that matches the framework — .NET has specific patterns for retries, circuit breakers, Polly integration
  5. Testing support — mockable interfaces so you can test agent logic without hitting the API

The fact that this is on NuGet (not just GitHub) is smart — it lowers friction to near-zero for any .NET project to add Claude capabilities.

For anyone building agents: the quality of your underlying SDK determines how much cognitive overhead you spend on plumbing vs actual agent logic. Good SDKs = faster iteration on agent behavior.

Collapse
 
shatru123 profile image
Shatrughna Ambhore

Really appreciate this — especially the way you broke down what makes an SDK “great” vs “mediocre” 🙌

That pattern you mentioned (SDKs needing to feel native to the language) is exactly what pushed me to build this. Using raw HTTP for something as core as AI interactions just didn’t feel right in a .NET ecosystem.

I completely agree on streaming, type safety, and tool calling — those were non-negotiables while designing this. I also tried to align error handling and extensibility with typical .NET patterns so it fits naturally into existing architectures.

The testing point is a big one too — I wanted this to be usable in real-world systems, not just demos.

I’m also experimenting with a “Skills” abstraction to move beyond simple request/response and make AI behavior reusable across applications — still evolving, but that’s the direction I’m excited about.

Would genuinely love your thoughts if you get a chance to try it in a real project 🚀