A fully featured C# implementation of Anthropic's Model Context Protocol (MCP)
Mcp.Net is a .NET implementation of the Model Context Protocol (MCP) - a standardized way for apps to talk to AI models and execute tools. Think of it as the "HTTP of AI tool usage" - a clean, consistent way for your app to give AI models the ability to:
⚠️ Pre-1.0 Notice
This is version 0.9.0 - the core is stable but some features are still in development.
See Current Status for details.
Experience MCP (with web-search, scraping, twilio, various demo tools) with OpenAI or Anthropic models in just two steps:
# 1. Start the server with demo tools
dotnet run --project Mcp.Net.Examples.SimpleServer/Mcp.Net.Examples.SimpleServer.csproj
# 2. In a new terminal, run the LLM chat app (requires OpenAI or Anthropic API key)
dotnet run --project Mcp.Net.LLM/Mcp.Net.LLM.csprojSee the LLM demo documentation for more details.
# For building a server (the thing that provides tools)
dotnet add package Mcp.Net.Server
# For building a client (the thing that talks to AI models)
dotnet add package Mcp.Net.Clientusing Mcp.Net.Core.Attributes;
using Mcp.Net.Server;
// 1. Create a simple stdio server
var server = new McpServer(
 new ServerInfo { Name = "QuickStart Server", Version = "1.0" }
);
// 2. Define tools using simple attributes and POCOs
[McpTool("Calculator", "Math operations")]
public class CalculatorTools
{
 // Simple synchronous tool that returns a plain string
 [McpTool("add", "Add two numbers")]
 public string Add(
 [McpParameter(required: true, description: "First number")] double a,
 [McpParameter(required: true, description: "Second number")] double b)
 {
 return $"The sum of {a} and {b} is {a + b}";
 }
 
 // Async tool with a POCO return type - easiest approach!
 [McpTool("getWeather", "Get weather for a location")]
 public async Task<WeatherResponse> GetWeatherAsync(
 [McpParameter(required: true, description: "Location")] string location)
 {
 // Simulate API call
 await Task.Delay(100);
 
 // Just return a POCO - no need to deal with ToolCallResult!
 return new WeatherResponse
 {
 Location = location,
 Temperature = "72°F",
 Conditions = "Sunny",
 Forecast = new[] { "Clear", "Partly cloudy", "Clear" }
 };
 }
}
// Simple POCO class
public class WeatherResponse
{
 public string Location { get; set; }
 public string Temperature { get; set; }
 public string Conditions { get; set; }
 public string[] Forecast { get; set; }
}
// 3. Register all tools from assembly in one line
server.RegisterToolsFromAssembly(Assembly.GetExecutingAssembly(), serviceProvider);
// 4. Connect to stdio transport and start
await server.ConnectAsync(new StdioTransport());
// Server is now running and ready to process requests!For more control, you can also register tools directly:
using System.Text.Json;
using Mcp.Net.Core.Models.Content;
using Mcp.Net.Core.Models.Tools;
using Mcp.Net.Server;
// Create server
var server = new McpServer(
 new ServerInfo { Name = "Manual Server", Version = "1.0" }
);
// Register tool with explicit schema and handler
server.RegisterTool(
 name: "multiply",
 description: "Multiply two numbers",
 inputSchema: JsonDocument.Parse(@"
 {
 ""type"": ""object"",
 ""properties"": {
 ""x"": { ""type"": ""number"" },
 ""y"": { ""type"": ""number"" }
 },
 ""required"": [""x"", ""y""]
 }
 ").RootElement,
 handler: async (args) =>
 {
 var x = args?.GetProperty("x").GetDouble() ?? 0;
 var y = args?.GetProperty("y").GetDouble() ?? 0;
 var result = x * y;
 
 // For full control, you can explicitly use ToolCallResult
 return new ToolCallResult
 {
 Content = new[] { new TextContent { Text = $"{x} * {y} = {result}" } }
 };
 }
);using Mcp.Net.Client;
// Connect to a stdio server (like Claude or a local MCP server)
var client = new StdioMcpClient("MyApp", "1.0");
await client.Initialize();
// List available tools
var tools = await client.ListTools();
Console.WriteLine($"Available tools: {string.Join(", ", tools.Select(t => t.Name))}");
// Call the add tool
var result = await client.CallTool("add", new { a = 5, b = 3 });
Console.WriteLine(((TextContent)result.Content.First()).Text); // "The sum is 8"
// Call the weather tool
var weatherResult = await client.CallTool("getWeather", new { location = "San Francisco" });
Console.WriteLine(((TextContent)weatherResult.Content.First()).Text); 
// "The weather in San Francisco is sunny and 72°F"The MCP server provides multiple ways to configure your server, especially for controlling network settings when using the SSE transport:
// Configure the server with the builder pattern
var builder = new McpServerBuilder()
 .WithName("My MCP Server")
 .WithVersion("1.0.0")
 .WithInstructions("This server provides helpful tools")
 // Configure network settings
 .UsePort(8080) // Default is 5000
 .UseHostname("0.0.0.0") // Default is localhost
 // Configure transport mode
 .UseSseTransport(); // Uses the port and hostname configured aboveWhen running the server from the command line:
# Run with custom port and hostname
dotnet run --project Mcp.Net.Server --port 8080 --hostname 0.0.0.0
# For cloud environments, binding to 0.0.0.0 is usually required
dotnet run --project Mcp.Net.Server --hostname 0.0.0.0
# Run with stdio transport instead of SSE
dotnet run --project Mcp.Net.Server --stdio
# or use the shorthand
dotnet run --project Mcp.Net.Server -s
# Enable debug-level logging
dotnet run --project Mcp.Net.Server --debug
# or use the shorthand
dotnet run --project Mcp.Net.Server -d
# Specify a custom log file path
dotnet run --project Mcp.Net.Server --log-path /path/to/logfile.log
# Use a specific URL scheme (http or https)
dotnet run --project Mcp.Net.Server --scheme https
# Combine multiple options
dotnet run --project Mcp.Net.Server --stdio --debug --port 8080 --hostname 0.0.0.0The ServerConfiguration and CommandLineOptions classes handle these arguments:
// CommandLineOptions.cs parses command-line arguments
public static CommandLineOptions Parse(string[] args)
{
 var options = new CommandLineOptions(args)
 {
 UseStdio = args.Contains("--stdio") || args.Contains("-s"),
 DebugMode = args.Contains("--debug") || args.Contains("-d"),
 LogPath = GetArgumentValue(args, "--log-path") ?? "mcp-server.log",
 Port = GetArgumentValue(args, "--port"),
 Hostname = GetArgumentValue(args, "--hostname"),
 Scheme = GetArgumentValue(args, "--scheme")
 };
 return options;
}# Set standard environment variables before running
export MCP_SERVER_PORT=8080
export MCP_SERVER_HOSTNAME=0.0.0.0
export MCP_SERVER_SCHEME=http
# Cloud platform compatibility - many cloud platforms use PORT
export PORT=8080
dotnet run --project Mcp.Net.ServerThe ServerConfiguration class handles these environment variables with a priority-based approach:
// ServerConfiguration.cs handles environment variables:
private void LoadFromEnvironmentVariables()
{
 // Standard MCP hostname variable
 string? envHostname = Environment.GetEnvironmentVariable("MCP_SERVER_HOSTNAME");
 if (!string.IsNullOrEmpty(envHostname))
 {
 Hostname = envHostname;
 }
 
 // Cloud platform compatibility - PORT is standard on platforms like Google Cloud Run
 string? cloudRunPort = Environment.GetEnvironmentVariable("PORT");
 if (!string.IsNullOrEmpty(cloudRunPort) && int.TryParse(cloudRunPort, out int parsedCloudPort))
 {
 Port = parsedCloudPort;
 }
 else
 {
 // Fall back to MCP-specific environment variable
 string? envPort = Environment.GetEnvironmentVariable("MCP_SERVER_PORT");
 if (!string.IsNullOrEmpty(envPort) && int.TryParse(envPort, out int parsedEnvPort))
 {
 Port = parsedEnvPort;
 }
 }
 
 // HTTPS configuration
 string? envScheme = Environment.GetEnvironmentVariable("MCP_SERVER_SCHEME");
 if (!string.IsNullOrEmpty(envScheme))
 {
 Scheme = envScheme.ToLowerInvariant();
 }
}The server also reads settings from appsettings.json:
{
 "Server": {
 "Port": 8080,
 "Hostname": "0.0.0.0",
 "Scheme": "http"
 }
}The configuration is loaded with a tiered priority approach:
// SseServerBuilder automatically loads from configuration files:
private void ConfigureAppSettings(WebApplicationBuilder builder, string[] args)
{
 // Add configuration from multiple sources with priority:
 // 1. Command line args (highest)
 // 2. Environment variables
 // 3. appsettings.json (lowest)
 builder.Configuration.AddJsonFile("appsettings.json", optional: true);
 builder.Configuration.AddEnvironmentVariables("MCP_");
 builder.Configuration.AddCommandLine(args);
}The server uses this priority order when resolving configuration:
This allows for flexible deployment in various environments, from local development to cloud platforms.
The SSE server includes built-in health check endpoints:
/health - Overall health status/health/ready - Readiness check for load balancers/health/live - Liveness check for container orchestratorsPerfect for web applications, the SSE transport:
Ideal for CLI tools and AI model integration:
var builder = WebApplication.CreateBuilder(args);
// Add MCP server to services
builder.Services.AddMcpServer(b =>
{
 b.WithName("My MCP Server")
 .WithVersion("1.0.0")
 .WithInstructions("Server providing math and weather tools")
 .UsePort(8080) // Configure port (default: 5000)
 .UseHostname("0.0.0.0") // Configure hostname (default: localhost)
 .UseSseTransport(); // Uses the port and hostname configured above
});
// Configure middleware
var app = builder.Build();
app.UseCors(); // If needed
app.UseMcpServer();
await app.RunAsync();// Return both text and an image
return new ToolCallResult
{
 Content = new IContent[] 
 { 
 new TextContent { Text = "Here's the chart you requested:" },
 new ImageContent 
 { 
 MimeType = "image/png",
 Data = Convert.ToBase64String(imageBytes) 
 }
 }
};This implementation is currently at version 0.9.0:
This project is licensed under the MIT License - see the LICENSE file for details.
Made with ❤️ by Sam Fold