C# and .NET provide robust frameworks for building enterprise-grade AI applications. This guide demonstrates integrating Claude in Azure AI Foundry using both the Anthropic C# SDK and compatibility patterns with Microsoft.Extensions.AI, enabling type-safe, production-ready implementations that leverage .NET’s extensive ecosystem.
While official Microsoft.Extensions.AI support for Claude is in development, this guide provides practical patterns for immediate implementation, including direct SDK usage and adapter patterns for unified IChatClient abstractions.
Project Setup
# Create new console application
dotnet new console -n ClaudeAzureApp
cd ClaudeAzureApp
# Add required packages
dotnet add package Anthropic.SDK
dotnet add package Azure.Identity
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.Json
dotnet add package Microsoft.Extensions.Configuration.EnvironmentVariablesConfiguration
Create appsettings.json:
{
"Azure": {
"FoundryResource": "your-resource-name",
"FoundryBaseUrl": "https://your-resource-name.services.ai.azure.com/anthropic",
"ApiKey": ""
},
"Claude": {
"DefaultModel": "claude-sonnet-4-5",
"MaxTokens": 4096
}
}Basic Implementation with Anthropic SDK
using Anthropic;
using Azure.Identity;
using Microsoft.Extensions.Configuration;
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables()
.Build();
// Option 1: API Key Authentication
var client = new AnthropicClient(
apiKey: config["Azure:ApiKey"],
baseUrl: config["Azure:FoundryBaseUrl"]
);
// Option 2: Entra ID Authentication
var credential = new DefaultAzureCredential();
var token = await credential.GetTokenAsync(
new Azure.Core.TokenRequestContext(
new[] { "https://ai.azure.com/.default" }
)
);
var clientWithEntraId = new AnthropicClient(
apiKey: token.Token, // Pass token as API key
baseUrl: config["Azure:FoundryBaseUrl"]
);
// Simple chat
var response = await client.Messages.CreateAsync(
model: config["Claude:DefaultModel"],
maxTokens: int.Parse(config["Claude:MaxTokens"] ?? "1024"),
messages: new[]
{
new Message
{
Role = "user",
Content = "Explain C# async/await in 3 sentences."
}
}
);
Console.WriteLine(response.Content.First().Text);Strongly-Typed Configuration
public class AzureConfig
{
public string FoundryResource { get; set; } = string.Empty;
public string FoundryBaseUrl { get; set; } = string.Empty;
public string ApiKey { get; set; } = string.Empty;
}
public class ClaudeConfig
{
public string DefaultModel { get; set; } = "claude-sonnet-4-5";
public int MaxTokens { get; set; } = 4096;
}
// Usage
var azureConfig = config.GetSection("Azure").Get();
var claudeConfig = config.GetSection("Claude").Get(); Conversation Manager
public class ConversationManager : IDisposable
{
private readonly AnthropicClient _client;
private readonly List _messages = new();
private readonly string _model;
private readonly int _maxTokens;
private readonly string? _systemPrompt;
public ConversationManager(
AnthropicClient client,
string model,
int maxTokens = 2048,
string? systemPrompt = null)
{
_client = client;
_model = model;
_maxTokens = maxTokens;
_systemPrompt = systemPrompt;
}
public async Task SendMessageAsync(string userMessage)
{
_messages.Add(new Message
{
Role = "user",
Content = userMessage
});
var response = await _client.Messages.CreateAsync(
model: _model,
maxTokens: _maxTokens,
system: _systemPrompt,
messages: _messages.ToArray()
);
var assistantMessage = response.Content.First().Text;
_messages.Add(new Message
{
Role = "assistant",
Content = assistantMessage
});
return assistantMessage;
}
public void ClearHistory() => _messages.Clear();
public void Dispose() { }
} Streaming Implementation
public async Task StreamChatAsync(string userMessage)
{
await foreach (var streamEvent in _client.Messages.StreamAsync(
model: _model,
maxTokens: _maxTokens,
messages: new[]
{
new Message { Role = "user", Content = userMessage }
}))
{
if (streamEvent is ContentBlockDelta delta &&
delta.Delta is TextDelta textDelta)
{
Console.Write(textDelta.Text);
}
}
Console.WriteLine();
}Production Application
using Anthropic;
using Azure.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
public class ClaudeApp : IAsyncDisposable
{
private readonly ILogger _logger;
private readonly AnthropicClient _client;
private readonly ClaudeConfig _config;
public ClaudeApp(
IConfiguration configuration,
ILogger logger)
{
_logger = logger;
_config = configuration
.GetSection("Claude")
.Get() ?? new();
var azureConfig = configuration
.GetSection("Azure")
.Get() ?? new();
_client = CreateClient(azureConfig);
}
private AnthropicClient CreateClient(AzureConfig config)
{
if (!string.IsNullOrEmpty(config.ApiKey))
{
return new AnthropicClient(
apiKey: config.ApiKey,
baseUrl: config.FoundryBaseUrl
);
}
var credential = new DefaultAzureCredential();
var token = credential.GetToken(
new Azure.Core.TokenRequestContext(
new[] { "https://ai.azure.com/.default" }
)
);
return new AnthropicClient(
apiKey: token.Token,
baseUrl: config.FoundryBaseUrl
);
}
public async Task ChatAsync(string message)
{
try
{
_logger.LogInformation("Sending message to Claude");
var response = await _client.Messages.CreateAsync(
model: _config.DefaultModel,
maxTokens: _config.MaxTokens,
messages: new[]
{
new Message { Role = "user", Content = message }
}
);
var result = response.Content.First().Text;
_logger.LogInformation(
"Received response. Tokens used: {InputTokens} in, {OutputTokens} out",
response.Usage.InputTokens,
response.Usage.OutputTokens
);
return result;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error calling Claude API");
throw;
}
}
public ValueTask DisposeAsync()
{
return ValueTask.CompletedTask;
}
}
// Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((context, services) =>
{
services.AddSingleton();
})
.Build();
var app = host.Services.GetRequiredService();
var result = await app.ChatAsync("What is dependency injection in C#?");
Console.WriteLine(result);
await host.StopAsync(); Conclusion
This guide covered C# implementation with Claude in Azure AI Foundry using the Anthropic SDK, configuration management, conversation handling, streaming, and production patterns. While official Microsoft.Extensions.AI support is in development, these patterns provide immediate production capability.
Part 6 will cover Claude Code integration with Azure DevOps for automated workflows and intelligent agent development.
