A lightweight .NET framework for building Model Context Protocol (MCP) servers. Integrates seamlessly with Azure AD, AWS Cognito, Auth0, OpenAI and other providers
C#
6
82 commits
updated Sep 13, 2026
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.
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.
WithComponentsFrom() are automatically registered as Transient services in the DI container. Zero manual builder.Services.AddTransient<T>() boilerplate.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.McpContext, CancellationToken, ClaimsPrincipal, IMcpSession) from schema exposure so LLMs only see valid user inputs.net8.0 and net10.0 binaries in a single package[McpTool] and [McpResource] attributes[McpPrompt] for LLM interaction templates[Authorize] attributeCallToolAsync<T> APIILLMProvider API for all providersIAsyncEnumerable<string>builder.AddAnthropicProvider()builder.WithTelemetry() — zero boilerplateGET /mcp/health exposed automaticallybuilder.WithHealthChecks() — no configuration requiredInstall 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
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;
}
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);
cd examples/BasicServer
dotnet run
The server will start on http://localhost:5000.
Real-world enterprise projects that demonstrate DotnetFastMCP in production:
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:
VisionMcpServer, PromptMcpServer, ImageMcpServer, InpaintingMcpServer, VideoMcpServerOrchestratorAgent (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)
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 | Purpose |
|---|---|
FastMCP | Core framework library |
FastMCP.CLI | Command-line interface tools |
BasicServer | Example MCP server implementation |
McpIntegrationTest | Integration tests |
ClientDemo | Example Client consuming BasicServer |
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
};
}
}
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();
# 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"
dotnet run
Your server is now running with OAuth Proxy endpoints:
http://localhost:5002/mcphttp://localhost:5002/oauth/authorizehttp://localhost:5002/oauth/tokenhttp://localhost:5002/.well-known/oauth-authorization-serverYou can also run the server in Stdio mode (for local LLM clients):
dotnet run -- --stdio
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 });
DotnetFastMCP supports 6 enterprise-grade OAuth providers out of the box:
| Provider | Method | Use Case | Default Scopes |
|---|---|---|---|
| Azure AD | AddAzureAdTokenVerifier() | Enterprise apps, Microsoft 365 | openid, profile, email, offline_access |
AddGoogleTokenVerifier() | Consumer apps, Google Workspace | openid, profile, email, userinfo.profile | |
| GitHub | AddGitHubTokenVerifier() | Developer tools, repositories | read:user, user:email |
| Auth0 | AddAuth0TokenVerifier() | Multi-tenant SaaS, custom identity | openid, profile, email, offline_access |
| Okta | AddOktaTokenVerifier() | Enterprise SSO, workforce identity | openid, profile, email, offline_access |
| AWS Cognito | AddAwsCognitoTokenVerifier() | AWS-native apps, user pools | openid, profile, email |
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
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
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
The project includes a comprehensive PowerShell-based integration test suite that validates a running server end-to-end.
Publish the server (from the root of the DotnetFastMCP project):
dotnet publish -c Release -o ..\publish examples\BasicServer
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.
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
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
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
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
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]
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
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();
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);
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";
}
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}" }
}
}
};
}
}
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
}
dotnet test
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:
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 | Description | Port |
|---|---|---|
| BasicServer | Simple MCP server with Auto-DI & [McpDescription] | 5000 |
| HealthChecksDemo | 🏥 Health monitoring & diagnostics demo | 5000 |
| TelemetryDemo | 📡 OpenTelemetry metrics & tracing demo | 5000 |
| AzureAdOAuth | Azure AD authentication example | 5002 |
| GoogleOAuth | Google OAuth example | 5000 |
| GitHubOAuth | GitHub OAuth example | 5001 |
| Auth0OAuth | Auth0 authentication example | 5005 |
| OktaOAuth | Okta authentication example | 5007 |
| AwsCognitoOAuth | AWS Cognito example | 5006 |
DotnetFastMCP 2.1 makes authoring production MCP servers completely zero-boilerplate by pairing automatic Dependency Injection with LLM-grade parameter schemas:
WithComponentsFrom() are automatically registered as Transient into ASP.NET Core DI. No more manual builder.Services.AddTransient<OrderTools>() lines.TryAddTransient semantics, so any class explicitly registered as Singleton or Scoped in builder.Services retains its desired lifetime.[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.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);
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:
| Status | HTTP Code | Meaning |
|---|---|---|
Healthy | 200 | All checks passed |
Degraded | 207 | Server up, ≥1 check timed out |
Unhealthy | 503 | ≥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.
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:
| Metric | Type | Tag | Description |
|---|---|---|---|
mcp.tool.invocations | Counter | tool.name | Total tool calls |
mcp.tool.duration | Histogram (ms) | tool.name | Tool execution time |
mcp.tool.errors | Counter | tool.name | Failed tool calls |
mcp.prompt.requests | Counter | — | Prompt template requests |
mcp.resource.reads | Counter | — | 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 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.
IMcpMiddleware.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>();
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.
Enforce Multi-Factor Authentication for sensitive tools.
[McpTool("transfer_funds")]
[AuthorizeMcpTool(RequireMfa = true)]
public static string TransferFunds()
{
return "Transferred!";
}
amr claim contains mfa.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>();
FastMCP includes a powerful LLM integration system with 8 providers supporting the latest models (Feb 2026).
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
});
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;
}
}
}
| Provider | Extension Method | Latest Model | Best For |
|---|---|---|---|
| Ollama | AddOllamaProvider() | llama3.1:8b | Local, privacy, offline |
| OpenAI | AddOpenAIProvider() | gpt-4-turbo | Production, function calling |
| Azure OpenAI | AddAzureOpenAIProvider() | gpt-4 | Enterprise, compliance |
| Anthropic | AddAnthropicProvider() | claude-opus-4.6 | Deep reasoning, 1M context |
| Google Gemini | AddGeminiProvider() | gemini-3-flash | Multimodal, high-volume |
| Cohere | AddCohereProvider() | command-a | Enterprise RAG, agents |
| Hugging Face | AddHuggingFaceProvider() | Any model | Open-source, flexibility |
| Deepseek | AddDeepseekProvider() | deepseek-v3.2 | Cost-effective, reasoning |
See LLM Integration Guide for complete documentation.
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!";
}
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;
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" }
}
};
}
DotnetFastMCP includes a built-in OAuth Proxy that provides:
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 endpointOverride default scopes for any provider:
builder.AddAzureAdTokenVerifier(new AzureAdAuthOptions
{
RequiredScopes = new[] { "openid", "profile", "email", "User.Read", "Calendars.Read" }
});
// Support multiple providers simultaneously
builder.AddAzureAdTokenVerifier();
builder.AddGoogleTokenVerifier();
builder.AddGitHubTokenVerifier();
.env files for local developmentInstall from NuGet (when published):
dotnet add package DotnetFastMCP
Contributions are welcome! Please:
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)This project is licensed under the MIT License - see the LICENSE file for details.
For bug reports and feature requests, please use GitHub Issues.
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).CallToolResult.Content while maintaining full MCP specification compliance.client.CallToolAsync("toolName", args) returning raw CallToolResult directly.[McpTool], [McpResource], or [McpPrompt] are automatically registered as Transient during WithComponentsFrom(). No manual builder.Services.AddTransient<T>() boilerplate required.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.McpContext, CancellationToken, ClaimsPrincipal, IMcpSession) are automatically excluded from tools/list schema definitions, preventing LLM argument errors.docs/auto-di-registration-guide.md.net8.0 and net10.0 binaries in a single package.GET /mcp/health exposed with a single builder.WithHealthChecks() calldatabase, llm, memory, external API) as a simple lambda with no interface to implementMaxResponseTimeMs; hanging checks reported as Degraded, not left blockingAllowAnonymous() so infrastructure probes bypass authenticationWithHealthChecks() is calledbuilder.WithTelemetry() with zero boilerplateILLMProvider API for all providersIAsyncEnumerable<string>builder.AddAnthropicProvider()mfa AMR claim for sensitive tools[AuthorizeMcpTool(RequireMfa=true)]McpContext.Storagegithub_createIssue)AddMcpMiddleware<T>McpContext injection for logging and progressMade with ❤️ by the DotnetFastMCP team
⭐ Star this repo if you find it useful!
C#
98.8%
PowerShell
1.2%
A lightweight .NET framework for building Model Context Protocol (MCP) servers. Integrates seamlessly with Azure AD, AWS Cognito, Auth0, OpenAI and other providers
C#
6
82 commits
updated Sep 13, 2026
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.
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.
WithComponentsFrom() are automatically registered as Transient services in the DI container. Zero manual builder.Services.AddTransient<T>() boilerplate.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.McpContext, CancellationToken, ClaimsPrincipal, IMcpSession) from schema exposure so LLMs only see valid user inputs.net8.0 and net10.0 binaries in a single package[McpTool] and [McpResource] attributes[McpPrompt] for LLM interaction templates[Authorize] attributeCallToolAsync<T> APIILLMProvider API for all providersIAsyncEnumerable<string>builder.AddAnthropicProvider()builder.WithTelemetry() — zero boilerplateGET /mcp/health exposed automaticallybuilder.WithHealthChecks() — no configuration requiredInstall 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
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;
}
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);
cd examples/BasicServer
dotnet run
The server will start on http://localhost:5000.
Real-world enterprise projects that demonstrate DotnetFastMCP in production:
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:
VisionMcpServer, PromptMcpServer, ImageMcpServer, InpaintingMcpServer, VideoMcpServerOrchestratorAgent (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)
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 | Purpose |
|---|---|
FastMCP | Core framework library |
FastMCP.CLI | Command-line interface tools |
BasicServer | Example MCP server implementation |
McpIntegrationTest | Integration tests |
ClientDemo | Example Client consuming BasicServer |
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
};
}
}
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();
# 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"
dotnet run
Your server is now running with OAuth Proxy endpoints:
http://localhost:5002/mcphttp://localhost:5002/oauth/authorizehttp://localhost:5002/oauth/tokenhttp://localhost:5002/.well-known/oauth-authorization-serverYou can also run the server in Stdio mode (for local LLM clients):
dotnet run -- --stdio
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 });
DotnetFastMCP supports 6 enterprise-grade OAuth providers out of the box:
| Provider | Method | Use Case | Default Scopes |
|---|---|---|---|
| Azure AD | AddAzureAdTokenVerifier() | Enterprise apps, Microsoft 365 | openid, profile, email, offline_access |
AddGoogleTokenVerifier() | Consumer apps, Google Workspace | openid, profile, email, userinfo.profile | |
| GitHub | AddGitHubTokenVerifier() | Developer tools, repositories | read:user, user:email |
| Auth0 | AddAuth0TokenVerifier() | Multi-tenant SaaS, custom identity | openid, profile, email, offline_access |
| Okta | AddOktaTokenVerifier() | Enterprise SSO, workforce identity | openid, profile, email, offline_access |
| AWS Cognito | AddAwsCognitoTokenVerifier() | AWS-native apps, user pools | openid, profile, email |
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
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
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
The project includes a comprehensive PowerShell-based integration test suite that validates a running server end-to-end.
Publish the server (from the root of the DotnetFastMCP project):
dotnet publish -c Release -o ..\publish examples\BasicServer
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.
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
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
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
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
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]
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
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();
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);
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";
}
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}" }
}
}
};
}
}
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
}
dotnet test
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:
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 | Description | Port |
|---|---|---|
| BasicServer | Simple MCP server with Auto-DI & [McpDescription] | 5000 |
| HealthChecksDemo | 🏥 Health monitoring & diagnostics demo | 5000 |
| TelemetryDemo | 📡 OpenTelemetry metrics & tracing demo | 5000 |
| AzureAdOAuth | Azure AD authentication example | 5002 |
| GoogleOAuth | Google OAuth example | 5000 |
| GitHubOAuth | GitHub OAuth example | 5001 |
| Auth0OAuth | Auth0 authentication example | 5005 |
| OktaOAuth | Okta authentication example | 5007 |
| AwsCognitoOAuth | AWS Cognito example | 5006 |
DotnetFastMCP 2.1 makes authoring production MCP servers completely zero-boilerplate by pairing automatic Dependency Injection with LLM-grade parameter schemas:
WithComponentsFrom() are automatically registered as Transient into ASP.NET Core DI. No more manual builder.Services.AddTransient<OrderTools>() lines.TryAddTransient semantics, so any class explicitly registered as Singleton or Scoped in builder.Services retains its desired lifetime.[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.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);
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:
| Status | HTTP Code | Meaning |
|---|---|---|
Healthy | 200 | All checks passed |
Degraded | 207 | Server up, ≥1 check timed out |
Unhealthy | 503 | ≥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.
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:
| Metric | Type | Tag | Description |
|---|---|---|---|
mcp.tool.invocations | Counter | tool.name | Total tool calls |
mcp.tool.duration | Histogram (ms) | tool.name | Tool execution time |
mcp.tool.errors | Counter | tool.name | Failed tool calls |
mcp.prompt.requests | Counter | — | Prompt template requests |
mcp.resource.reads | Counter | — | 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 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.
IMcpMiddleware.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>();
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.
Enforce Multi-Factor Authentication for sensitive tools.
[McpTool("transfer_funds")]
[AuthorizeMcpTool(RequireMfa = true)]
public static string TransferFunds()
{
return "Transferred!";
}
amr claim contains mfa.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>();
FastMCP includes a powerful LLM integration system with 8 providers supporting the latest models (Feb 2026).
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
});
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;
}
}
}
| Provider | Extension Method | Latest Model | Best For |
|---|---|---|---|
| Ollama | AddOllamaProvider() | llama3.1:8b | Local, privacy, offline |
| OpenAI | AddOpenAIProvider() | gpt-4-turbo | Production, function calling |
| Azure OpenAI | AddAzureOpenAIProvider() | gpt-4 | Enterprise, compliance |
| Anthropic | AddAnthropicProvider() | claude-opus-4.6 | Deep reasoning, 1M context |
| Google Gemini | AddGeminiProvider() | gemini-3-flash | Multimodal, high-volume |
| Cohere | AddCohereProvider() | command-a | Enterprise RAG, agents |
| Hugging Face | AddHuggingFaceProvider() | Any model | Open-source, flexibility |
| Deepseek | AddDeepseekProvider() | deepseek-v3.2 | Cost-effective, reasoning |
See LLM Integration Guide for complete documentation.
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!";
}
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;
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" }
}
};
}
DotnetFastMCP includes a built-in OAuth Proxy that provides:
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 endpointOverride default scopes for any provider:
builder.AddAzureAdTokenVerifier(new AzureAdAuthOptions
{
RequiredScopes = new[] { "openid", "profile", "email", "User.Read", "Calendars.Read" }
});
// Support multiple providers simultaneously
builder.AddAzureAdTokenVerifier();
builder.AddGoogleTokenVerifier();
builder.AddGitHubTokenVerifier();
.env files for local developmentInstall from NuGet (when published):
dotnet add package DotnetFastMCP
Contributions are welcome! Please:
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)This project is licensed under the MIT License - see the LICENSE file for details.
For bug reports and feature requests, please use GitHub Issues.
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).CallToolResult.Content while maintaining full MCP specification compliance.client.CallToolAsync("toolName", args) returning raw CallToolResult directly.[McpTool], [McpResource], or [McpPrompt] are automatically registered as Transient during WithComponentsFrom(). No manual builder.Services.AddTransient<T>() boilerplate required.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.McpContext, CancellationToken, ClaimsPrincipal, IMcpSession) are automatically excluded from tools/list schema definitions, preventing LLM argument errors.docs/auto-di-registration-guide.md.net8.0 and net10.0 binaries in a single package.GET /mcp/health exposed with a single builder.WithHealthChecks() calldatabase, llm, memory, external API) as a simple lambda with no interface to implementMaxResponseTimeMs; hanging checks reported as Degraded, not left blockingAllowAnonymous() so infrastructure probes bypass authenticationWithHealthChecks() is calledbuilder.WithTelemetry() with zero boilerplateILLMProvider API for all providersIAsyncEnumerable<string>builder.AddAnthropicProvider()mfa AMR claim for sensitive tools[AuthorizeMcpTool(RequireMfa=true)]McpContext.Storagegithub_createIssue)AddMcpMiddleware<T>McpContext injection for logging and progressMade with ❤️ by the DotnetFastMCP team
⭐ Star this repo if you find it useful!
C#
98.8%
PowerShell
1.2%