A production-ready template for building MCP (Model Context Protocol) servers and clients in .NET. This template implements best practices from enterprise-grade AI applications.
The Model Context Protocol (MCP) is a standard protocol for AI agents to interact with tools, prompts, and resources. This template provides:
- MCPServer - A complete MCP server implementation with HTTP transport
- MCPServer.Client - An example client for connecting to MCP servers
- MCPServer.Contracts - Shared configuration and DTOs
- MCPServer.Tests - Unit tests demonstrating testing patterns
- MCP Protocol v0.6.0 support with HTTP transport
- Azure Entra ID (Azure AD) JWT authentication
- Rate limiting with configurable policies
- Security headers middleware (HSTS, CSP, X-Frame-Options, etc.)
- CORS configuration
- Swagger/OpenAPI documentation
- Example tools, prompts, and resources
- Comprehensive configuration management
- Unit tests with xUnit
- .NET 9.0 SDK or later
- Node.js 18+ (for npm-based MCP servers)
cd src/MCPServer
dotnet runThe server will start on:
- HTTP: http://localhost:5000
- HTTPS: https://localhost:5001
cd src/MCPServer.Client
dotnet runcd src
dotnet testsrc/
├── MCPServer/ # MCP Server project
│ ├── Extensions/ # Service collection extensions
│ ├── Tools/ # MCP Tools (Calculator, DateTime, FileSystem)
│ ├── Prompts/ # MCP Prompts (CodeReview, Documentation)
│ ├── Resources/ # MCP Resources (SystemInfo, Configuration)
│ ├── Program.cs # Application entry point
│ └── appsettings.json # Configuration
│
├── MCPServer.Contracts/ # Shared contracts
│ └── Config/ # Configuration classes
│ ├── MCPServerConfig.cs # Server configuration
│ └── MCPClientConfig.cs # Client configuration
│
├── MCPServer.Client/ # Example MCP Client
│ └── Services/
│ └── McpToolProvider.cs # MCP client wrapper
│
└── MCPServer.Tests/ # Unit tests
├── Tools/ # Tool tests
└── Config/ # Configuration tests
{
"MCPServer": {
"ServerInfo": {
"Name": "DotNet-MCPServer",
"Version": "1.0.0",
"Instructions": "Description for AI clients",
"InitializationTimeoutSeconds": 60
},
"AzureEntra": {
"TenantId": "your-tenant-id",
"ClientId": "your-client-id"
},
"RateLimit": {
"Enabled": true,
"PermitLimit": 100,
"WindowSeconds": 60
},
"Security": {
"RequireAuthentication": true,
"EnableHttpsRedirection": true,
"EnableHsts": true,
"AllowedOrigins": "https://example.com",
"ScopesSupported": ["mcp:tools", "mcp:prompts", "mcp:resources"]
},
"Tools": {
"AllowedBasePaths": ["C:/temp/mcp-workspace"],
"MaxFileSizeBytes": 10485760,
"MaxSearchResults": 100
}
}
}{
"MCPClient": {
"ClientInfo": {
"Name": "DotNet-MCPClient",
"Version": "1.0.0"
},
"Servers": {
"file_system": {
"Enabled": true,
"Type": "Stdio",
"Command": "npx",
"Args": ["-y", "@modelcontextprotocol/server-filesystem", "C:/temp"],
"StartupTimeoutSeconds": 30
}
}
}
}Tools are automatically discovered via assembly scanning. Create a new class with the [McpServerToolType] attribute:
using ModelContextProtocol.Server;
using System.ComponentModel;
[McpServerToolType]
public static class MyCustomTool
{
[McpServerTool("my_tool")]
[Description("Description for AI clients")]
public static string MyToolMethod(
[Description("Parameter description")] string input)
{
return $"Processed: {input}";
}
}using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using System.ComponentModel;
[McpServerPromptType]
public static class MyPrompts
{
[McpServerPrompt("my_prompt")]
[Description("A custom prompt")]
public static IEnumerable<PromptMessage> MyPrompt(
[Description("Input parameter")] string input)
{
return new[]
{
new PromptMessage
{
Role = Role.User,
Content = new TextContent { Text = $"Process this: {input}" }
}
};
}
}using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
[McpServerResourceType]
public static class MyResources
{
[McpServerResource("custom://my-resource", Name = "My Resource",
Description = "A custom resource")]
public static ResourceContents GetMyResource()
{
return new TextResourceContents
{
Uri = "custom://my-resource",
MimeType = "application/json",
Text = "{ \"data\": \"value\" }"
};
}
}- Register an application in Azure AD
- Configure the client ID and tenant ID in
appsettings.json - Set
Security.RequireAuthenticationtotrue
Set in appsettings.Development.json:
{
"MCPServer": {
"Security": {
"RequireAuthentication": false
}
}
}- JWT Authentication - Azure Entra ID token validation
- Rate Limiting - Configurable request limits per time window
- Security Headers - X-Frame-Options, X-Content-Type-Options, CSP, etc.
- Path Validation - File system tools validate paths against allowed directories
- Input Validation - URI validation to prevent injection attacks
The template includes comprehensive unit tests:
# Run all tests
dotnet test
# Run with coverage
dotnet test --collect:"XPlat Code Coverage"
# Run specific test project
dotnet test src/MCPServer.TestsFROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
WORKDIR /app
EXPOSE 5000 5001
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore
RUN dotnet build -c Release -o /app/build
FROM build AS publish
RUN dotnet publish -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MCPServer.dll"]- Publish the application
- Configure application settings in Azure Portal
- Enable HTTPS only
- Configure Azure AD authentication if needed
Add to claude_desktop_config.json:
{
"mcpServers": {
"dotnet-mcp": {
"command": "dotnet",
"args": ["run", "--project", "path/to/MCPServer"]
}
}
}Use the MCPServer.Client project as a reference for building custom clients.
- Fork the repository
- Create a feature branch
- Write tests for new functionality
- Submit a pull request
MIT License - See LICENSE file for details.