tekspry/DotnetFastMCP

A lightweight .NET framework for building Model Context Protocol (MCP) servers. Integrates seamlessly with Azure AD, AWS Cognito, Auth0, OpenAI and other providers

See the code

README

DotnetFastMCP — Enterprise Security & Governance Gateway for MCP Servers

.NET 8.0 .NET 10.0 NuGet License GitHub

Enterprise security, governance, and observability layer for Model Context Protocol (MCP) servers in .NET — OAuth 2.0/OIDC authentication, per-tool MFA enforcement, OpenTelemetry instrumentation, and zero-config health checks. Built on ASP.NET Core.

🎯 Overview

DotnetFastMCP adds enterprise-grade security, governance, and observability to your MCP servers. While the core protocol is simple, running MCP tools in production requires OAuth 2.0/OIDC authentication, per-tool MFA enforcement, distributed tracing, and health monitoring — none of which the base protocol provides. DotnetFastMCP handles all of this with a clean attribute-based API on ASP.NET Core, plus a native .NET client library for consuming MCP servers.

⭐ Key Features

⚡ Zero-Boilerplate MCP Servers (NEW! v2.1.0)

  • ✅ Automatic DI Registration - Non-static tool, resource, and prompt classes scanned via WithComponentsFrom() are automatically registered as Transient services in the DI container. Zero manual builder.Services.AddTransient<T>() boilerplate.
  • ✅ Preserves Custom Lifetimes - Built on TryAddTransient semantics to honor custom Singleton or Scoped registrations without collision.
  • ✅ [McpDescription] Parameter Attributes - Annotate method parameters with rich descriptions emitted directly into JSON Schema inputSchema (tools/list), significantly enhancing LLM tool-calling accuracy.
  • ✅ Smart Schema Filtering - Automatically hides framework-injected types (McpContext, CancellationToken, ClaimsPrincipal, IMcpSession) from schema exposure so LLMs only see valid user inputs.

🚀 .NET 10 LTS & .NET 8 LTS Dual Support (v2.0.0)

  • ✅ Dual-Targeting - Ships both net8.0 and net10.0 binaries in a single package
  • ✅ Zero Breaking Changes - 100% backward compatible for existing .NET 8 applications
  • ✅ Modern Non-Blocking Async Streams - High-performance SSE parsing compliant with .NET 10 CA2024 rules
  • ✅ Comprehensive Test Matrix - Dual-targeted unit & in-memory integration tests covering positive & negative scenarios

Core Framework

  • ✅ Simple Attribute-Based API - Declare tools and resources with [McpTool] and [McpResource] attributes
  • ✅ First-Class Prompts Support - Define prompts with [McpPrompt] for LLM interaction templates
  • ✅ Automatic Component Discovery - Reflection-based scanning of assemblies
  • ✅ JSON-RPC 2.0 Compliant - Full protocol compliance with proper error handling
  • ✅ Flexible Parameter Binding - Supports both array and named parameters
  • ✅ Built on ASP.NET Core - Leverage the powerful ASP.NET Core hosting model
  • ✅ Production Ready - Comprehensive error handling and logging
  • ✅ Type-Safe - Full C# type system integration

🔐 Enterprise Authentication

  • ✅ 6 OAuth Providers Supported - Azure AD, Google, GitHub, Auth0, Okta, AWS Cognito
  • ✅ OAuth Proxy Built-In - Automatic Dynamic Client Registration (DCR) for non-DCR providers
  • ✅ JWT Token Verification - Automatic token validation with JWKS caching
  • ✅ Zero Configuration - Set environment variables and go
  • ✅ Sensible Defaults - Pre-configured scopes for common use cases
  • ✅ Fine-Grained Authorization - Protect tools with [Authorize] attribute
  • ✅ Claims-Based Access - Access user information from authenticated requests
  • ✅ MFA Support - Enforce Multi-Factor Authentication for sensitive tools

🔌 Native Client Library

  • ✅ McpClient - Type-safe .NET client for consuming any MCP server
  • ✅ Transport Agnostic - Support for both Stdio and SSE connections
  • ✅ Notification Handling - Events for real-time logs and progress
  • ✅ Tool Invocation - Clean CallToolAsync<T> API

🤖 LLM Integration

  • ✅ 8 LLM Providers - Ollama, OpenAI, Azure OpenAI, Anthropic Claude, Google Gemini, Cohere, Hugging Face, Deepseek
  • ✅ Latest Models (Feb 2026) - Claude Opus 4.6, Gemini 3 Pro/Flash, Command A, DeepSeek V3.2
  • ✅ Unified Interface - Single ILLMProvider API for all providers
  • ✅ Streaming Support - Real-time token streaming with IAsyncEnumerable<string>
  • ✅ Production-Ready - HttpClientFactory, Polly retry policies, connection pooling
  • ✅ Plug-and-Play - Simple extension methods: builder.AddAnthropicProvider()

📡 Observability

  • ✅ OpenTelemetry Integration - First-class metrics and distributed tracing
  • ✅ 5 Auto-Tracked Metrics - Tool invocations, duration, errors, prompt requests, resource reads
  • ✅ One-Line Setup - builder.WithTelemetry() — zero boilerplate
  • ✅ Exporter Agnostic - Plug in Prometheus, Application Insights, Grafana, Jaeger, or any OTLP backend
  • ✅ OTel Semantic Conventions - Standard tag names, exception events, span status
  • ✅ Zero Overhead When Disabled - Fully opt-in, no performance cost if unused
  • ✅ Stdio + HTTP - Metrics work across both transports

🏥 Health Checks & Diagnostics

  • ✅ Built-In Health Endpoint - GET /mcp/health exposed automatically
  • ✅ One-Line Setup - builder.WithHealthChecks() — no configuration required
  • ✅ Plug-In Custom Checks - Add any check as a simple lambda (no interfaces needed)
  • ✅ Parallel Execution - All checks run concurrently with per-check timeout
  • ✅ Standard HTTP Status Codes - 200 Healthy / 207 Degraded / 503 Unhealthy
  • ✅ Kubernetes & Docker Ready - Drop-in for liveness/readiness probes
  • ✅ Auto Server Diagnostics - Tool count, uptime, framework version included
  • ✅ Zero Overhead When Disabled - Fully opt-in, endpoint not registered unless configured

🚀 Quick Start

Installation

Install via NuGet Package Manager:

dotnet add package DotnetFastMCP --version 2.1.1

Or clone the repository:

git clone https://github.com/tekspry/.NetFastMCP.git
cd DotnetFastMCP
dotnet build -c Release

Create Your First MCP Server

1. Define Your Tools

Tools can be written as instance classes with constructor dependency injection (auto-registered!) or static methods:

using FastMCP.Attributes;
using Microsoft.Extensions.Logging;

// Instance-based tool with constructor injection (automatically registered into DI via WithComponentsFrom!)
public class CalculatorTools
{
    private readonly ILogger<CalculatorTools> _logger;

    public CalculatorTools(ILogger<CalculatorTools> logger)
    {
        _logger = logger;
    }

    [McpTool(Description = "Performs mathematical addition")]
    public int Add(
        [McpDescription("The first number to add")] int a,
        [McpDescription("The second number to add")] int b)
    {
        _logger.LogInformation("Adding {A} + {B}", a, b);
        return a + b;
    }
}

// Static tools are also supported out of the box
public static class EchoTools
{
    [McpTool(Description = "Returns an echo of the input message")]
    public static string Echo(
        [McpDescription("Text message to echo back")] string message) => message;
}

2. Create Program.cs

using FastMCP.Hosting;
using FastMCP.Server;
using System.Reflection;

var server = new FastMCPServer("MyMcpServer");
var builder = McpServerBuilder.Create(server, args);
builder.WithComponentsFrom(Assembly.GetExecutingAssembly());

var app = builder.Build();
await app.RunMcpAsync(args);

Running the Example Server

cd examples/BasicServer
dotnet run

The server will start on http://localhost:5000.

🏆 Built With DotnetFastMCP

Real-world enterprise projects that demonstrate DotnetFastMCP in production:

👗 Fashion Accessory AI Marketing Pipeline

GitHub .NET 10.0 Google A2A

An enterprise-grade, distributed multimodal AI pipeline on .NET 10 LTS that automates the transformation of raw fashion accessory photographs into commercial marketing visuals and video content.

Architecture highlights:

  • 🏗️ Two-Dimensional AI Architecture — DotnetFastMCP (vertical MCP tool layer) + Google A2A Protocol (horizontal agent communication)
  • 🤖 5 DotnetFastMCP Servers — VisionMcpServer, PromptMcpServer, ImageMcpServer, InpaintingMcpServer, VideoMcpServer
  • 🎨 Multimodal AI — Gemini 3.1 Flash Image for dual-conditioning image synthesis, Kling AI for video generation
  • 🛡️ Multi-Tenant SaaS — Entity Framework Core global query filters with tenant isolation
  • ⏱️ Async Background Jobs — Hangfire with exponential backoff and rate-limit protection
OrchestratorAgent (A2A)
    ├── VisionAgent → VisionMcpServer  :5100  (extract_accessory_features)
    ├── CreativeAgent → PromptMcpServer :5200  (generate_image_prompts)
    ├── ImageAgent → ImageMcpServer    :5300  (generate_accessory_image)
    ├── InpaintingAgent → InpaintingMcpServer :5500  (inpaint_accessory)
    └── VideoAgent → VideoMcpServer    :5400  (generate_accessory_video)

🔗 View Repository →


📚 Architecture

Core Components

DotnetFastMCP/
├── src/
│   ├── FastMCP/
│   │   ├── Attributes/          # Component declaration attributes
│   │   ├── Client/              # 🔌 Client library implementation
│   │   ├── Hosting/             # Server hosting and middleware
│   │   ├── Protocol/            # JSON-RPC protocol implementation
│   │   ├── Server/              # FastMCPServer core class
│   │   └── FastMCP.csproj
│   └── FastMCP.CLI/             # Command-line utilities
├── examples/
│   └── BasicServer/             # Example MCP server implementation
├── tests/
│   └── McpIntegrationTest/      # Integration tests
├── LAUNCH_TESTS.ps1             # PowerShell test suite launcher
└── RUN_AND_TEST.ps1             # PowerShell integration test script

Project Structure

ProjectPurpose
FastMCPCore framework library
FastMCP.CLICommand-line interface tools
BasicServerExample MCP server implementation
McpIntegrationTestIntegration tests
ClientDemoExample Client consuming BasicServer

🔧 Creating an MCP Server

1. Define Components

For better organization, split your components into multiple files (e.g., Tools.cs, Resources.cs). The framework will discover them automatically.

File: Tools.cs

using FastMCP.Attributes;
using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;

public static class MyTools
{
    /// <summary>
    /// Public tool - no authentication required
    /// </summary>
    [McpTool]
    public static int Add(int a, int b) => a + b;

public static class Resources
{
    /// <summary>
    /// Protected tool - requires authentication
    /// </summary>
    [McpTool]
    [Authorize]
    public static object GetUserProfile(ClaimsPrincipal user)
    {
        return new
        {
            Name = user.Identity?.Name,
            Email = user.FindFirst("email")?.Value,
            IsAuthenticated = user.Identity?.IsAuthenticated
        };
    }
}

2. Configure Server with Authentication

using FastMCP.Hosting;
using FastMCP.Server;
using System.Reflection;

var mcpServer = new FastMCPServer(name: "My Secure MCP Server");
var builder = McpServerBuilder.Create(mcpServer, args);

// Add authentication (choose your provider)
builder.AddAzureAdTokenVerifier();  // or AddGoogleTokenVerifier(), AddGitHubTokenVerifier(), etc.

// Register tools
builder.WithComponentsFrom(Assembly.GetExecutingAssembly());

var app = builder.Build();
app.Urls.Add("http://localhost:5002");
await app.RunAsync();

3. Set Environment Variables

# Windows PowerShell
$env:FASTMCP_SERVER_AUTH_AZUREAD_TENANT_ID="your-tenant-id"
$env:FASTMCP_SERVER_AUTH_AZUREAD_CLIENT_ID="your-client-id"
$env:FASTMCP_SERVER_AUTH_AZUREAD_CLIENT_SECRET="your-client-secret"
# Linux/Mac
export FASTMCP_SERVER_AUTH_AZUREAD_TENANT_ID="your-tenant-id"
export FASTMCP_SERVER_AUTH_AZUREAD_CLIENT_ID="your-client-id"
export FASTMCP_SERVER_AUTH_AZUREAD_CLIENT_SECRET="your-client-secret"

4. Run and Test

dotnet run

Your server is now running with OAuth Proxy endpoints:

  • MCP endpoint: http://localhost:5002/mcp
  • OAuth authorization: http://localhost:5002/oauth/authorize
  • OAuth token: http://localhost:5002/oauth/token
  • Discovery: http://localhost:5002/.well-known/oauth-authorization-server

Stdio Mode

You can also run the server in Stdio mode (for local LLM clients):

dotnet run -- --stdio

Create an MCP Client

Connect to any MCP server using the C# Client Library:

using FastMCP.Client;
using FastMCP.Client.Transports;

// 1. Connect (via Stdio or SSE)
var transport = new StdioClientTransport("dotnet", "run --project examples/BasicServer -- --stdio");
await using var client = new McpClient(transport);
await client.ConnectAsync();

// 2. List & Call Tools
var tools = await client.ListToolsAsync();
var result = await client.CallToolAsync<int>("add_numbers", new { a = 10, b = 20 });

🔐 Authentication Providers

DotnetFastMCP supports 6 enterprise-grade OAuth providers out of the box:

ProviderMethodUse CaseDefault Scopes
Azure ADAddAzureAdTokenVerifier()Enterprise apps, Microsoft 365openid, profile, email, offline_access
GoogleAddGoogleTokenVerifier()Consumer apps, Google Workspaceopenid, profile, email, userinfo.profile
GitHubAddGitHubTokenVerifier()Developer tools, repositoriesread:user, user:email
Auth0AddAuth0TokenVerifier()Multi-tenant SaaS, custom identityopenid, profile, email, offline_access
OktaAddOktaTokenVerifier()Enterprise SSO, workforce identityopenid, profile, email, offline_access
AWS CognitoAddAwsCognitoTokenVerifier()AWS-native apps, user poolsopenid, profile, email

Quick Setup Examples

Azure AD
builder.AddAzureAdTokenVerifier();

Environment Variables:

FASTMCP_SERVER_AUTH_AZUREAD_TENANT_ID=your-tenant-id
FASTMCP_SERVER_AUTH_AZUREAD_CLIENT_ID=your-client-id
FASTMCP_SERVER_AUTH_AZUREAD_CLIENT_SECRET=your-client-secret

Example: examples/Auth/AzureAdOAuth

Google
builder.AddGoogleTokenVerifier();

Environment Variables:

FASTMCP_SERVER_AUTH_GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
FASTMCP_SERVER_AUTH_GOOGLE_CLIENT_SECRET=your-client-secret

Example: examples/Auth/GoogleOAuth

GitHub
builder.AddGitHubTokenVerifier();

Environment Variables:

FASTMCP_SERVER_AUTH_GITHUB_CLIENT_ID=your-github-client-id
FASTMCP_SERVER_AUTH_GITHUB_CLIENT_SECRET=your-github-client-secret

Example: examples/Auth/GitHubOAuth

### PowerShell Integration Test Suite

The project includes a comprehensive PowerShell-based integration test suite that validates a running server end-to-end.

  1. Publish the server (from the root of the DotnetFastMCP project):

    dotnet publish -c Release -o ..\publish examples\BasicServer
    
  2. Run the tests: Open a PowerShell terminal and run the launcher script from the project root:

    .\LAUNCH_TESTS.ps1
    

This will open a new window, start the BasicServer, and run a series of tests covering all tools and resources, including error handling.

Example Manual Test

Auth0
builder.AddAuth0TokenVerifier();

Environment Variables:

FASTMCP_SERVER_AUTH_AUTH0_DOMAIN=your-tenant.auth0.com
FASTMCP_SERVER_AUTH_AUTH0_AUDIENCE=https://your-api-identifier
FASTMCP_SERVER_AUTH_AUTH0_CLIENT_ID=your-client-id
FASTMCP_SERVER_AUTH_AUTH0_CLIENT_SECRET=your-client-secret

Example: examples/Auth/Auth0OAuth

Okta
builder.AddOktaTokenVerifier();

Environment Variables:

FASTMCP_SERVER_AUTH_OKTA_DOMAIN=dev-123456.okta.com
FASTMCP_SERVER_AUTH_OKTA_AUDIENCE=api://default
FASTMCP_SERVER_AUTH_OKTA_CLIENT_ID=your-client-id
FASTMCP_SERVER_AUTH_OKTA_CLIENT_SECRET=your-client-secret

Example: examples/Auth/OktaOAuth

AWS Cognito
builder.AddAwsCognitoTokenVerifier();

Environment Variables:

FASTMCP_SERVER_AUTH_AWSCOGNITO_USER_POOL_ID=us-east-1_XXXXXXXXX
FASTMCP_SERVER_AUTH_AWSCOGNITO_REGION=us-east-1
FASTMCP_SERVER_AUTH_AWSCOGNITO_CLIENT_ID=your-app-client-id
FASTMCP_SERVER_AUTH_AWSCOGNITO_CLIENT_SECRET=your-app-client-secret
FASTMCP_SERVER_AUTH_AWSCOGNITO_DOMAIN=myapp.auth.us-east-1.amazoncognito.com

Example: examples/Auth/AwsCognitoOAuth

📚 Architecture

Project Structure

DotnetFastMCP/
├── src/
│   └── FastMCP/
│       ├── Attributes/              # Component declaration attributes
│       ├── Authentication/          # 🔐 OAuth providers & token verification
│       │   ├── Providers/          # Azure AD, Google, GitHub, Auth0, Okta, AWS
│       │   ├── Proxy/              # OAuth Proxy for DCR
│       │   └── Verification/       # JWT token validation
│       ├── Hosting/                 # Server hosting and middleware
│       ├── Protocol/                # JSON-RPC protocol implementation
│       └── Server/                  # FastMCPServer core class
├── examples/
│   ├── BasicServer/                 # Simple MCP server
│   └── Auth/                        # 🔐 Authentication examples
│       ├── AzureAdOAuth/           # Azure AD example
│       ├── GoogleOAuth/            # Google OAuth example
│       ├── GitHubOAuth/            # GitHub OAuth example
│       ├── Auth0OAuth/             # Auth0 example
│       ├── OktaOAuth/              # Okta example
│       └── AwsCognitoOAuth/        # AWS Cognito example
└── tests/
    └── McpIntegrationTest/          # Integration tests

Project Structure (Client)

The FastMCP framework now includes a complete client implementation in src/FastMCP/Client.

graph TD
    App[Your App] -->|Uses| Client[McpClient]
    Client -->|IClientTransport| Trans[Transport Layer]
    Trans -->|Stdio| Local[Local Process]
    Trans -->|SSE/HTTP| Remote[Remote Server]

Authentication Flow

sequenceDiagram
    participant Client
    participant MCP Server
    participant OAuth Provider
    
    Client->>MCP Server: Request with Bearer Token
    MCP Server->>Token Verifier: Validate Token
    Token Verifier->>OAuth Provider: Fetch JWKS (if needed)
    OAuth Provider-->>Token Verifier: Public Keys
    Token Verifier-->>MCP Server: Validated Claims
    MCP Server-->>Client: Protected Resource

🔧 Creating an MCP Server

Basic Server (No Authentication)

using FastMCP.Hosting;
using FastMCP.Server;
using System.Reflection;

var mcpServer = new FastMCPServer(name: "My MCP Server");
var builder = McpServerBuilder.Create(mcpServer, args);

builder.WithComponentsFrom(Assembly.GetExecutingAssembly());

var app = builder.Build();
await app.RunAsync();

Secure Server (With Authentication)

using FastMCP.Hosting;
using FastMCP.Server;
using System.Reflection;

var mcpServer = new FastMCPServer(name: "My Secure MCP Server");
var builder = McpServerBuilder.Create(mcpServer, args);

// Add authentication - automatically configures OAuth Proxy
builder.AddAzureAdTokenVerifier();  // or any other provider

builder.WithComponentsFrom(Assembly.GetExecutingAssembly());

var app = builder.Build();
app.Urls.Add("http://localhost:5002");
await app.RunMcpAsync(args);

Protected Tools

using FastMCP.Attributes;
using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;

public static class SecureTools
{
    /// <summary>
    /// Public tool - anyone can call
    /// </summary>
    [McpTool]
    public static string Echo(string message) => message;

    /// <summary>
    /// Protected tool - requires valid OAuth token
    /// </summary>
    [McpTool]
    [Authorize]
    public static object GetUserInfo(ClaimsPrincipal user)
    {
        return new
        {
            Name = user.Identity?.Name ?? "Unknown",
            Email = user.FindFirst("email")?.Value ?? "Not available",
            IsAuthenticated = user.Identity?.IsAuthenticated ?? false,
            Claims = user.Claims.Select(c => new { c.Type, c.Value }).ToList()
        };
    }

    /// <summary>
    /// Role-based authorization
    /// </summary>
    [McpTool]
    [Authorize(Roles = "Admin")]
    public static string AdminOnly() => "Admin access granted";
}

📡 JSON-RPC Protocol

Prompts

Prompts allow servers to provide templates that LLMs can use.

using FastMCP.Attributes;
using FastMCP.Protocol;

public static class MyPrompts
{
    [McpPrompt("analyze_code")]
    public static GetPromptResult Analyze(string code)
    {
        return new GetPromptResult
        {
            Description = "Analyze the given code",
            Messages = new List<PromptMessage>
            {
                new PromptMessage 
                { 
                    Role = "user", 
                    Content = new { type = "text", text = $"Please analyze this code:\n{code}" } 
                }
            }
        };
    }
}

Calling Tools

Public Tool (No Auth):

POST /mcp
{
  "jsonrpc": "2.0",
  "method": "Echo",
  "params": ["Hello World"],
  "id": 1
}

Protected Tool (With Auth):

POST /mcp
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc...

{
  "jsonrpc": "2.0",
  "method": "GetUserInfo",
  "params": [],
  "id": 2
}

🧪 Testing

Run All Tests

dotnet test

Test Authentication Flow

Each authentication example includes a comprehensive .rest file for testing:

# Open in VS Code with REST Client extension
code examples/Auth/AzureAdOAuth/azure-ad-auth-tests.rest

Test files include:

  • ✅ Discovery endpoints
  • ✅ Public tool tests
  • ✅ Protected tool tests (should fail without auth)
  • ✅ OAuth authorization flow
  • ✅ Token exchange
  • ✅ Provider-specific API calls

📖 Documentation

Guides & Features

Complete Authentication Guide

See MFA Support Guide for enforcing Multi-Factor Authentication on sensitive tools, and the individual provider README files under examples/Auth/ for detailed OAuth setup instructions.

Example Projects

ExampleDescriptionPort
BasicServerSimple MCP server with Auto-DI & [McpDescription]5000
HealthChecksDemo🏥 Health monitoring & diagnostics demo5000
TelemetryDemo📡 OpenTelemetry metrics & tracing demo5000
AzureAdOAuthAzure AD authentication example5002
GoogleOAuthGoogle OAuth example5000
GitHubOAuthGitHub OAuth example5001
Auth0OAuthAuth0 authentication example5005
OktaOAuthOkta authentication example5007
AwsCognitoOAuthAWS Cognito example5006

🏗️ Advanced Features

⚡ Automatic DI Registration & [McpDescription] (NEW! v2.1.0)

DotnetFastMCP 2.1 makes authoring production MCP servers completely zero-boilerplate by pairing automatic Dependency Injection with LLM-grade parameter schemas:

  1. Zero-Config DI: Non-static tool, resource, and prompt classes scanned with WithComponentsFrom() are automatically registered as Transient into ASP.NET Core DI. No more manual builder.Services.AddTransient<OrderTools>() lines.
  2. Preserves Custom Lifetimes: Built on TryAddTransient semantics, so any class explicitly registered as Singleton or Scoped in builder.Services retains its desired lifetime.
  3. [McpDescription] for Parameters: Annotate tool parameters with descriptions that are exposed directly in the JSON Schema inputSchema (tools/list), giving LLMs exact semantic context and eliminating hallucinated arguments.
  4. Framework Parameter Exclusion: Types such as McpContext, CancellationToken, ClaimsPrincipal, and IMcpSession are automatically filtered out from the public schema.
public class OrderTools
{
    private readonly IOrderRepository _repository;
    private readonly ILogger<OrderTools> _logger;

    // Injected automatically via ASP.NET Core DI
    public OrderTools(IOrderRepository repository, ILogger<OrderTools> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    [McpTool(Description = "Retrieves order status by order identifier and country")]
    public async Task<string> GetOrderStatus(
        [McpDescription("Unique order ID, e.g. ORD-98765")] string orderId,
        [McpDescription("Two-letter country code, e.g. US, UK")] string countryCode = "US",
        CancellationToken ct = default) // Framework types are automatically excluded from the tool schema
    {
        _logger.LogInformation("Fetching order {OrderId} in {Country}", orderId, countryCode);
        return await _repository.GetStatusAsync(orderId, countryCode, ct);
    }
}
// Program.cs - Zero boilerplate registration!
var server = new FastMCPServer("OrderServer");
var builder = McpServerBuilder.Create(server, args);

// Automatically registers OrderTools as Transient, discovers [McpTool], and configures schemas!
builder.WithComponentsFrom(Assembly.GetExecutingAssembly());

var app = builder.Build();
await app.RunMcpAsync(args);

🏥 Health Checks & Diagnostics (v1.15.0)

FastMCP ships with a built-in production health check endpoint. Enable with one line and plug in any custom check as a simple lambda.

using FastMCP.Health;

// Zero-config — exposes GET /mcp/health automatically
builder.WithHealthChecks();

// With custom checks (database, LLM provider, memory, etc.)
builder.WithHealthChecks(checks =>
{
    checks.AddCheck("memory", () =>
        GC.GetTotalMemory(false) < 500_000_000L); // sync: < 500 MB

    checks.AddAsyncCheck("database", async ct =>
        await dbContext.Database.CanConnectAsync(ct));

    checks.AddAsyncCheck("llm_provider", async ct =>
        await llmProvider.IsHealthyAsync(ct));
});

Response JSON (HTTP 200 — Healthy):

{
  "status": "Healthy",
  "timestamp": "2026-04-19T20:00:00Z",
  "checks": [
    { "name": "mcp_server",   "status": "Healthy", "durationMs": 0 },
    { "name": "memory",       "status": "Healthy", "durationMs": 0.1 },
    { "name": "database",     "status": "Healthy", "durationMs": 4.9 },
    { "name": "llm_provider", "status": "Healthy", "durationMs": 22.3 }
  ],
  "diagnostics": {
    "serverName": "my-mcp-server",
    "frameworkVersion": "1.15.0.0",
    "toolCount": 12,
    "uptimeSeconds": 3721.4
  }
}

HTTP status code mapping:

StatusHTTP CodeMeaning
Healthy200All checks passed
Degraded207Server up, ≥1 check timed out
Unhealthy503≥1 check failed or threw

Kubernetes liveness / readiness probe:

livenessProbe:
  httpGet:
    path: /mcp/health
    port: 5000
  initialDelaySeconds: 15
  periodSeconds: 30
readinessProbe:
  httpGet:
    path: /mcp/health
    port: 5000
  periodSeconds: 10

See Health Checks Guide for full documentation, including Docker Compose, Azure Container Apps, per-check timeout configuration, unit testing patterns, and complete validation examples.


📡 Observability — OpenTelemetry (v1.14.0)

FastMCP ships with built-in OpenTelemetry instrumentation. Enable with one line and connect to any backend.

using FastMCP.Telemetry;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;

// 1. Enable FastMCP telemetry (one line)
builder.WithTelemetry(t =>
{
    t.ServiceName    = "my-mcp-server";
    t.EnableMetrics  = true;
    t.EnableTracing  = true;
});

// 2. Configure your exporter of choice
builder.Services.AddOpenTelemetry()
    .WithMetrics(m =>
    {
        m.AddMcpInstrumentation();  // FastMCP extension method
        m.AddPrometheusExporter();  // or AddConsoleExporter(), AddOtlpExporter()
    })
    .WithTracing(t =>
    {
        t.AddMcpInstrumentation();  // FastMCP extension method
        t.AddOtlpExporter();        // or AddJaeger(), AddZipkin()
    });

Metrics automatically tracked:

MetricTypeTagDescription
mcp.tool.invocationsCountertool.nameTotal tool calls
mcp.tool.durationHistogram (ms)tool.nameTool execution time
mcp.tool.errorsCountertool.nameFailed tool calls
mcp.prompt.requestsCounter—Prompt template requests
mcp.resource.readsCounter—Resource read requests

Validate with dotnet-counters (no exporter needed):

dotnet-counters monitor -n YourAppName --counters FastMCP

See Observability Guide for full documentation, including production exporter setup, distributed tracing details, and real request/response validation examples.


Middleware Interception

Middleware allows you to intercept and modify JSON-RPC messages (requests and responses) flowing through the server pipeline. This is useful for logging, validation, modification, or custom monitoring.

  1. Define Middleware: Implement IMcpMiddleware.
  2. Register Middleware: Use builder.AddMcpMiddleware<T>().
public class LoggingMiddleware : IMcpMiddleware
{
    public async Task<JsonRpcResponse> InvokeAsync(McpMiddlewareContext context, McpMiddlewareDelegate next, CancellationToken ct)
    {
        Console.Error.WriteLine($"[LOG] Incoming: {context.Request.Method}");
        
        // Pass to next handler
        var response = await next(context, ct);
        
        Console.Error.WriteLine($"[LOG] Completed. Error: {response.Error != null}");
        return response;
    }
}

// In Program.cs:
builder.AddMcpMiddleware<LoggingMiddleware>();

Server Composition (NEW!)

Mount other MCP servers into your main server instantiation. This supports a "Micro-MCP" architecture where you can compose a robust agent from smaller, focused modules.

// 1. Create Sub-Server (e.g. GitHub Tools)
var githubServer = new FastMCPServer("GitHub");
// ... register tools ...

// 2. Import into Main Server with "gh" prefix
builder.AddServer(githubServer, prefix: "gh");

// Result:
// The client sees tools named: "gh_create_issue", "gh_get_repo", etc.

MFA Support (NEW!)

Enforce Multi-Factor Authentication for sensitive tools.

[McpTool("transfer_funds")]
[AuthorizeMcpTool(RequireMfa = true)]
public static string TransferFunds()
{
    return "Transferred!";
}
  • MFA Check: Verifies amr claim contains mfa.
  • Security: Provides granular protection for critical operations.

Storage Abstraction (NEW!)

FastMCP now includes a built-in state persistence layer. Tools can request McpContext to access IMcpStorage.

[McpTool]
public static async Task<string> SetValue(string key, string value, McpContext context)
{
    await context.Storage.SetAsync(key, value);
    return "Saved!";
}

The default implementation is In-Memory, but you can swap it for Redis, SQL, or File storage:

builder.AddMcpStorage<MyRedisStorage>();

LLM Integration (NEW!)

FastMCP includes a powerful LLM integration system with 8 providers supporting the latest models (Feb 2026).

Quick Setup

using FastMCP.AI;

// Option 1: Local (Ollama)
builder.AddOllamaProvider(options =>
{
    options.BaseUrl = "http://localhost:11434";
    options.DefaultModel = "llama3.1:8b";
});

// Option 2: Cloud (Anthropic Claude Opus 4.6 - Latest)
builder.AddAnthropicProvider(options =>
{
    options.ApiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY")!;
    options.DefaultModel = "claude-opus-4.6"; // 1M context, Feb 2026
});

// Option 3: Google Gemini 3
builder.AddGeminiProvider(options =>
{
    options.ApiKey = Environment.GetEnvironmentVariable("GEMINI_API_KEY")!;
    options.DefaultModel = "gemini-3-flash"; // Fast, cost-effective
});

Use in Tools

public class AITools
{
    private readonly ILLMProvider _llm;

    public AITools(ILLMProvider llm) => _llm = llm;

    [McpTool("generate_story")]
    public async Task<string> GenerateStory(string topic)
    {
        return await _llm.GenerateAsync(
            $"Write a story about {topic}",
            new LLMGenerationOptions
            {
                SystemPrompt = "You are a creative storyteller.",
                Temperature = 0.8,
                MaxTokens = 500
            });
    }

    [McpTool("stream_response")]
    public async IAsyncEnumerable<string> StreamResponse(string prompt)
    {
        await foreach (var token in _llm.StreamAsync(prompt))
        {
            yield return token;
        }
    }
}

Supported Providers (Feb 2026)

ProviderExtension MethodLatest ModelBest For
OllamaAddOllamaProvider()llama3.1:8bLocal, privacy, offline
OpenAIAddOpenAIProvider()gpt-4-turboProduction, function calling
Azure OpenAIAddAzureOpenAIProvider()gpt-4Enterprise, compliance
AnthropicAddAnthropicProvider()claude-opus-4.6Deep reasoning, 1M context
Google GeminiAddGeminiProvider()gemini-3-flashMultimodal, high-volume
CohereAddCohereProvider()command-aEnterprise RAG, agents
Hugging FaceAddHuggingFaceProvider()Any modelOpen-source, flexibility
DeepseekAddDeepseekProvider()deepseek-v3.2Cost-effective, reasoning

See LLM Integration Guide for complete documentation.

Background Tasks (NEW!)

FastMCP allows tools to fire-and-forget long running operations using RunInBackground.

[McpTool]
public static async Task<string> ProcessFile(string file, McpContext context)
{
    await context.RunInBackground(async (ct) => 
    {
        // This runs without blocking the client
        await HeavyProcessing(file, ct);
    });

    return "Processing started!";
}

Icons Support (NEW!)

Enhance the user interface of clients by providing icons for your server and tools.

// Server Icon
server.Icon = "https://myserver.com/logo.png";

// Tool Icon
[McpTool(Icon = "https://myserver.com/tools/calc.png")]
public static int Add(int a, int b) => a + b;

Binary Content Support (NEW!)

Return rich content like Images from your tools and prompts.

[McpTool]
public static CallToolResult GetSnapshot()
{
    return new CallToolResult 
    {
        Content = new List<ContentItem> 
        {
            new ImageContent { Data = "base64...", MimeType = "image/png" }
        }
    };
}

OAuth Proxy

DotnetFastMCP includes a built-in OAuth Proxy that provides:

  • ✅ Dynamic Client Registration (DCR) - Automatic client registration for MCP clients
  • ✅ Authorization Code Flow - Full OAuth 2.0 authorization code flow with PKCE
  • ✅ Token Management - Automatic token exchange, refresh, and revocation
  • ✅ Discovery Endpoints - RFC 8414 compliant OAuth discovery

Automatically Available Endpoints:

  • /.well-known/oauth-authorization-server - OAuth server metadata
  • /oauth/authorize - Authorization endpoint
  • /oauth/token - Token endpoint
  • /oauth/register - Dynamic client registration
  • /oauth/userinfo - User information endpoint

Custom Scopes

Override default scopes for any provider:

builder.AddAzureAdTokenVerifier(new AzureAdAuthOptions
{
    RequiredScopes = new[] { "openid", "profile", "email", "User.Read", "Calendars.Read" }
});

Multiple Authentication Schemes

// Support multiple providers simultaneously
builder.AddAzureAdTokenVerifier();
builder.AddGoogleTokenVerifier();
builder.AddGitHubTokenVerifier();

🔐 Security Best Practices

Development

  • ✅ Use environment variables for secrets
  • ✅ Never commit credentials to source control
  • ✅ Use .env files for local development
  • ✅ Test with short-lived tokens

Production

  • ✅ Use HTTPS for all communication
  • ✅ Store secrets in Azure Key Vault / AWS Secrets Manager
  • ✅ Enable MFA for OAuth providers
  • ✅ Implement rate limiting
  • ✅ Monitor authentication logs
  • ✅ Use separate app registrations per environment
  • ✅ Validate token scopes match required permissions

📦 NuGet Package

Install from NuGet (when published):

dotnet add package DotnetFastMCP

🤝 Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🔗 Resources

Official Documentation

Framework Documentation

Provider Documentation

🐛 Issues & Support

For bug reports and feature requests, please use GitHub Issues.

✨ What's New

v2.1.1 - Client Deserialization Patch (Latest - Sep 2026)

  • 🐛 Fix McpClient.CallToolAsync<TResult> Deserialization - Resolved deserialization error where calling tools returning primitive types (int, bool, double, etc.), string, or custom POCO models threw JSON conversion errors (fixes #37).
  • 📦 Automatic Envelope Unwrapping - Correctly unwraps and deserializes the inner payload from CallToolResult.Content while maintaining full MCP specification compliance.
  • ⚡ Direct Envelope Overload - Added non-generic client.CallToolAsync("toolName", args) returning raw CallToolResult directly.
  • 🧪 Comprehensive Test Coverage - Added unit and integration test suites validating primitive, string, and complex model deserialization.

v2.1.0 - Zero-Boilerplate MCP Servers (Sep 2026)

  • ⚡ Automatic DI Registration - Non-static classes containing [McpTool], [McpResource], or [McpPrompt] are automatically registered as Transient during WithComponentsFrom(). No manual builder.Services.AddTransient<T>() boilerplate required.
  • 🛡️ Lifespan Safety - Implemented via TryAddTransient so custom Singleton or Scoped registrations configured in builder.Services are never overwritten.
  • 📝 [McpDescription] Parameter Attribute - Tool parameters annotated with [McpDescription] have their documentation automatically rendered into JSON Schema properties.<param>.description in tools/list.
  • 🧼 Clean Schema Generation - Framework types (McpContext, CancellationToken, ClaimsPrincipal, IMcpSession) are automatically excluded from tools/list schema definitions, preventing LLM argument errors.
  • 🧪 57 Tests Passing - Dual-targeted unit and integration test suite passing across both .NET 8 LTS and .NET 10 LTS.
  • 📖 Comprehensive Guide - Detailed documentation in docs/auto-di-registration-guide.md.

v2.0.0 - .NET 10 LTS & .NET 8 LTS Dual Support (Aug 2026)

  • 🚀 Dual-Targeting - Ships both net8.0 and net10.0 binaries in a single package.
  • 🔒 Zero Breaking Changes - 100% backward compatible for existing .NET 8 applications.
  • ⚡ High-Performance Non-Blocking Async Streams - SSE parser compliant with .NET 10 CA2024 rules.
  • 🧪 Comprehensive Test Matrix - Unit & in-memory integration tests running across both target frameworks.

v1.15.0 - Health Checks & Diagnostics (Apr 2026)

  • 🏥 Built-In Health Endpoint - GET /mcp/health exposed with a single builder.WithHealthChecks() call
  • 🔌 Lambda-Based Custom Checks - Add any check (database, llm, memory, external API) as a simple lambda with no interface to implement
  • ⚡ Parallel Execution - All checks run concurrently; a slow check never delays a fast one
  • ⏱️ Per-Check Timeout - Configurable MaxResponseTimeMs; hanging checks reported as Degraded, not left blocking
  • 🌐 Standard HTTP Status Codes - 200 Healthy / 207 Degraded / 503 Unhealthy; understood natively by Kubernetes, load balancers, and APM tools
  • 📊 Auto Server Diagnostics - Automatically includes server name, framework version, tool/resource/prompt counts, and uptime
  • 🛡️ Always Reachable - Endpoint marked AllowAnonymous() so infrastructure probes bypass authentication
  • 🎯 Zero Overhead - Fully opt-in; endpoint is not registered unless WithHealthChecks() is called
  • 📚 Comprehensive Docs - Full guide covering Kubernetes, Docker, ACA probes, validation walkthrough, and unit tests

v1.14.0 - OpenTelemetry Observability (Mar 2026)

  • 📡 OpenTelemetry Integration - First-class metrics and distributed tracing built in
  • 📊 5 Auto-Tracked Metrics - Tool invocations, duration, errors, prompt requests, resource reads
  • ✨ One-Line Setup - builder.WithTelemetry() with zero boilerplate
  • 🔌 Exporter Agnostic - Works with Prometheus, App Insights, Grafana, Jaeger, any OTLP backend
  • 🔍 Distributed Tracing - Full span support with OTel semantic convention tags
  • 🛡️ PII Safe Defaults - Tool inputs never logged unless explicitly enabled
  • 🎯 Zero Overhead - Fully opt-in, no cost when not used
  • 📚 Comprehensive Docs - Full guide with validation examples and production checklist

v1.13.0 - LLM Integration (Feb 2026)

  • 🤖 8 LLM Providers - Ollama, OpenAI, Azure OpenAI, Anthropic Claude, Google Gemini, Cohere, Hugging Face, Deepseek
  • ✨ Latest Models - Claude Opus 4.6 (1M context), Gemini 3 Pro/Flash, Command A, DeepSeek V3.2
  • 🔌 Unified Interface - Single ILLMProvider API for all providers
  • 📡 Streaming Support - Real-time token streaming with IAsyncEnumerable<string>
  • 🏗️ Production-Ready - HttpClientFactory, Polly retry policies, connection pooling
  • 🎯 Plug-and-Play - Simple registration: builder.AddAnthropicProvider()
  • 📚 Comprehensive Docs - Complete integration guide with examples

v1.12.0 - MFA Support

  • 🛡️ MFA Enforcement - Require mfa AMR claim for sensitive tools
  • ✅ Granular Control - Enable per-tool using [AuthorizeMcpTool(RequireMfa=true)]
  • 🔒 Enhanced Security - Standards-based multi-factor authentication check

v1.11.0 - Binary Content Support

  • ✅ Polymorphic Content - Support for mixed Text and Image responses
  • ✅ Image Support - Return Base64 encoded images from tools
  • ✅ Multimodal Prompts - Embbed images in prompts for LLM context

v1.10.0 - Icons Support

  • ✅ Server Icons - Define a brand icon for your MCP server
  • ✅ Tool/Resource Icons - Visually distinguish capabilities
  • ✅ UI/UX Enhancement - Enable richer client experiences

v1.9.0 - Background Tasks

  • ✅ Fire-and-Forget - Offload long-running operations from tools
  • ✅ Non-Blocking - Return immediate responses to clients
  • ✅ Hosted Service - Built-in queuing mechanism using Channels

v1.8.0 - Storage Abstractions

  • ✅ State Persistence - Tools can now persist data via McpContext.Storage
  • ✅ Pluggable Backends - Swap in Redis/SQL/File storage easily
  • ✅ In-Memory Default - Zero-config built-in storage for development

v1.7.0 - Server Composition

  • ✅ Server Composition - Mount other MCP servers as modules (Micro-MCPs)
  • ✅ Namespacing - Automatically prefix imported tools (e.g., github_createIssue)
  • ✅ Zero-Overhead - High-performance internal dictionary routing (O(1))

v1.6.0 - Middleware Interception

  • ✅ Middleware Pipeline - Intercept and modify requests/responses
  • ✅ Critical Fixes - Resolved Stdio transport initialization deadlocks
  • ✅ Builder API - Easy registration with AddMcpMiddleware<T>

v1.5.0 - Native Client Library

  • ✅ McpClient - Type-safe .NET client for consuming MCP servers
  • ✅ Transport Agnostic - Support for both Stdio and SSE connections
  • ✅ Notification Handling - Events for real-time logs and progress

v1.4.0 - Server-Sent Events (SSE)

  • ✅ SSE Transport - Real-time server-to-client streaming transport
  • ✅ Async Notifications - Push logs and progress updates to HTTP clients

v1.3.0 - Context & Interaction

  • ✅ Context System - McpContext injection for logging and progress
  • ✅ IMcpSession - Transport-agnostic interaction abstraction

v1.2.0 - Protocol Discovery

  • ✅ Dynamic Discovery - Auto-discovery of Tools, Resources, and Prompts
  • ✅ Prompts/List - Full support for prompt templates

v1.1.0 - Stdio Transport & Authentication

  • ✅ Stdio Transport - Initial support for stdio communication
  • 🔐 6 OAuth Providers - Azure AD, Google, GitHub, Auth0, Okta, AWS Cognito
  • 🔐 OAuth Proxy - Built-in DCR support

v1.0.0 - Core Framework

  • ✅ Attribute-based API
  • ✅ JSON-RPC 2.0 compliance
  • ✅ ASP.NET Core integration

Made with ❤️ by the DotnetFastMCP team

⭐ Star this repo if you find it useful!

ai-agent
aspnetcore
claude
csharp
dotnet-core
genrative-ai
llms
mcp-gateway
microservice
model-context-protocol
open-ai
sdk
semantic-kernel
tooling

tekspry/DotnetFastMCP

A lightweight .NET framework for building Model Context Protocol (MCP) servers. Integrates seamlessly with Azure AD, AWS Cognito, Auth0, OpenAI and other providers

See the code

README

DotnetFastMCP — Enterprise Security & Governance Gateway for MCP Servers

.NET 8.0 .NET 10.0 NuGet License GitHub

Enterprise security, governance, and observability layer for Model Context Protocol (MCP) servers in .NET — OAuth 2.0/OIDC authentication, per-tool MFA enforcement, OpenTelemetry instrumentation, and zero-config health checks. Built on ASP.NET Core.

🎯 Overview

DotnetFastMCP adds enterprise-grade security, governance, and observability to your MCP servers. While the core protocol is simple, running MCP tools in production requires OAuth 2.0/OIDC authentication, per-tool MFA enforcement, distributed tracing, and health monitoring — none of which the base protocol provides. DotnetFastMCP handles all of this with a clean attribute-based API on ASP.NET Core, plus a native .NET client library for consuming MCP servers.

⭐ Key Features

⚡ Zero-Boilerplate MCP Servers (NEW! v2.1.0)

  • ✅ Automatic DI Registration - Non-static tool, resource, and prompt classes scanned via WithComponentsFrom() are automatically registered as Transient services in the DI container. Zero manual builder.Services.AddTransient<T>() boilerplate.
  • ✅ Preserves Custom Lifetimes - Built on TryAddTransient semantics to honor custom Singleton or Scoped registrations without collision.
  • ✅ [McpDescription] Parameter Attributes - Annotate method parameters with rich descriptions emitted directly into JSON Schema inputSchema (tools/list), significantly enhancing LLM tool-calling accuracy.
  • ✅ Smart Schema Filtering - Automatically hides framework-injected types (McpContext, CancellationToken, ClaimsPrincipal, IMcpSession) from schema exposure so LLMs only see valid user inputs.

🚀 .NET 10 LTS & .NET 8 LTS Dual Support (v2.0.0)

  • ✅ Dual-Targeting - Ships both net8.0 and net10.0 binaries in a single package
  • ✅ Zero Breaking Changes - 100% backward compatible for existing .NET 8 applications
  • ✅ Modern Non-Blocking Async Streams - High-performance SSE parsing compliant with .NET 10 CA2024 rules
  • ✅ Comprehensive Test Matrix - Dual-targeted unit & in-memory integration tests covering positive & negative scenarios

Core Framework

  • ✅ Simple Attribute-Based API - Declare tools and resources with [McpTool] and [McpResource] attributes
  • ✅ First-Class Prompts Support - Define prompts with [McpPrompt] for LLM interaction templates
  • ✅ Automatic Component Discovery - Reflection-based scanning of assemblies
  • ✅ JSON-RPC 2.0 Compliant - Full protocol compliance with proper error handling
  • ✅ Flexible Parameter Binding - Supports both array and named parameters
  • ✅ Built on ASP.NET Core - Leverage the powerful ASP.NET Core hosting model
  • ✅ Production Ready - Comprehensive error handling and logging
  • ✅ Type-Safe - Full C# type system integration

🔐 Enterprise Authentication

  • ✅ 6 OAuth Providers Supported - Azure AD, Google, GitHub, Auth0, Okta, AWS Cognito
  • ✅ OAuth Proxy Built-In - Automatic Dynamic Client Registration (DCR) for non-DCR providers
  • ✅ JWT Token Verification - Automatic token validation with JWKS caching
  • ✅ Zero Configuration - Set environment variables and go
  • ✅ Sensible Defaults - Pre-configured scopes for common use cases
  • ✅ Fine-Grained Authorization - Protect tools with [Authorize] attribute
  • ✅ Claims-Based Access - Access user information from authenticated requests
  • ✅ MFA Support - Enforce Multi-Factor Authentication for sensitive tools

🔌 Native Client Library

  • ✅ McpClient - Type-safe .NET client for consuming any MCP server
  • ✅ Transport Agnostic - Support for both Stdio and SSE connections
  • ✅ Notification Handling - Events for real-time logs and progress
  • ✅ Tool Invocation - Clean CallToolAsync<T> API

🤖 LLM Integration

  • ✅ 8 LLM Providers - Ollama, OpenAI, Azure OpenAI, Anthropic Claude, Google Gemini, Cohere, Hugging Face, Deepseek
  • ✅ Latest Models (Feb 2026) - Claude Opus 4.6, Gemini 3 Pro/Flash, Command A, DeepSeek V3.2
  • ✅ Unified Interface - Single ILLMProvider API for all providers
  • ✅ Streaming Support - Real-time token streaming with IAsyncEnumerable<string>
  • ✅ Production-Ready - HttpClientFactory, Polly retry policies, connection pooling
  • ✅ Plug-and-Play - Simple extension methods: builder.AddAnthropicProvider()

📡 Observability

  • ✅ OpenTelemetry Integration - First-class metrics and distributed tracing
  • ✅ 5 Auto-Tracked Metrics - Tool invocations, duration, errors, prompt requests, resource reads
  • ✅ One-Line Setup - builder.WithTelemetry() — zero boilerplate
  • ✅ Exporter Agnostic - Plug in Prometheus, Application Insights, Grafana, Jaeger, or any OTLP backend
  • ✅ OTel Semantic Conventions - Standard tag names, exception events, span status
  • ✅ Zero Overhead When Disabled - Fully opt-in, no performance cost if unused
  • ✅ Stdio + HTTP - Metrics work across both transports

🏥 Health Checks & Diagnostics

  • ✅ Built-In Health Endpoint - GET /mcp/health exposed automatically
  • ✅ One-Line Setup - builder.WithHealthChecks() — no configuration required
  • ✅ Plug-In Custom Checks - Add any check as a simple lambda (no interfaces needed)
  • ✅ Parallel Execution - All checks run concurrently with per-check timeout
  • ✅ Standard HTTP Status Codes - 200 Healthy / 207 Degraded / 503 Unhealthy
  • ✅ Kubernetes & Docker Ready - Drop-in for liveness/readiness probes
  • ✅ Auto Server Diagnostics - Tool count, uptime, framework version included
  • ✅ Zero Overhead When Disabled - Fully opt-in, endpoint not registered unless configured

🚀 Quick Start

Installation

Install via NuGet Package Manager:

dotnet add package DotnetFastMCP --version 2.1.1

Or clone the repository:

git clone https://github.com/tekspry/.NetFastMCP.git
cd DotnetFastMCP
dotnet build -c Release

Create Your First MCP Server

1. Define Your Tools

Tools can be written as instance classes with constructor dependency injection (auto-registered!) or static methods:

using FastMCP.Attributes;
using Microsoft.Extensions.Logging;

// Instance-based tool with constructor injection (automatically registered into DI via WithComponentsFrom!)
public class CalculatorTools
{
    private readonly ILogger<CalculatorTools> _logger;

    public CalculatorTools(ILogger<CalculatorTools> logger)
    {
        _logger = logger;
    }

    [McpTool(Description = "Performs mathematical addition")]
    public int Add(
        [McpDescription("The first number to add")] int a,
        [McpDescription("The second number to add")] int b)
    {
        _logger.LogInformation("Adding {A} + {B}", a, b);
        return a + b;
    }
}

// Static tools are also supported out of the box
public static class EchoTools
{
    [McpTool(Description = "Returns an echo of the input message")]
    public static string Echo(
        [McpDescription("Text message to echo back")] string message) => message;
}

2. Create Program.cs

using FastMCP.Hosting;
using FastMCP.Server;
using System.Reflection;

var server = new FastMCPServer("MyMcpServer");
var builder = McpServerBuilder.Create(server, args);
builder.WithComponentsFrom(Assembly.GetExecutingAssembly());

var app = builder.Build();
await app.RunMcpAsync(args);

Running the Example Server

cd examples/BasicServer
dotnet run

The server will start on http://localhost:5000.

🏆 Built With DotnetFastMCP

Real-world enterprise projects that demonstrate DotnetFastMCP in production:

👗 Fashion Accessory AI Marketing Pipeline

GitHub .NET 10.0 Google A2A

An enterprise-grade, distributed multimodal AI pipeline on .NET 10 LTS that automates the transformation of raw fashion accessory photographs into commercial marketing visuals and video content.

Architecture highlights:

  • 🏗️ Two-Dimensional AI Architecture — DotnetFastMCP (vertical MCP tool layer) + Google A2A Protocol (horizontal agent communication)
  • 🤖 5 DotnetFastMCP Servers — VisionMcpServer, PromptMcpServer, ImageMcpServer, InpaintingMcpServer, VideoMcpServer
  • 🎨 Multimodal AI — Gemini 3.1 Flash Image for dual-conditioning image synthesis, Kling AI for video generation
  • 🛡️ Multi-Tenant SaaS — Entity Framework Core global query filters with tenant isolation
  • ⏱️ Async Background Jobs — Hangfire with exponential backoff and rate-limit protection
OrchestratorAgent (A2A)
    ├── VisionAgent → VisionMcpServer  :5100  (extract_accessory_features)
    ├── CreativeAgent → PromptMcpServer :5200  (generate_image_prompts)
    ├── ImageAgent → ImageMcpServer    :5300  (generate_accessory_image)
    ├── InpaintingAgent → InpaintingMcpServer :5500  (inpaint_accessory)
    └── VideoAgent → VideoMcpServer    :5400  (generate_accessory_video)

🔗 View Repository →


📚 Architecture

Core Components

DotnetFastMCP/
├── src/
│   ├── FastMCP/
│   │   ├── Attributes/          # Component declaration attributes
│   │   ├── Client/              # 🔌 Client library implementation
│   │   ├── Hosting/             # Server hosting and middleware
│   │   ├── Protocol/            # JSON-RPC protocol implementation
│   │   ├── Server/              # FastMCPServer core class
│   │   └── FastMCP.csproj
│   └── FastMCP.CLI/             # Command-line utilities
├── examples/
│   └── BasicServer/             # Example MCP server implementation
├── tests/
│   └── McpIntegrationTest/      # Integration tests
├── LAUNCH_TESTS.ps1             # PowerShell test suite launcher
└── RUN_AND_TEST.ps1             # PowerShell integration test script

Project Structure

ProjectPurpose
FastMCPCore framework library
FastMCP.CLICommand-line interface tools
BasicServerExample MCP server implementation
McpIntegrationTestIntegration tests
ClientDemoExample Client consuming BasicServer

🔧 Creating an MCP Server

1. Define Components

For better organization, split your components into multiple files (e.g., Tools.cs, Resources.cs). The framework will discover them automatically.

File: Tools.cs

using FastMCP.Attributes;
using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;

public static class MyTools
{
    /// <summary>
    /// Public tool - no authentication required
    /// </summary>
    [McpTool]
    public static int Add(int a, int b) => a + b;

public static class Resources
{
    /// <summary>
    /// Protected tool - requires authentication
    /// </summary>
    [McpTool]
    [Authorize]
    public static object GetUserProfile(ClaimsPrincipal user)
    {
        return new
        {
            Name = user.Identity?.Name,
            Email = user.FindFirst("email")?.Value,
            IsAuthenticated = user.Identity?.IsAuthenticated
        };
    }
}

2. Configure Server with Authentication

using FastMCP.Hosting;
using FastMCP.Server;
using System.Reflection;

var mcpServer = new FastMCPServer(name: "My Secure MCP Server");
var builder = McpServerBuilder.Create(mcpServer, args);

// Add authentication (choose your provider)
builder.AddAzureAdTokenVerifier();  // or AddGoogleTokenVerifier(), AddGitHubTokenVerifier(), etc.

// Register tools
builder.WithComponentsFrom(Assembly.GetExecutingAssembly());

var app = builder.Build();
app.Urls.Add("http://localhost:5002");
await app.RunAsync();

3. Set Environment Variables

# Windows PowerShell
$env:FASTMCP_SERVER_AUTH_AZUREAD_TENANT_ID="your-tenant-id"
$env:FASTMCP_SERVER_AUTH_AZUREAD_CLIENT_ID="your-client-id"
$env:FASTMCP_SERVER_AUTH_AZUREAD_CLIENT_SECRET="your-client-secret"
# Linux/Mac
export FASTMCP_SERVER_AUTH_AZUREAD_TENANT_ID="your-tenant-id"
export FASTMCP_SERVER_AUTH_AZUREAD_CLIENT_ID="your-client-id"
export FASTMCP_SERVER_AUTH_AZUREAD_CLIENT_SECRET="your-client-secret"

4. Run and Test

dotnet run

Your server is now running with OAuth Proxy endpoints:

  • MCP endpoint: http://localhost:5002/mcp
  • OAuth authorization: http://localhost:5002/oauth/authorize
  • OAuth token: http://localhost:5002/oauth/token
  • Discovery: http://localhost:5002/.well-known/oauth-authorization-server

Stdio Mode

You can also run the server in Stdio mode (for local LLM clients):

dotnet run -- --stdio

Create an MCP Client

Connect to any MCP server using the C# Client Library:

using FastMCP.Client;
using FastMCP.Client.Transports;

// 1. Connect (via Stdio or SSE)
var transport = new StdioClientTransport("dotnet", "run --project examples/BasicServer -- --stdio");
await using var client = new McpClient(transport);
await client.ConnectAsync();

// 2. List & Call Tools
var tools = await client.ListToolsAsync();
var result = await client.CallToolAsync<int>("add_numbers", new { a = 10, b = 20 });

🔐 Authentication Providers

DotnetFastMCP supports 6 enterprise-grade OAuth providers out of the box:

ProviderMethodUse CaseDefault Scopes
Azure ADAddAzureAdTokenVerifier()Enterprise apps, Microsoft 365openid, profile, email, offline_access
GoogleAddGoogleTokenVerifier()Consumer apps, Google Workspaceopenid, profile, email, userinfo.profile
GitHubAddGitHubTokenVerifier()Developer tools, repositoriesread:user, user:email
Auth0AddAuth0TokenVerifier()Multi-tenant SaaS, custom identityopenid, profile, email, offline_access
OktaAddOktaTokenVerifier()Enterprise SSO, workforce identityopenid, profile, email, offline_access
AWS CognitoAddAwsCognitoTokenVerifier()AWS-native apps, user poolsopenid, profile, email

Quick Setup Examples

Azure AD
builder.AddAzureAdTokenVerifier();

Environment Variables:

FASTMCP_SERVER_AUTH_AZUREAD_TENANT_ID=your-tenant-id
FASTMCP_SERVER_AUTH_AZUREAD_CLIENT_ID=your-client-id
FASTMCP_SERVER_AUTH_AZUREAD_CLIENT_SECRET=your-client-secret

Example: examples/Auth/AzureAdOAuth

Google
builder.AddGoogleTokenVerifier();

Environment Variables:

FASTMCP_SERVER_AUTH_GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
FASTMCP_SERVER_AUTH_GOOGLE_CLIENT_SECRET=your-client-secret

Example: examples/Auth/GoogleOAuth

GitHub
builder.AddGitHubTokenVerifier();

Environment Variables:

FASTMCP_SERVER_AUTH_GITHUB_CLIENT_ID=your-github-client-id
FASTMCP_SERVER_AUTH_GITHUB_CLIENT_SECRET=your-github-client-secret

Example: examples/Auth/GitHubOAuth

### PowerShell Integration Test Suite

The project includes a comprehensive PowerShell-based integration test suite that validates a running server end-to-end.

  1. Publish the server (from the root of the DotnetFastMCP project):

    dotnet publish -c Release -o ..\publish examples\BasicServer
    
  2. Run the tests: Open a PowerShell terminal and run the launcher script from the project root:

    .\LAUNCH_TESTS.ps1
    

This will open a new window, start the BasicServer, and run a series of tests covering all tools and resources, including error handling.

Example Manual Test

Auth0
builder.AddAuth0TokenVerifier();

Environment Variables:

FASTMCP_SERVER_AUTH_AUTH0_DOMAIN=your-tenant.auth0.com
FASTMCP_SERVER_AUTH_AUTH0_AUDIENCE=https://your-api-identifier
FASTMCP_SERVER_AUTH_AUTH0_CLIENT_ID=your-client-id
FASTMCP_SERVER_AUTH_AUTH0_CLIENT_SECRET=your-client-secret

Example: examples/Auth/Auth0OAuth

Okta
builder.AddOktaTokenVerifier();

Environment Variables:

FASTMCP_SERVER_AUTH_OKTA_DOMAIN=dev-123456.okta.com
FASTMCP_SERVER_AUTH_OKTA_AUDIENCE=api://default
FASTMCP_SERVER_AUTH_OKTA_CLIENT_ID=your-client-id
FASTMCP_SERVER_AUTH_OKTA_CLIENT_SECRET=your-client-secret

Example: examples/Auth/OktaOAuth

AWS Cognito
builder.AddAwsCognitoTokenVerifier();

Environment Variables:

FASTMCP_SERVER_AUTH_AWSCOGNITO_USER_POOL_ID=us-east-1_XXXXXXXXX
FASTMCP_SERVER_AUTH_AWSCOGNITO_REGION=us-east-1
FASTMCP_SERVER_AUTH_AWSCOGNITO_CLIENT_ID=your-app-client-id
FASTMCP_SERVER_AUTH_AWSCOGNITO_CLIENT_SECRET=your-app-client-secret
FASTMCP_SERVER_AUTH_AWSCOGNITO_DOMAIN=myapp.auth.us-east-1.amazoncognito.com

Example: examples/Auth/AwsCognitoOAuth

📚 Architecture

Project Structure

DotnetFastMCP/
├── src/
│   └── FastMCP/
│       ├── Attributes/              # Component declaration attributes
│       ├── Authentication/          # 🔐 OAuth providers & token verification
│       │   ├── Providers/          # Azure AD, Google, GitHub, Auth0, Okta, AWS
│       │   ├── Proxy/              # OAuth Proxy for DCR
│       │   └── Verification/       # JWT token validation
│       ├── Hosting/                 # Server hosting and middleware
│       ├── Protocol/                # JSON-RPC protocol implementation
│       └── Server/                  # FastMCPServer core class
├── examples/
│   ├── BasicServer/                 # Simple MCP server
│   └── Auth/                        # 🔐 Authentication examples
│       ├── AzureAdOAuth/           # Azure AD example
│       ├── GoogleOAuth/            # Google OAuth example
│       ├── GitHubOAuth/            # GitHub OAuth example
│       ├── Auth0OAuth/             # Auth0 example
│       ├── OktaOAuth/              # Okta example
│       └── AwsCognitoOAuth/        # AWS Cognito example
└── tests/
    └── McpIntegrationTest/          # Integration tests

Project Structure (Client)

The FastMCP framework now includes a complete client implementation in src/FastMCP/Client.

graph TD
    App[Your App] -->|Uses| Client[McpClient]
    Client -->|IClientTransport| Trans[Transport Layer]
    Trans -->|Stdio| Local[Local Process]
    Trans -->|SSE/HTTP| Remote[Remote Server]

Authentication Flow

sequenceDiagram
    participant Client
    participant MCP Server
    participant OAuth Provider
    
    Client->>MCP Server: Request with Bearer Token
    MCP Server->>Token Verifier: Validate Token
    Token Verifier->>OAuth Provider: Fetch JWKS (if needed)
    OAuth Provider-->>Token Verifier: Public Keys
    Token Verifier-->>MCP Server: Validated Claims
    MCP Server-->>Client: Protected Resource

🔧 Creating an MCP Server

Basic Server (No Authentication)

using FastMCP.Hosting;
using FastMCP.Server;
using System.Reflection;

var mcpServer = new FastMCPServer(name: "My MCP Server");
var builder = McpServerBuilder.Create(mcpServer, args);

builder.WithComponentsFrom(Assembly.GetExecutingAssembly());

var app = builder.Build();
await app.RunAsync();

Secure Server (With Authentication)

using FastMCP.Hosting;
using FastMCP.Server;
using System.Reflection;

var mcpServer = new FastMCPServer(name: "My Secure MCP Server");
var builder = McpServerBuilder.Create(mcpServer, args);

// Add authentication - automatically configures OAuth Proxy
builder.AddAzureAdTokenVerifier();  // or any other provider

builder.WithComponentsFrom(Assembly.GetExecutingAssembly());

var app = builder.Build();
app.Urls.Add("http://localhost:5002");
await app.RunMcpAsync(args);

Protected Tools

using FastMCP.Attributes;
using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;

public static class SecureTools
{
    /// <summary>
    /// Public tool - anyone can call
    /// </summary>
    [McpTool]
    public static string Echo(string message) => message;

    /// <summary>
    /// Protected tool - requires valid OAuth token
    /// </summary>
    [McpTool]
    [Authorize]
    public static object GetUserInfo(ClaimsPrincipal user)
    {
        return new
        {
            Name = user.Identity?.Name ?? "Unknown",
            Email = user.FindFirst("email")?.Value ?? "Not available",
            IsAuthenticated = user.Identity?.IsAuthenticated ?? false,
            Claims = user.Claims.Select(c => new { c.Type, c.Value }).ToList()
        };
    }

    /// <summary>
    /// Role-based authorization
    /// </summary>
    [McpTool]
    [Authorize(Roles = "Admin")]
    public static string AdminOnly() => "Admin access granted";
}

📡 JSON-RPC Protocol

Prompts

Prompts allow servers to provide templates that LLMs can use.

using FastMCP.Attributes;
using FastMCP.Protocol;

public static class MyPrompts
{
    [McpPrompt("analyze_code")]
    public static GetPromptResult Analyze(string code)
    {
        return new GetPromptResult
        {
            Description = "Analyze the given code",
            Messages = new List<PromptMessage>
            {
                new PromptMessage 
                { 
                    Role = "user", 
                    Content = new { type = "text", text = $"Please analyze this code:\n{code}" } 
                }
            }
        };
    }
}

Calling Tools

Public Tool (No Auth):

POST /mcp
{
  "jsonrpc": "2.0",
  "method": "Echo",
  "params": ["Hello World"],
  "id": 1
}

Protected Tool (With Auth):

POST /mcp
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc...

{
  "jsonrpc": "2.0",
  "method": "GetUserInfo",
  "params": [],
  "id": 2
}

🧪 Testing

Run All Tests

dotnet test

Test Authentication Flow

Each authentication example includes a comprehensive .rest file for testing:

# Open in VS Code with REST Client extension
code examples/Auth/AzureAdOAuth/azure-ad-auth-tests.rest

Test files include:

  • ✅ Discovery endpoints
  • ✅ Public tool tests
  • ✅ Protected tool tests (should fail without auth)
  • ✅ OAuth authorization flow
  • ✅ Token exchange
  • ✅ Provider-specific API calls

📖 Documentation

Guides & Features

Complete Authentication Guide

See MFA Support Guide for enforcing Multi-Factor Authentication on sensitive tools, and the individual provider README files under examples/Auth/ for detailed OAuth setup instructions.

Example Projects

ExampleDescriptionPort
BasicServerSimple MCP server with Auto-DI & [McpDescription]5000
HealthChecksDemo🏥 Health monitoring & diagnostics demo5000
TelemetryDemo📡 OpenTelemetry metrics & tracing demo5000
AzureAdOAuthAzure AD authentication example5002
GoogleOAuthGoogle OAuth example5000
GitHubOAuthGitHub OAuth example5001
Auth0OAuthAuth0 authentication example5005
OktaOAuthOkta authentication example5007
AwsCognitoOAuthAWS Cognito example5006

🏗️ Advanced Features

⚡ Automatic DI Registration & [McpDescription] (NEW! v2.1.0)

DotnetFastMCP 2.1 makes authoring production MCP servers completely zero-boilerplate by pairing automatic Dependency Injection with LLM-grade parameter schemas:

  1. Zero-Config DI: Non-static tool, resource, and prompt classes scanned with WithComponentsFrom() are automatically registered as Transient into ASP.NET Core DI. No more manual builder.Services.AddTransient<OrderTools>() lines.
  2. Preserves Custom Lifetimes: Built on TryAddTransient semantics, so any class explicitly registered as Singleton or Scoped in builder.Services retains its desired lifetime.
  3. [McpDescription] for Parameters: Annotate tool parameters with descriptions that are exposed directly in the JSON Schema inputSchema (tools/list), giving LLMs exact semantic context and eliminating hallucinated arguments.
  4. Framework Parameter Exclusion: Types such as McpContext, CancellationToken, ClaimsPrincipal, and IMcpSession are automatically filtered out from the public schema.
public class OrderTools
{
    private readonly IOrderRepository _repository;
    private readonly ILogger<OrderTools> _logger;

    // Injected automatically via ASP.NET Core DI
    public OrderTools(IOrderRepository repository, ILogger<OrderTools> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    [McpTool(Description = "Retrieves order status by order identifier and country")]
    public async Task<string> GetOrderStatus(
        [McpDescription("Unique order ID, e.g. ORD-98765")] string orderId,
        [McpDescription("Two-letter country code, e.g. US, UK")] string countryCode = "US",
        CancellationToken ct = default) // Framework types are automatically excluded from the tool schema
    {
        _logger.LogInformation("Fetching order {OrderId} in {Country}", orderId, countryCode);
        return await _repository.GetStatusAsync(orderId, countryCode, ct);
    }
}
// Program.cs - Zero boilerplate registration!
var server = new FastMCPServer("OrderServer");
var builder = McpServerBuilder.Create(server, args);

// Automatically registers OrderTools as Transient, discovers [McpTool], and configures schemas!
builder.WithComponentsFrom(Assembly.GetExecutingAssembly());

var app = builder.Build();
await app.RunMcpAsync(args);

🏥 Health Checks & Diagnostics (v1.15.0)

FastMCP ships with a built-in production health check endpoint. Enable with one line and plug in any custom check as a simple lambda.

using FastMCP.Health;

// Zero-config — exposes GET /mcp/health automatically
builder.WithHealthChecks();

// With custom checks (database, LLM provider, memory, etc.)
builder.WithHealthChecks(checks =>
{
    checks.AddCheck("memory", () =>
        GC.GetTotalMemory(false) < 500_000_000L); // sync: < 500 MB

    checks.AddAsyncCheck("database", async ct =>
        await dbContext.Database.CanConnectAsync(ct));

    checks.AddAsyncCheck("llm_provider", async ct =>
        await llmProvider.IsHealthyAsync(ct));
});

Response JSON (HTTP 200 — Healthy):

{
  "status": "Healthy",
  "timestamp": "2026-04-19T20:00:00Z",
  "checks": [
    { "name": "mcp_server",   "status": "Healthy", "durationMs": 0 },
    { "name": "memory",       "status": "Healthy", "durationMs": 0.1 },
    { "name": "database",     "status": "Healthy", "durationMs": 4.9 },
    { "name": "llm_provider", "status": "Healthy", "durationMs": 22.3 }
  ],
  "diagnostics": {
    "serverName": "my-mcp-server",
    "frameworkVersion": "1.15.0.0",
    "toolCount": 12,
    "uptimeSeconds": 3721.4
  }
}

HTTP status code mapping:

StatusHTTP CodeMeaning
Healthy200All checks passed
Degraded207Server up, ≥1 check timed out
Unhealthy503≥1 check failed or threw

Kubernetes liveness / readiness probe:

livenessProbe:
  httpGet:
    path: /mcp/health
    port: 5000
  initialDelaySeconds: 15
  periodSeconds: 30
readinessProbe:
  httpGet:
    path: /mcp/health
    port: 5000
  periodSeconds: 10

See Health Checks Guide for full documentation, including Docker Compose, Azure Container Apps, per-check timeout configuration, unit testing patterns, and complete validation examples.


📡 Observability — OpenTelemetry (v1.14.0)

FastMCP ships with built-in OpenTelemetry instrumentation. Enable with one line and connect to any backend.

using FastMCP.Telemetry;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;

// 1. Enable FastMCP telemetry (one line)
builder.WithTelemetry(t =>
{
    t.ServiceName    = "my-mcp-server";
    t.EnableMetrics  = true;
    t.EnableTracing  = true;
});

// 2. Configure your exporter of choice
builder.Services.AddOpenTelemetry()
    .WithMetrics(m =>
    {
        m.AddMcpInstrumentation();  // FastMCP extension method
        m.AddPrometheusExporter();  // or AddConsoleExporter(), AddOtlpExporter()
    })
    .WithTracing(t =>
    {
        t.AddMcpInstrumentation();  // FastMCP extension method
        t.AddOtlpExporter();        // or AddJaeger(), AddZipkin()
    });

Metrics automatically tracked:

MetricTypeTagDescription
mcp.tool.invocationsCountertool.nameTotal tool calls
mcp.tool.durationHistogram (ms)tool.nameTool execution time
mcp.tool.errorsCountertool.nameFailed tool calls
mcp.prompt.requestsCounter—Prompt template requests
mcp.resource.readsCounter—Resource read requests

Validate with dotnet-counters (no exporter needed):

dotnet-counters monitor -n YourAppName --counters FastMCP

See Observability Guide for full documentation, including production exporter setup, distributed tracing details, and real request/response validation examples.


Middleware Interception

Middleware allows you to intercept and modify JSON-RPC messages (requests and responses) flowing through the server pipeline. This is useful for logging, validation, modification, or custom monitoring.

  1. Define Middleware: Implement IMcpMiddleware.
  2. Register Middleware: Use builder.AddMcpMiddleware<T>().
public class LoggingMiddleware : IMcpMiddleware
{
    public async Task<JsonRpcResponse> InvokeAsync(McpMiddlewareContext context, McpMiddlewareDelegate next, CancellationToken ct)
    {
        Console.Error.WriteLine($"[LOG] Incoming: {context.Request.Method}");
        
        // Pass to next handler
        var response = await next(context, ct);
        
        Console.Error.WriteLine($"[LOG] Completed. Error: {response.Error != null}");
        return response;
    }
}

// In Program.cs:
builder.AddMcpMiddleware<LoggingMiddleware>();

Server Composition (NEW!)

Mount other MCP servers into your main server instantiation. This supports a "Micro-MCP" architecture where you can compose a robust agent from smaller, focused modules.

// 1. Create Sub-Server (e.g. GitHub Tools)
var githubServer = new FastMCPServer("GitHub");
// ... register tools ...

// 2. Import into Main Server with "gh" prefix
builder.AddServer(githubServer, prefix: "gh");

// Result:
// The client sees tools named: "gh_create_issue", "gh_get_repo", etc.

MFA Support (NEW!)

Enforce Multi-Factor Authentication for sensitive tools.

[McpTool("transfer_funds")]
[AuthorizeMcpTool(RequireMfa = true)]
public static string TransferFunds()
{
    return "Transferred!";
}
  • MFA Check: Verifies amr claim contains mfa.
  • Security: Provides granular protection for critical operations.

Storage Abstraction (NEW!)

FastMCP now includes a built-in state persistence layer. Tools can request McpContext to access IMcpStorage.

[McpTool]
public static async Task<string> SetValue(string key, string value, McpContext context)
{
    await context.Storage.SetAsync(key, value);
    return "Saved!";
}

The default implementation is In-Memory, but you can swap it for Redis, SQL, or File storage:

builder.AddMcpStorage<MyRedisStorage>();

LLM Integration (NEW!)

FastMCP includes a powerful LLM integration system with 8 providers supporting the latest models (Feb 2026).

Quick Setup

using FastMCP.AI;

// Option 1: Local (Ollama)
builder.AddOllamaProvider(options =>
{
    options.BaseUrl = "http://localhost:11434";
    options.DefaultModel = "llama3.1:8b";
});

// Option 2: Cloud (Anthropic Claude Opus 4.6 - Latest)
builder.AddAnthropicProvider(options =>
{
    options.ApiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY")!;
    options.DefaultModel = "claude-opus-4.6"; // 1M context, Feb 2026
});

// Option 3: Google Gemini 3
builder.AddGeminiProvider(options =>
{
    options.ApiKey = Environment.GetEnvironmentVariable("GEMINI_API_KEY")!;
    options.DefaultModel = "gemini-3-flash"; // Fast, cost-effective
});

Use in Tools

public class AITools
{
    private readonly ILLMProvider _llm;

    public AITools(ILLMProvider llm) => _llm = llm;

    [McpTool("generate_story")]
    public async Task<string> GenerateStory(string topic)
    {
        return await _llm.GenerateAsync(
            $"Write a story about {topic}",
            new LLMGenerationOptions
            {
                SystemPrompt = "You are a creative storyteller.",
                Temperature = 0.8,
                MaxTokens = 500
            });
    }

    [McpTool("stream_response")]
    public async IAsyncEnumerable<string> StreamResponse(string prompt)
    {
        await foreach (var token in _llm.StreamAsync(prompt))
        {
            yield return token;
        }
    }
}

Supported Providers (Feb 2026)

ProviderExtension MethodLatest ModelBest For
OllamaAddOllamaProvider()llama3.1:8bLocal, privacy, offline
OpenAIAddOpenAIProvider()gpt-4-turboProduction, function calling
Azure OpenAIAddAzureOpenAIProvider()gpt-4Enterprise, compliance
AnthropicAddAnthropicProvider()claude-opus-4.6Deep reasoning, 1M context
Google GeminiAddGeminiProvider()gemini-3-flashMultimodal, high-volume
CohereAddCohereProvider()command-aEnterprise RAG, agents
Hugging FaceAddHuggingFaceProvider()Any modelOpen-source, flexibility
DeepseekAddDeepseekProvider()deepseek-v3.2Cost-effective, reasoning

See LLM Integration Guide for complete documentation.

Background Tasks (NEW!)

FastMCP allows tools to fire-and-forget long running operations using RunInBackground.

[McpTool]
public static async Task<string> ProcessFile(string file, McpContext context)
{
    await context.RunInBackground(async (ct) => 
    {
        // This runs without blocking the client
        await HeavyProcessing(file, ct);
    });

    return "Processing started!";
}

Icons Support (NEW!)

Enhance the user interface of clients by providing icons for your server and tools.

// Server Icon
server.Icon = "https://myserver.com/logo.png";

// Tool Icon
[McpTool(Icon = "https://myserver.com/tools/calc.png")]
public static int Add(int a, int b) => a + b;

Binary Content Support (NEW!)

Return rich content like Images from your tools and prompts.

[McpTool]
public static CallToolResult GetSnapshot()
{
    return new CallToolResult 
    {
        Content = new List<ContentItem> 
        {
            new ImageContent { Data = "base64...", MimeType = "image/png" }
        }
    };
}

OAuth Proxy

DotnetFastMCP includes a built-in OAuth Proxy that provides:

  • ✅ Dynamic Client Registration (DCR) - Automatic client registration for MCP clients
  • ✅ Authorization Code Flow - Full OAuth 2.0 authorization code flow with PKCE
  • ✅ Token Management - Automatic token exchange, refresh, and revocation
  • ✅ Discovery Endpoints - RFC 8414 compliant OAuth discovery

Automatically Available Endpoints:

  • /.well-known/oauth-authorization-server - OAuth server metadata
  • /oauth/authorize - Authorization endpoint
  • /oauth/token - Token endpoint
  • /oauth/register - Dynamic client registration
  • /oauth/userinfo - User information endpoint

Custom Scopes

Override default scopes for any provider:

builder.AddAzureAdTokenVerifier(new AzureAdAuthOptions
{
    RequiredScopes = new[] { "openid", "profile", "email", "User.Read", "Calendars.Read" }
});

Multiple Authentication Schemes

// Support multiple providers simultaneously
builder.AddAzureAdTokenVerifier();
builder.AddGoogleTokenVerifier();
builder.AddGitHubTokenVerifier();

🔐 Security Best Practices

Development

  • ✅ Use environment variables for secrets
  • ✅ Never commit credentials to source control
  • ✅ Use .env files for local development
  • ✅ Test with short-lived tokens

Production

  • ✅ Use HTTPS for all communication
  • ✅ Store secrets in Azure Key Vault / AWS Secrets Manager
  • ✅ Enable MFA for OAuth providers
  • ✅ Implement rate limiting
  • ✅ Monitor authentication logs
  • ✅ Use separate app registrations per environment
  • ✅ Validate token scopes match required permissions

📦 NuGet Package

Install from NuGet (when published):

dotnet add package DotnetFastMCP

🤝 Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🔗 Resources

Official Documentation

Framework Documentation

Provider Documentation

🐛 Issues & Support

For bug reports and feature requests, please use GitHub Issues.

✨ What's New

v2.1.1 - Client Deserialization Patch (Latest - Sep 2026)

  • 🐛 Fix McpClient.CallToolAsync<TResult> Deserialization - Resolved deserialization error where calling tools returning primitive types (int, bool, double, etc.), string, or custom POCO models threw JSON conversion errors (fixes #37).
  • 📦 Automatic Envelope Unwrapping - Correctly unwraps and deserializes the inner payload from CallToolResult.Content while maintaining full MCP specification compliance.
  • ⚡ Direct Envelope Overload - Added non-generic client.CallToolAsync("toolName", args) returning raw CallToolResult directly.
  • 🧪 Comprehensive Test Coverage - Added unit and integration test suites validating primitive, string, and complex model deserialization.

v2.1.0 - Zero-Boilerplate MCP Servers (Sep 2026)

  • ⚡ Automatic DI Registration - Non-static classes containing [McpTool], [McpResource], or [McpPrompt] are automatically registered as Transient during WithComponentsFrom(). No manual builder.Services.AddTransient<T>() boilerplate required.
  • 🛡️ Lifespan Safety - Implemented via TryAddTransient so custom Singleton or Scoped registrations configured in builder.Services are never overwritten.
  • 📝 [McpDescription] Parameter Attribute - Tool parameters annotated with [McpDescription] have their documentation automatically rendered into JSON Schema properties.<param>.description in tools/list.
  • 🧼 Clean Schema Generation - Framework types (McpContext, CancellationToken, ClaimsPrincipal, IMcpSession) are automatically excluded from tools/list schema definitions, preventing LLM argument errors.
  • 🧪 57 Tests Passing - Dual-targeted unit and integration test suite passing across both .NET 8 LTS and .NET 10 LTS.
  • 📖 Comprehensive Guide - Detailed documentation in docs/auto-di-registration-guide.md.

v2.0.0 - .NET 10 LTS & .NET 8 LTS Dual Support (Aug 2026)

  • 🚀 Dual-Targeting - Ships both net8.0 and net10.0 binaries in a single package.
  • 🔒 Zero Breaking Changes - 100% backward compatible for existing .NET 8 applications.
  • ⚡ High-Performance Non-Blocking Async Streams - SSE parser compliant with .NET 10 CA2024 rules.
  • 🧪 Comprehensive Test Matrix - Unit & in-memory integration tests running across both target frameworks.

v1.15.0 - Health Checks & Diagnostics (Apr 2026)

  • 🏥 Built-In Health Endpoint - GET /mcp/health exposed with a single builder.WithHealthChecks() call
  • 🔌 Lambda-Based Custom Checks - Add any check (database, llm, memory, external API) as a simple lambda with no interface to implement
  • ⚡ Parallel Execution - All checks run concurrently; a slow check never delays a fast one
  • ⏱️ Per-Check Timeout - Configurable MaxResponseTimeMs; hanging checks reported as Degraded, not left blocking
  • 🌐 Standard HTTP Status Codes - 200 Healthy / 207 Degraded / 503 Unhealthy; understood natively by Kubernetes, load balancers, and APM tools
  • 📊 Auto Server Diagnostics - Automatically includes server name, framework version, tool/resource/prompt counts, and uptime
  • 🛡️ Always Reachable - Endpoint marked AllowAnonymous() so infrastructure probes bypass authentication
  • 🎯 Zero Overhead - Fully opt-in; endpoint is not registered unless WithHealthChecks() is called
  • 📚 Comprehensive Docs - Full guide covering Kubernetes, Docker, ACA probes, validation walkthrough, and unit tests

v1.14.0 - OpenTelemetry Observability (Mar 2026)

  • 📡 OpenTelemetry Integration - First-class metrics and distributed tracing built in
  • 📊 5 Auto-Tracked Metrics - Tool invocations, duration, errors, prompt requests, resource reads
  • ✨ One-Line Setup - builder.WithTelemetry() with zero boilerplate
  • 🔌 Exporter Agnostic - Works with Prometheus, App Insights, Grafana, Jaeger, any OTLP backend
  • 🔍 Distributed Tracing - Full span support with OTel semantic convention tags
  • 🛡️ PII Safe Defaults - Tool inputs never logged unless explicitly enabled
  • 🎯 Zero Overhead - Fully opt-in, no cost when not used
  • 📚 Comprehensive Docs - Full guide with validation examples and production checklist

v1.13.0 - LLM Integration (Feb 2026)

  • 🤖 8 LLM Providers - Ollama, OpenAI, Azure OpenAI, Anthropic Claude, Google Gemini, Cohere, Hugging Face, Deepseek
  • ✨ Latest Models - Claude Opus 4.6 (1M context), Gemini 3 Pro/Flash, Command A, DeepSeek V3.2
  • 🔌 Unified Interface - Single ILLMProvider API for all providers
  • 📡 Streaming Support - Real-time token streaming with IAsyncEnumerable<string>
  • 🏗️ Production-Ready - HttpClientFactory, Polly retry policies, connection pooling
  • 🎯 Plug-and-Play - Simple registration: builder.AddAnthropicProvider()
  • 📚 Comprehensive Docs - Complete integration guide with examples

v1.12.0 - MFA Support

  • 🛡️ MFA Enforcement - Require mfa AMR claim for sensitive tools
  • ✅ Granular Control - Enable per-tool using [AuthorizeMcpTool(RequireMfa=true)]
  • 🔒 Enhanced Security - Standards-based multi-factor authentication check

v1.11.0 - Binary Content Support

  • ✅ Polymorphic Content - Support for mixed Text and Image responses
  • ✅ Image Support - Return Base64 encoded images from tools
  • ✅ Multimodal Prompts - Embbed images in prompts for LLM context

v1.10.0 - Icons Support

  • ✅ Server Icons - Define a brand icon for your MCP server
  • ✅ Tool/Resource Icons - Visually distinguish capabilities
  • ✅ UI/UX Enhancement - Enable richer client experiences

v1.9.0 - Background Tasks

  • ✅ Fire-and-Forget - Offload long-running operations from tools
  • ✅ Non-Blocking - Return immediate responses to clients
  • ✅ Hosted Service - Built-in queuing mechanism using Channels

v1.8.0 - Storage Abstractions

  • ✅ State Persistence - Tools can now persist data via McpContext.Storage
  • ✅ Pluggable Backends - Swap in Redis/SQL/File storage easily
  • ✅ In-Memory Default - Zero-config built-in storage for development

v1.7.0 - Server Composition

  • ✅ Server Composition - Mount other MCP servers as modules (Micro-MCPs)
  • ✅ Namespacing - Automatically prefix imported tools (e.g., github_createIssue)
  • ✅ Zero-Overhead - High-performance internal dictionary routing (O(1))

v1.6.0 - Middleware Interception

  • ✅ Middleware Pipeline - Intercept and modify requests/responses
  • ✅ Critical Fixes - Resolved Stdio transport initialization deadlocks
  • ✅ Builder API - Easy registration with AddMcpMiddleware<T>

v1.5.0 - Native Client Library

  • ✅ McpClient - Type-safe .NET client for consuming MCP servers
  • ✅ Transport Agnostic - Support for both Stdio and SSE connections
  • ✅ Notification Handling - Events for real-time logs and progress

v1.4.0 - Server-Sent Events (SSE)

  • ✅ SSE Transport - Real-time server-to-client streaming transport
  • ✅ Async Notifications - Push logs and progress updates to HTTP clients

v1.3.0 - Context & Interaction

  • ✅ Context System - McpContext injection for logging and progress
  • ✅ IMcpSession - Transport-agnostic interaction abstraction

v1.2.0 - Protocol Discovery

  • ✅ Dynamic Discovery - Auto-discovery of Tools, Resources, and Prompts
  • ✅ Prompts/List - Full support for prompt templates

v1.1.0 - Stdio Transport & Authentication

  • ✅ Stdio Transport - Initial support for stdio communication
  • 🔐 6 OAuth Providers - Azure AD, Google, GitHub, Auth0, Okta, AWS Cognito
  • 🔐 OAuth Proxy - Built-in DCR support

v1.0.0 - Core Framework

  • ✅ Attribute-based API
  • ✅ JSON-RPC 2.0 compliance
  • ✅ ASP.NET Core integration

Made with ❤️ by the DotnetFastMCP team

⭐ Star this repo if you find it useful!

ai-agent
aspnetcore
claude
csharp
dotnet-core
genrative-ai
llms
mcp-gateway
microservice
model-context-protocol
open-ai
sdk
semantic-kernel
tooling

Languages

C#

98.8%

PowerShell

1.2%