Help us improve
Share bugs, ideas, or general feedback.
From dotnet-blazor
Integrates AI services into .NET apps using Microsoft.Extensions.AI for chat completions, streaming, and function calling; covers Semantic Kernel, embeddings, vector search, and agents.
npx claudepluginhub markus41/claude --plugin dotnet-blazorHow this skill is triggered — by the user, by Claude, or both
Slash command
/dotnet-blazor:dotnet-aiThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
The recommended way to integrate AI in .NET apps. Provides a vendor-agnostic abstraction over AI services.
Building AI/LLM features. Semantic Kernel setup, plugins, prompt templates, memory stores, agents.
Access Azure OpenAI and OpenAI models via .NET SDK for chat completions, embeddings, image generation, audio transcription, and assistants in C# apps.
Provides the Azure OpenAI SDK for .NET to interact with Azure OpenAI and OpenAI services for chat completions, embeddings, image generation, audio transcription, and assistants.
Share bugs, ideas, or general feedback.
The recommended way to integrate AI in .NET apps. Provides a vendor-agnostic abstraction over AI services.
// Install: dotnet add package Microsoft.Extensions.AI
// Provider packages: Microsoft.Extensions.AI.OpenAI, Microsoft.Extensions.AI.AzureAIInference, etc.
using Microsoft.Extensions.AI;
// Register in DI
builder.Services.AddChatClient(new AzureOpenAIClient(
new Uri(builder.Configuration["AI:Endpoint"]!),
new DefaultAzureCredential())
.GetChatClient("gpt-4o"));
// Or with OpenAI directly
builder.Services.AddChatClient(new OpenAIClient(apiKey)
.GetChatClient("gpt-4o"));
public sealed class ChatService(IChatClient chatClient)
{
public async Task<string> AskAsync(string question, CancellationToken ct)
{
var response = await chatClient.GetResponseAsync(question, cancellationToken: ct);
return response.Text;
}
public async Task<string> AskWithContextAsync(string question, string systemPrompt, CancellationToken ct)
{
var messages = new List<ChatMessage>
{
new(ChatRole.System, systemPrompt),
new(ChatRole.User, question)
};
var response = await chatClient.GetResponseAsync(messages, cancellationToken: ct);
return response.Text;
}
// Streaming
public async IAsyncEnumerable<string> StreamAsync(
string prompt, [EnumeratorCancellation] CancellationToken ct = default)
{
await foreach (var update in chatClient.GetStreamingResponseAsync(prompt, cancellationToken: ct))
{
if (update.Text is not null)
yield return update.Text;
}
}
}
using Microsoft.Extensions.AI;
using OpenAI;
// Build client with function invocation middleware
IChatClient client =
new ChatClientBuilder(new OpenAIClient(key).GetChatClient("gpt-4o").AsIChatClient())
.UseFunctionInvocation() // Auto-invokes local functions
.Build();
// Define tools available to the model
var chatOptions = new ChatOptions
{
Tools = [AIFunctionFactory.Create((string location, string unit) =>
{
return "Periods of rain or drizzle, 15 C";
},
"get_current_weather",
"Gets the current weather in a given location")]
};
// Conversation with automatic tool invocation
List<ChatMessage> chatHistory =
[
new(ChatRole.System, "You are a hiking enthusiast who helps discover fun hikes."),
new(ChatRole.User, "I live in Montreal. What's the current weather like?")
];
ChatResponse response = await client.GetResponseAsync(chatHistory, chatOptions);
Console.WriteLine(response.Text); // Model auto-called get_current_weather
// IEmbeddingGenerator<string, Embedding<float>>
builder.Services.AddEmbeddingGenerator(new AzureOpenAIClient(endpoint, credential)
.GetEmbeddingClient("text-embedding-3-small"));
public sealed class SemanticSearchService(IEmbeddingGenerator<string, Embedding<float>> embedder)
{
public async Task<float[]> GetEmbeddingAsync(string text, CancellationToken ct)
{
var embedding = await embedder.GenerateAsync(text, cancellationToken: ct);
return embedding[0].Vector.ToArray();
}
public async Task<IReadOnlyList<float[]>> GetBatchEmbeddingsAsync(
IEnumerable<string> texts, CancellationToken ct)
{
var embeddings = await embedder.GenerateAsync(texts.ToList(), cancellationToken: ct);
return embeddings.Select(e => e.Vector.ToArray()).ToList();
}
}
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
// Build kernel
var kernel = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion("gpt-4o", endpoint, credential)
.Build();
// Simple prompt
var result = await kernel.InvokePromptAsync("Summarize: {{$input}}", new() { ["input"] = text });
// With plugins
kernel.Plugins.AddFromType<TimePlugin>();
kernel.Plugins.AddFromType<WeatherPlugin>();
// Auto function calling
var settings = new OpenAIPromptExecutionSettings { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() };
var chatService = kernel.GetRequiredService<IChatCompletionService>();
var response = await chatService.GetChatMessageContentAsync("What time is it in London?", settings, kernel);
# Requires .NET 10.0 SDK
dotnet new install Microsoft.McpServer.ProjectTemplates
dotnet new mcpserver -n MyMcpServer
// Program.cs
using ModelContextProtocol.Server;
using System.ComponentModel;
var hostBuilder = Host.CreateDefaultBuilder(args)
.ConfigureServices((context, services) =>
{
services.AddMcpServer(options =>
{
options.Name = "SampleMcpServer";
options.Version = "1.0";
})
.WithStdioServerTransport() // or .WithHttpServerTransport()
.AddMcpServerTools();
});
var host = hostBuilder.Build();
await host.RunAsync();
// Tool definitions
public class RandomNumberTools
{
[McpServerTool]
[Description("Gets a random number between min and max")]
public string GetRandomNumber(
[Description("Minimum value")] int min,
[Description("Maximum value")] int max)
{
return $"Your random number is {Random.Shared.Next(min, max + 1)}.";
}
[McpServerTool]
[Description("Describes random weather in the provided city")]
public string GetCityWeather(
[Description("Name of the city")] string city)
{
var weather = Environment.GetEnvironmentVariable("WEATHER_CHOICES") ?? "balmy,rainy,stormy";
var choices = weather.Split(",");
return $"The weather in {city} is {choices[Random.Shared.Next(0, choices.Length)]}.";
}
}
{
"servers": {
"MyMcpServer": {
"type": "stdio",
"command": "dotnet",
"args": ["run", "--project", "<path-to-csproj>"],
"env": { "WEATHER_CHOICES": "sunny,humid,freezing" }
}
}
}
using ModelContextProtocol.Client;
using Microsoft.Extensions.AI;
// Create MCP client connection
var transport = new StdioClientTransport(new()
{
Command = "dotnet run",
Arguments = ["--project", "<path-to-mcp-server>"],
Name = "Minimal MCP Server",
});
McpClient mcpClient = await McpClient.CreateAsync(transport);
// Discover tools
IList<McpClientTool> tools = await mcpClient.ListToolsAsync();
foreach (McpClientTool tool in tools)
Console.WriteLine(tool);
// Integrate MCP tools with chat client
IChatClient chatClient = new ChatClientBuilder(baseClient)
.UseFunctionInvocation()
.Build();
// Use MCP tools in chat
List<ChatMessage> messages = [new(ChatRole.User, "What's the weather in Paris?")];
await foreach (var update in chatClient.GetStreamingResponseAsync(
messages, new() { Tools = [.. tools] }))
{
Console.Write(update);
}
// Using Microsoft.Extensions.VectorData
using Microsoft.Extensions.VectorData;
public sealed class ProductSearchVector
{
[VectorStoreRecordKey]
public int Id { get; set; }
[VectorStoreRecordData]
public string Name { get; set; } = "";
[VectorStoreRecordData]
public string Description { get; set; } = "";
[VectorStoreRecordVector(1536)] // OpenAI embedding dimension
public ReadOnlyMemory<float> Embedding { get; set; }
}
// Search
public sealed class VectorSearchService(
IVectorStore vectorStore,
IEmbeddingGenerator<string, Embedding<float>> embedder)
{
public async Task<IReadOnlyList<ProductSearchVector>> SearchAsync(
string query, int topK = 5, CancellationToken ct = default)
{
var collection = vectorStore.GetCollection<int, ProductSearchVector>("products");
var queryEmbedding = await embedder.GenerateAsync(query, cancellationToken: ct);
var results = await collection.VectorizedSearchAsync(
queryEmbedding[0].Vector, new() { Top = topK }, ct);
return await results.Results.Select(r => r.Record).ToListAsync(ct);
}
}
@page "/ai-chat"
@rendermode InteractiveServer
@inject IChatClient ChatClient
<div class="chat-container">
@foreach (var message in _messages)
{
<div class="message @message.Role">@message.Content</div>
}
@if (_isStreaming)
{
<div class="message assistant">@_streamingText</div>
}
</div>
<EditForm Model="@_input" OnValidSubmit="SendMessage">
<InputText @bind-Value="_input.Text" placeholder="Ask anything..." />
<button type="submit" disabled="@_isStreaming">Send</button>
</EditForm>
@code {
private readonly List<(string Role, string Content)> _messages = [];
private ChatInput _input = new();
private bool _isStreaming;
private string _streamingText = "";
private async Task SendMessage()
{
var userMessage = _input.Text;
_messages.Add(("user", userMessage));
_input = new();
_isStreaming = true;
_streamingText = "";
await foreach (var chunk in ChatClient.GetStreamingResponseAsync(userMessage))
{
if (chunk.Text is not null)
{
_streamingText += chunk.Text;
StateHasChanged();
}
}
_messages.Add(("assistant", _streamingText));
_isStreaming = false;
}
private sealed class ChatInput { public string Text { get; set; } = ""; }
}
using Microsoft.ML.Tokenizers;
// Tiktoken for GPT-4o (requires Microsoft.ML.Tokenizers.Data.O200kBase)
Tokenizer tokenizer = TiktokenTokenizer.CreateForModel("gpt-4o");
string text = "Hello, how are you?";
int tokenCount = tokenizer.CountTokens(text);
// Encode to token IDs
IReadOnlyList<int> ids = tokenizer.EncodeToIds(text);
// Decode back
string decoded = tokenizer.Decode(ids);
// Trim to max tokens
int maxTokens = 100;
int lastIndex = tokenizer.GetIndexByTokenCount(text, maxTokens, out string? normalizedText, out int count);
string trimmed = text[..lastIndex];
| Package | Purpose | Notes |
|---|---|---|
Microsoft.Extensions.AI.Abstractions | Core interfaces (IChatClient, IEmbeddingGenerator) | Base for all providers |
Microsoft.Extensions.AI | Full library + middleware (caching, telemetry) | Includes Abstractions |
Microsoft.Extensions.AI.OpenAI | OpenAI/Azure OpenAI provider | --prerelease required |
Microsoft.Extensions.VectorData.Abstractions | Vector store interfaces (CRUD, search) | Interface definitions |
Microsoft.SemanticKernel.Connectors.InMemory | In-memory vector store | --prerelease required |
ModelContextProtocol | Official MCP C# SDK | --prerelease, requires .NET 10 |
Microsoft.ML.Tokenizers | Tokenization (Tiktoken, BPE, Llama) | Stable, .NET Standard 2.0+ |
Microsoft.ML.Tokenizers.Data.O200kBase | Tiktoken vocab for GPT-4/5 | Required for Tiktoken |
Azure.AI.OpenAI | Azure OpenAI SDK | Official Azure package |
Azure.Identity | Entra ID auth (DefaultAzureCredential) | For Azure services |