xClaude 0.0.4
dotnet add package xClaude --version 0.0.4
NuGet\Install-Package xClaude -Version 0.0.4
<PackageReference Include="xClaude" Version="0.0.4" />
<PackageVersion Include="xClaude" Version="0.0.4" />
<PackageReference Include="xClaude" />
paket add xClaude --version 0.0.4
#r "nuget: xClaude, 0.0.4"
#:package xClaude@0.0.4
#addin nuget:?package=xClaude&version=0.0.4
#tool nuget:?package=xClaude&version=0.0.4
xClaude
A Claude agent engine for .NET, built on the official Anthropic SDK: a streaming tool-use loop with per-delta events, conversation seeding, tool interception, budget caps, and structured outputs — behind a small, SDK-free API.
- Streaming agent loop —
ClaudeAgentRunner.RunAgentAsyncdrives the full tool-use conversation: stream → dispatch tools → append results → repeat, until a final answer, a terminal tool, a budget cap, or a refusal. - Rich event stream — per-delta text and thinking, per-block messages, tool calls/results, per-turn wire-shape messages for persistence, cumulative usage/cost.
- Your tools, your DI — implement
IAgentTool(name + description + JSON schema + execute), register it, list it in the run's allowed tools. - Pluggable key resolution —
IClaudeApiKeyProviderlets multi-tenant hosts resolve a different API key per run; the default reads configuration/environment. - Conversation seeding — persist each turn's raw content-block JSON (
TurnMessageEvent), feed it back viaAgentRunConfig.Historyto continue faithfully (thinking signatures intact). - Tool interception — substitute a tool result instead of executing (confirmation flows, policy gates, dry runs).
- Structured outputs —
RunStructuredAsync<T>generates a JSON schema fromTand returns a validated, typed payload. - Server tools — Anthropic-hosted web search / web fetch with live progress events.
- Cost control — built-in per-model pricing, running cost events, optional USD budget cap.
Install
dotnet add package xClaude
Quick start
// Program.cs
builder.Services.AddXClaude(builder.Configuration); // reads the "Anthropic" section
builder.Services.AddScoped<IAgentTool, GetWeatherTool>();
// appsettings.json
{
"Anthropic": {
"ApiKey": "sk-ant-…", // or ANTHROPIC_API_KEY, or a custom IClaudeApiKeyProvider
"DefaultModel": "claude-opus-5",
"Effort": "high",
"ShowThinking": true
}
}
public sealed class GetWeatherTool : IAgentTool
{
public string Name => "get_weather";
public string Description => "Get current weather for a location. Call when the user asks about weather.";
public JsonObject InputSchema => ToolSchema.Object(new JsonObject
{
["location"] = ToolSchema.String("City name"),
}, required: ["location"]);
public async Task<ToolExecutionResult> ExecuteAsync(JsonElement input, ToolContext ctx, CancellationToken ct)
{
var location = input.GetProperty("location").GetString();
return new ToolExecutionResult(IsError: false, $"72°F and sunny in {location}");
}
}
var result = await runner.RunAgentAsync(new AgentRunConfig
{
SystemPrompts = ["You are a helpful assistant."],
InitialUserMessage = "What's the weather in Paris?",
AllowedToolNames = ["get_weather"],
EventSink = mySink, // IAgentEventSink — live progress
BudgetCapUsd = 0.50m,
}, new ToolContext { Services = scopedProvider }, cancellationToken);
Console.WriteLine(result.Text); // final answer
Console.WriteLine(result.CostUsd); // what it cost
Events
Implement IAgentEventSink and set AgentRunConfig.EventSink:
| Event | When |
|---|---|
AssistantDeltaEvent |
Each streamed chunk of visible text (live typing) |
ThinkingDeltaEvent |
Each streamed chunk of thinking (requires ShowThinking) |
AssistantMessageEvent |
A text block completed |
ThoughtEvent |
A turn's accumulated thinking |
ToolCallEvent / ToolResultEvent |
Tool dispatched / result ready (client and server tools) |
ToolPendingEvent |
The interceptor substituted a result instead of executing |
TurnMessageEvent |
A conversation message completed, in raw wire shape — persist this |
UsageEvent |
Cumulative tokens + cost after each turn |
AgentErrorEvent |
budget_exceeded, max_turns_reached, refusal |
Multi-turn conversations
Persist TurnMessageEvent.ContentJson per message (and your user messages), then:
var config = new AgentRunConfig
{
History = savedMessages.Select(m => new AgentMessage(m.Role, m.ContentJson)).ToList(),
InitialUserMessage = "And what about tomorrow?",
// …
};
Content-block JSON must be fed back unmodified — thinking signatures are validated by the API.
Multi-tenant / BYOK
// Register BEFORE AddXClaude — the default provider is TryAdd'ed.
builder.Services.AddScoped<IClaudeApiKeyProvider, MyTenantKeyProvider>();
builder.Services.AddXClaude(builder.Configuration);
Structured extraction
var verdict = await runner.RunStructuredAsync<IncidentVerdict>(
system: "You are a security analyst.",
userInput: logExcerpt,
cancellationToken: ct);
Pass history to keep a guaranteed schema and a conversation. It is the same seeding
contract as AgentRunConfig.History — persist TurnMessageEvent.ContentJson verbatim and feed
it back, signatures unmodified — and both entry points seed through one builder, so prior turns
reach the wire identically either way:
var reply = await runner.RunStructuredAsync<NpcReply>(
system: persona,
userInput: playerLine,
history: priorTurns, // oldest first
cancellationToken: ct);
License
MIT — see LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Anthropic (>= 12.39.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.9)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Http (>= 10.0.9)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.9)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.