dotnet-ai

Installation
SKILL.md

.NET AI Integration

Microsoft.Extensions.AI (Unified AI Abstraction)

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"));

Chat Completion

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;
        }
    }
}

Function Calling (Tool Use) - from official docs

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

Embeddings

// 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();
    }
}

Semantic Kernel (AI Orchestration)

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);

MCP (Model Context Protocol) in .NET - from official docs

Build MCP Server

# 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)]}.";
    }
}

MCP Server Config (.vscode/mcp.json)

{
  "servers": {
    "MyMcpServer": {
      "type": "stdio",
      "command": "dotnet",
      "args": ["run", "--project", "<path-to-csproj>"],
      "env": { "WEATHER_CHOICES": "sunny,humid,freezing" }
    }
  }
}

Build MCP Client

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);
}

Vector Search

// 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);
    }
}

AI Integration in Blazor

@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; } = ""; }
}

Tokenization (from official docs)

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];

Key Packages (verified from official docs)

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

Reference

Related skills
Installs
8
GitHub Stars
11
First Seen
Apr 4, 2026