DotnetFastMCP
Provides OAuth 2.0 authentication integration with Auth0, enabling secure user login and token verification for MCP tools.
Provides a built-in health endpoint compatible with Docker health checks.
Provides OAuth 2.0 authentication integration with GitHub, allowing users to sign in with GitHub accounts to access MCP tools.
Provides OAuth 2.0 authentication integration with Google, allowing users to sign in with Google accounts to access MCP tools.
Provides an LLM provider for Google Gemini models, enabling text generation and streaming via a unified interface.
Allows exporting OpenTelemetry metrics and traces to Grafana for visualization and dashboards.
Provides an LLM provider for Hugging Face models, enabling text generation and streaming via a unified interface.
Allows exporting OpenTelemetry traces to Jaeger for distributed tracing analysis.
Provides a built-in health endpoint compatible with Kubernetes liveness and readiness probes.
Provides OAuth 2.0 authentication integration with Okta, enabling secure user login and token verification for MCP tools.
Provides an LLM provider for Ollama, enabling local text generation and streaming via a unified interface.
Provides an LLM provider for OpenAI models, enabling text generation and streaming via a unified interface.
Provides OpenTelemetry integration for distributed tracing and metrics, auto-tracking tool invocations, duration, errors, and more.
Allows exporting OpenTelemetry metrics to Prometheus for monitoring and alerting.
DotnetFastMCP — Enterprise Security & Governance Gateway for MCP Servers
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
🖼️ Multimodal Content & Universal Media Loader (NEW! v2.2.0)
✅ Universal Media Loader (
McpMediaLoader) - Ingest and resolve images/media from any location: remote HTTP/HTTPS, local file paths,file://URIs, and RFC 2397data:...;base64,...URIs without boilerplate.✅ Eliminates Repetitive Helper Code - Replaces duplicate ~40-line
FetchImageDataAsyncmethods across multimodal agent servers with a single line:await McpMediaLoader.LoadAsBase64Async(url, httpClient).✅ Smart MIME Sniffing - Magic-byte inspection for JPEG, PNG, GIF, WebP, BMP with HTTP
Content-Typeheader inspection and file extension fallbacks.✅ Zero-Allocation Fast-Path - Data URIs with Base64 payloads are parsed directly without allocating intermediate byte arrays.
✅ Expanded
McpContentFactory Methods - First-class helpers forMcpContent.Json(obj),McpContent.ImageFromBytes(bytes, mime),McpContent.ImageFromStreamAsync(stream, mime),McpContent.ImageFromUriAsync(uri),McpContent.ResourceText(),McpContent.ResourceBlob(), andMcpContent.Composite(...).
⚡ Zero-Boilerplate MCP Servers (v2.1.0)
✅ Automatic DI Registration - Non-static tool, resource, and prompt classes scanned via
WithComponentsFrom()are automatically registered asTransientservices in the DI container. Zero manualbuilder.Services.AddTransient<T>()boilerplate.✅ Preserves Custom Lifetimes - Built on
TryAddTransientsemantics to honor custom Singleton or Scoped registrations without collision.✅
[McpDescription]Parameter Attributes - Annotate method parameters with rich descriptions emitted directly into JSON SchemainputSchema(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.0andnet10.0binaries 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
ILLMProviderAPI 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/healthexposed 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
Related MCP server: MCPAuth
🚀 Quick Start
Installation
Install via NuGet Package Manager:
dotnet add package DotnetFastMCP --version 2.2.0Or clone the repository:
git clone https://github.com/tekspry/DotnetFastMCP.git
cd DotnetFastMCP
dotnet build -c ReleaseCreate 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 runThe server will start on http://localhost:5000.
🏆 Built With DotnetFastMCP
Real-world enterprise projects that demonstrate DotnetFastMCP in production:
👗 Fashion Accessory AI Marketing Pipeline
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)📚 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 scriptProject Structure
Project | Purpose |
| Core framework library |
| Command-line interface tools |
| Example MCP server implementation |
| Integration tests |
| Example 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 runYour server is now running with OAuth Proxy endpoints:
MCP endpoint:
http://localhost:5002/mcpOAuth authorization:
http://localhost:5002/oauth/authorizeOAuth token:
http://localhost:5002/oauth/tokenDiscovery:
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 -- --stdioCreate 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:
Provider | Method | Use Case | Default Scopes |
Azure AD |
| Enterprise apps, Microsoft 365 |
|
| Consumer apps, Google Workspace |
| |
GitHub |
| Developer tools, repositories |
|
Auth0 |
| Multi-tenant SaaS, custom identity |
|
Okta |
| Enterprise SSO, workforce identity |
|
AWS Cognito |
| AWS-native apps, user pools |
|
Quick Setup Examples
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-secretExample: 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-secretExample: 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-secretExample: 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
DotnetFastMCPproject):dotnet publish -c Release -o ..\publish examples\BasicServerRun 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
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-secretExample: 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-secretExample: 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.comExample: 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 testsProject 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 testTest 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.restTest 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
Multimodal Content & Universal Media Loader Guide (NEW! v2.2.0)
Automatic DI Registration & Parameter Descriptions Guide (v2.1.0)
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
Example | Description | Port |
Simple MCP server with Auto-DI & [McpDescription] | 5000 | |
🏥 Health monitoring & diagnostics demo | 5000 | |
📡 OpenTelemetry metrics & tracing demo | 5000 | |
Azure AD authentication example | 5002 | |
Google OAuth example | 5000 | |
GitHub OAuth example | 5001 | |
Auth0 authentication example | 5005 | |
Okta authentication example | 5007 | |
AWS Cognito example | 5006 |
🏗️ Advanced Features
🖼️ Multimodal Content & Universal Media Loader (NEW! v2.2.0)
DotnetFastMCP 2.2 introduces first-class multimodal capabilities designed for vision, generative AI, and image-processing agent servers:
McpMediaLoaderUniversal Media Resolution: Seamlessly load images and media from remote HTTP/HTTPS URLs, local files,file://URIs, or RFC 2397data:...;base64,...URIs without needing custom boilerplate.Zero Duplicate Boilerplate: Replaces repetitive ~40-line
FetchImageDataAsyncmethods across microservices with a single line:var (base64, mimeType) = await McpMediaLoader.LoadAsBase64Async(imageUrl, httpClient);Magic-Byte Sniffing: Inspects byte headers (
image/png,image/jpeg,image/gif,image/webp,image/bmp) when HTTP headers are missing or inaccurate.Rich
McpContentFactory Methods:McpContent.ImageFromBytes(bytes, "image/png")await McpContent.ImageFromUriAsync("https://example.com/photo.jpg", httpClient)await McpContent.ImageFromStreamAsync(stream, "image/webp")McpContent.Json(orderObject)McpContent.Composite(McpContent.Text("Summary"), McpContent.Image(...))
using FastMCP;
using FastMCP.Attributes;
public class VisionTools
{
private readonly HttpClient _httpClient;
public VisionTools(HttpClient httpClient) => _httpClient = httpClient;
[McpTool("analyze_image", "Inspect an image from a URL, local path, or base64 data URI")]
public async Task<McpContent> AnalyzeImage(
[McpDescription("Image URL, file path, or data URI")] string imageUri,
CancellationToken ct)
{
// One-line universal media loading (supports HTTP/HTTPS, file://, local paths, data URIs)
var (base64, mimeType) = await McpMediaLoader.LoadAsBase64Async(imageUri, _httpClient, cancellationToken: ct);
// Return an MCP Image Content payload directly:
return McpContent.Image(base64, mimeType);
}
}⚡ Automatic DI Registration & [McpDescription] (v2.1.0)
DotnetFastMCP 2.1 makes authoring production MCP servers completely zero-boilerplate by pairing automatic Dependency Injection with LLM-grade parameter schemas:
Zero-Config DI: Non-static tool, resource, and prompt classes scanned with
WithComponentsFrom()are automatically registered asTransientinto ASP.NET Core DI. No more manualbuilder.Services.AddTransient<OrderTools>()lines.Preserves Custom Lifetimes: Built on
TryAddTransientsemantics, so any class explicitly registered asSingletonorScopedinbuilder.Servicesretains its desired lifetime.[McpDescription]for Parameters: Annotate tool parameters with descriptions that are exposed directly in the JSON SchemainputSchema(tools/list), giving LLMs exact semantic context and eliminating hallucinated arguments.Framework Parameter Exclusion: Types such as
McpContext,CancellationToken,ClaimsPrincipal, andIMcpSessionare 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:
Status | HTTP Code | Meaning |
| 200 | All checks passed |
| 207 | Server up, ≥1 check timed out |
| 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: 10See 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:
Metric | Type | Tag | Description |
| Counter |
| Total tool calls |
| Histogram (ms) |
| Tool execution time |
| Counter |
| Failed tool calls |
| Counter | — | Prompt template requests |
| Counter | — | Resource read requests |
Validate with dotnet-counters (no exporter needed):
dotnet-counters monitor -n YourAppName --counters FastMCPSee 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.
Define Middleware: Implement
IMcpMiddleware.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
amrclaim containsmfa.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)
Provider | Extension Method | Latest Model | Best For |
Ollama |
|
| Local, privacy, offline |
OpenAI |
|
| Production, function calling |
Azure OpenAI |
|
| Enterprise, compliance |
Anthropic |
|
| Deep reasoning, 1M context |
Google Gemini |
|
| Multimodal, high-volume |
Cohere |
|
| Enterprise RAG, agents |
Hugging Face |
| Any model | Open-source, flexibility |
Deepseek |
|
| Cost-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
.envfiles 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:
Fork the repository
Create a feature branch (
git checkout -b feature/amazing-feature)Commit your changes (
git commit -m 'Add amazing feature')Push to the branch (
git push origin feature/amazing-feature)Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🔗 Resources
Official Documentation
Framework Documentation
Health Checks Guide 🆕 v1.15.0
Observability Guide v1.14.0
Provider Documentation
🐛 Issues & Support
For bug reports and feature requests, please use GitHub Issues.
✨ What's New
v2.2.0 - Multimodal Content & Universal Media Loader (Latest - Oct 2026)
🖼️ Universal
McpMediaLoader- Ingest and resolve images/media from any location: remote HTTP/HTTPS, local file paths,file://URIs, and RFC 2397data:...;base64,...URIs without custom boilerplate.✂️ Eliminates Microservice Boilerplate - Replaces duplicate ~40-line
FetchImageDataAsyncmethods across multimodal agent servers with a single line:await McpMediaLoader.LoadAsBase64Async(url, httpClient).🔍 Intelligent MIME Sniffing - Magic-byte inspection for JPEG, PNG, GIF, WebP, BMP with HTTP
Content-Typeheader inspection and file extension fallbacks.⚡ Zero-Allocation Data URI Fast-Path - Direct extraction from Base64 data URIs without allocating intermediate byte arrays.
📦 Expanded
McpContentFactory Methods - First-class helpers forMcpContent.Json(obj),McpContent.ImageFromBytes(bytes, mime),McpContent.ImageFromStreamAsync(stream, mime),McpContent.ImageFromUriAsync(uri),McpContent.ResourceText(),McpContent.ResourceBlob(), andMcpContent.Composite(...).🧪 126 Tests Passing (252 Total) - Comprehensive test matrix with 41 new unit, edge-case, and negative test cases passing across both .NET 8 LTS and .NET 10 LTS.
v2.1.2 - MCP Protocol Specification Compliance & Glama.ai Certification (Oct 2026)
🏅 Glama.ai Certified - Passed 100% of automated containerized introspection tests on the Glama.ai MCP registry.
🎯 Strict MCP
pingHandling - StandardizedpingJSON-RPC response to emit an empty result object ({}) strictly complying with the MCP specificationEmptyResultSchema.🧼 Zod Schema Null Omission - Configured
WhenWritingNullonPrompt,Resource, andToolprotocol metadata, ensuring compatibility with@modelcontextprotocol/sdkZod validation without mutating user data payloads.🔕 Silent Notification Handling - Standardized notification processing across STDIO, SSE, and HTTP transports to strictly prevent erroneous
id: nullresponse frames from being emitted over the wire.🧪 78 Tests Passing - Comprehensive unit and integration test matrix validating protocol compliance, parameter filtering, and serialization.
v2.1.1 - Client Deserialization Patch (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.Contentwhile maintaining full MCP specification compliance.⚡ Direct Envelope Overload - Added non-generic
client.CallToolAsync("toolName", args)returning rawCallToolResultdirectly.🧪 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 asTransientduringWithComponentsFrom(). No manualbuilder.Services.AddTransient<T>()boilerplate required.🛡️ Lifespan Safety - Implemented via
TryAddTransientso customSingletonorScopedregistrations configured inbuilder.Servicesare never overwritten.📝
[McpDescription]Parameter Attribute - Tool parameters annotated with[McpDescription]have their documentation automatically rendered into JSON Schemaproperties.<param>.descriptionintools/list.🧼 Clean Schema Generation - Framework types (
McpContext,CancellationToken,ClaimsPrincipal,IMcpSession) are automatically excluded fromtools/listschema 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.0andnet10.0binaries 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/healthexposed with a singlebuilder.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 asDegraded, 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
ILLMProviderAPI 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
mfaAMR 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 -
McpContextinjection 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!
Available Tools
7 toolsadd_numbersA
Calculates the arithmetic sum of two integers (a + b). Use this tool when you need to perform addition. Do not use for subtraction (use 'subtract_numbers' instead), multiplication (use 'multiply_numbers' instead), or division (use 'divide_numbers' instead). Deterministic, pure calculation with no side effects.
| Name | Required | Description | Default |
|---|---|---|---|
| a | Yes | The first integer operand (addend). Range: -2,147,483,648 to 2,147,483,647. | |
| b | Yes | The second integer operand (addend) to add to 'a'. Range: -2,147,483,648 to 2,147,483,647. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden; it discloses determinism and absence of side effects, which is genuinely useful for an agent deciding whether to batch or retry. It does not mention error conditions (e.g., integer overflow beyond the stated 32-bit range), but the schema documents the range, so this is a minor gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with the operation and immediately followed by usage and exclusions. Every sentence earns its place; the sibling exclusions are the payload, not filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-integer pure function with a fully documented schema, the description covers purpose, routing, and side-effect profile, which is sufficient to invoke it correctly. No output schema exists, but the return value (the sum) is obvious from the description.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description adds value by restating the operation as 'a + b', clarifying operand roles and ordering beyond the schema's per-field descriptions. With only two self-explanatory operands, no further semantics are needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Calculates the arithmetic sum of two integers (a + b)') and explicitly distinguishes itself from the named siblings subtract_numbers, multiply_numbers, and divide_numbers. An agent can identify exactly what this tool does and what it is not.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states when to use it ('when you need to perform addition') and explicitly lists when not to use it, naming the alternative tool for each excluded operation. This is textbook routing guidance with clear exclusions and named alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
divide_numbersA
Calculates the integer quotient of two numbers (a / b). Use this tool for integer floor division. Do not use for addition (use 'add_numbers' instead) or multiplication (use 'multiply_numbers' instead). Returns floor quotient; throws an error if divisor 'b' is zero.
| Name | Required | Description | Default |
|---|---|---|---|
| a | Yes | The dividend integer to divide. | |
| b | Yes | The divisor integer to divide by. Must not be zero. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden, and it does disclose two meaningful traits: the result is a floor quotient (not true division or a float), and a zero divisor throws an error. It stops short of stating rounding behavior for negatives or the error type/shape, which would fully close the gap for a zero-annotation tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the core behavior before the routing exclusions. The 'Do not use for addition/multiplication' clause is mildly verbose given the names already imply scope, but it earns its place as routing guidance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-integer arithmetic tool with no output schema and no annotations, the description covers purpose, return semantics, and failure mode – enough to invoke correctly. Only the negative-number rounding convention is unaddressed, a minor gap for this operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% – both 'a' (dividend) and 'b' (divisor, must not be zero) are documented in the schema. The description restates the a / b relationship and the non-zero constraint on 'b' but adds no format or edge-case detail beyond the schema, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Calculates the integer quotient of two numbers') and immediately constrains the operation to integer floor division. It explicitly distinguishes itself from the sibling tools add_numbers and multiply_numbers, so an agent can route correctly without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives both when-to-use ('Use this tool for integer floor division') and explicit when-not-to-use guidance, naming the two sibling alternatives (add_numbers, multiply_numbers) that a confused agent might otherwise pick. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
format_textA
Transforms and normalizes string casing and whitespace. Use this tool for text standardization like uppercase, lowercase, titlecase, or trimming. Do not use for generating interpersonal greetings (use 'greet_user' instead). Deterministic in-memory string transformation.
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | The raw text string to format. | |
| operation | No | The formatting operation: 'uppercase', 'lowercase', 'titlecase', or 'trim'. Defaults to 'trim'. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden, and it does disclose the key trait: 'Deterministic in-memory string transformation' tells the agent there are no side effects, no external state, and no network calls. It does not describe behavior on an invalid or missing operation value, which is a minor remaining gap for a mutation-free utility.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with purpose, then routing guidance, then a behavioral note. The final sentence slightly overlaps the first but earns its place by signaling determinism.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter, no-output-schema utility, the description covers purpose, routing, and purity, which is nearly everything an agent needs. Only the handling of an invalid operation value is left unspecified.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both parameters are already documented in the schema, making 3 the baseline. The description restates the operation set (uppercase/lowercase/titlecase/trim) but adds no default behavior or edge-case guidance beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Transforms and normalizes string casing and whitespace') and names the sibling it is not (greet_user). An agent can distinguish it from the arithmetic and task-processing siblings without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use examples (uppercase, lowercase, titlecase, trimming) and an explicit when-not-to-use with the alternative tool named ('Do not use for generating interpersonal greetings (use greet_user instead)'). Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
greet_userA
Generates a personalized greeting message for a user. Use this tool to produce customized salutations in formal or casual tone when interacting with humans. Do not use for raw text formatting or casing changes (use 'format_text' instead). Safe, idempotent formatting.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | The name of the user to greet (e.g. 'Alice'). Must not be empty. | |
| style | No | The greeting style tone: 'formal' for professional salutations or 'casual' for friendly greetings. Defaults to 'casual'. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the burden, and it does disclose that the operation is 'Safe, idempotent formatting' — useful behavioral context for a mutation-free utility. It stops short of describing output shape, but the tool is trivially side-effect-free, so the gap is minor.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four short sentences, front-loaded with the core purpose and followed immediately by routing guidance. There is slight redundancy between 'personalized greeting message' and 'customized salutations', but no meaningful waste.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple two-parameter, single-required utility with full schema coverage and no output schema, the description covers purpose, usage, exclusions, tone options, and safety profile. Nothing an agent needs to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents both 'name' and 'style' including the default and enum-like values. The description's mention of 'formal or casual tone' merely echoes the schema, adding no syntax or constraint detail. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Generates a personalized greeting message for a user') and explicitly distinguishes itself from the sibling 'format_text'. An agent can identify the tool's function without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use ('produce customized salutations in formal or casual tone when interacting with humans') and an explicit when-not with the named alternative ('Do not use for raw text formatting or casing changes (use format_text instead)'). Nothing about routing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
multiply_numbersA
Calculates the arithmetic product of two integers (a * b). Use this tool when you need to multiply two numbers. Do not use for addition (use 'add_numbers' instead) or division (use 'divide_numbers' instead). Deterministic pure calculation with no side effects.
| Name | Required | Description | Default |
|---|---|---|---|
| a | Yes | The first integer factor. | |
| b | Yes | The second integer factor to multiply with 'a'. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses that this is a deterministic pure calculation with no side effects, which is meaningful behavioral context for a mutation-free tool. It doesn't address edge cases like integer overflow, which keeps it short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, all earning their place, with the core operation front-loaded before the routing guidance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter pure arithmetic tool with full schema coverage and no output schema needed, the description covers purpose, routing, and behavioral guarantees. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both parameters are already documented in the schema. The description only restates the a * b relationship, adding no syntax or format detail beyond what the schema provides. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific operation and resource: 'Calculates the arithmetic product of two integers (a * b).' It even names the sibling tools it should not be confused with, so an agent can distinguish it from add_numbers and divide_numbers without inspecting schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use it ('when you need to multiply two numbers') and when not to, naming the exact alternative tools for addition and division. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
process_taskA
Executes a simulated task with progress tracking and context logging. Use this tool when executing operations that require live progress updates and diagnostic log streaming via McpContext. Do not use for instant math calculations. Emits real-time progress notifications at 10% and 100% completion milestones via JSON-RPC.
| Name | Required | Description | Default |
|---|---|---|---|
| input | Yes | The task description or input payload string to process. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does disclose runtime behavior: real-time progress notifications at 10% and 100% via JSON-RPC and diagnostic log streaming through McpContext. It does not cover error behavior or whether the task has side effects, but the operational profile is largely conveyed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with the core purpose before usage guidance. Slightly repetitive in restating progress/logging twice, but nothing is wasted enough to impede selection.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with no annotations and no output schema, the description adequately covers purpose, usage conditions, and runtime emissions. Return-value semantics are not needed since progress flows via notifications, leaving only error handling unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Only one parameter with 100% schema description coverage, so the schema already explains 'input' fully. The description adds no syntax, format, or payload-shape detail beyond what the schema provides, matching the baseline for high-coverage schemas.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Executes a simulated task') plus the distinguishing capabilities of progress tracking and context logging. It is clearly separable from the sibling arithmetic/text utilities, though what a 'simulated task' actually computes remains somewhat abstract.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the selecting condition ('operations that require live progress updates and diagnostic log streaming via McpContext') and an exclusion ('Do not use for instant math calculations'), which directly routes the agent away from the arithmetic siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
subtract_numbersA
Calculates the arithmetic difference between two integers (a - b). Use this tool when you need to subtract one value from another. Do not use for addition (use 'add_numbers' instead), multiplication (use 'multiply_numbers' instead), or division (use 'divide_numbers' instead). Deterministic pure calculation; returns negative values if the subtrahend 'b' is greater than minuend 'a'.
| Name | Required | Description | Default |
|---|---|---|---|
| a | Yes | The minuend integer from which 'b' will be subtracted. | |
| b | Yes | The subtrahend integer to subtract from 'a'. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does add real behavioral context: it declares the operation deterministic and pure, and discloses the sign behavior when b > a ('returns negative values if the subtrahend b is greater than minuend a'). It does not mention error handling for non-integer or missing input, though the schema's type constraints largely cover that.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with the operation and its formula, then routing, then edge-case behavior. The enumeration of three sibling alternatives is slightly listy but each clause earns its place as a routing rule.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a pure two-integer arithmetic tool with no output schema, annotations, or side effects, this covers everything an agent needs: operation, argument order, sign behavior, and sibling disambiguation. Nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and both parameters already document minuend/subtrahend roles, so the schema does the heavy lifting. The description restates the ordering (a - b) but adds no syntax or format detail beyond the schema, making the baseline 3 correct.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and operation ('Calculates the arithmetic difference between two integers (a - b)') and explicitly differentiates itself from three named siblings (add_numbers, multiply_numbers, divide_numbers). An agent can route to this tool without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives both when-to-use ('when you need to subtract one value from another') and explicit when-not-to-use with the alternative for each excluded case. This is textbook routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
8 tool updates
v0.1.1- Changed
add_numbers2 fields changed- changed
Input schema / properties / a / descriptionPrevious value: -"The first integer operand"New value: +"The first integer operand (addend). Range: -2,147,483,648 to 2,147,483,647." - changed
Input schema / properties / b / descriptionPrevious value: -"The second integer operand"New value: +"The second integer operand (addend) to add to 'a'. Range: -2,147,483,648 to 2,147,483,647."
- Added
divide_numbers - Added
format_text - Changed
greet_user2 fields changed- changed
Input schema / properties / name / descriptionPrevious value: -"The name of the user to greet, e.g. 'Alice'"New value: +"The name of the user to greet (e.g. 'Alice'). Must not be empty." - changed
Input schema / properties / style / descriptionPrevious value: -"The greeting style: 'formal' or 'casual' (default: casual)"New value: +"The greeting style tone: 'formal' for professional salutations or 'casual' for friendly greetings. Defaults to 'casual'."
- Added
multiply_numbers - Added
process_task - Added
subtract_numbers - Removed
TestContext
3 tool updates
v0.1.0- First observed
add_numbers - First observed
greet_user - First observed
TestContext
TDQS
Scored across 7 tools
Each tool has a distinct action and target: four arithmetic operations, text formatting, greeting, and task processing. Descriptions explicitly cross-reference alternatives (e.g., 'use add_numbers instead'), so an agent should not confuse them.
All tool names follow the same snake_case verb_noun pattern: divide_numbers, format_text, process_task, greet_user, add_numbers, subtract_numbers, multiply_numbers. No deviations or mixed conventions.
Seven tools is a reasonable count for a small utility/demo server. However, the four arithmetic primitives could be consolidated into a single calculator tool, making the surface slightly over-granular.
The arithmetic operations form a complete set, but the overall surface mixes unrelated utility domains (text, greeting, simulated task) without a clear lifecycle or domain scope. Text formatting is limited to casing/whitespace and the task tool is a single simulation, so notable coverage gaps remain for any inferred general-purpose toolkit.
Maintenance
Related MCP Connectors
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
MCP server for secureFlows: token-free URL builders and integration-linting tools for AI agents.
Hosted MCP server with managed OAuth for 15+ toolkits: Google Workspace, Fitbit, Oura, Kalshi, etc.
MCP server for progressive tool usage at any scale (see https://klavis.ai)
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceA comprehensive Model Context Protocol server template that implements HTTP-based transport with OAuth proxy for third-party authorization servers like Auth0, enabling AI tools to securely connect while supporting Dynamic Application Registration.16 npm7MIT
- AlicenseNot gradedqualityDmaintenanceA self-hostable OAuth 2.0 server designed for the Model-Context-Protocol (MCP) that enables you to secure your MCP applications with a robust implementation you control.1,800 npm113ISC
- AlicenseNot gradedqualityCmaintenanceEasiest framework for building MCP servers with automatic discovery of tools, prompts, and resources, plus enterprise-grade authentication and telemetry.841Apache 2.0
- FlicenseNot gradedqualityDmaintenanceA production Model Context Protocol (MCP) server with 70+ tools, OAuth 2.1 authentication, and frame-based tool filtering.1-