Install
$ agentstack add mcp-samfold-mcp-net ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Mcp.Net - Model Context Protocol for .NET 🚀 Connect your apps to AI models with a standardized protocol for tools, resources, and prompts
✨ What is Mcp.Net?
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:
- 🧰 Use tools like search, weather lookup, database access
- 🌐 Access web resources and fetch web content
- 📝 Work with predefined prompts and templates
> ⚠️ Pre-1.0 Notice > > This is version 0.9.0 - the core is stable but some features are still in development. > See [Current Status](#current-status) for details.
🏃♀️ Quick Start
Try the interactive LLM demo
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.csproj
See the [LLM demo documentation](Mcp.Net.LLM/README.md) for more details.
Install the packages
# 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.Client
Run the sample server + client with OAuth 2.1
# Terminal 1 — start the demo server (SSE on http://localhost:5000)
dotnet run --project Mcp.Net.Examples.SimpleServer/Mcp.Net.Examples.SimpleServer.csproj
# Terminal 2 — launch the client (performs dynamic registration + PKCE)
dotnet run --project Mcp.Net.Examples.SimpleClient -- --url http://localhost:5000 --auth-mode pkce
> ℹ️ The first SSE GET returns 401 Unauthorized by design. The client follows the > WWW-Authenticate challenge, registers itself at /oauth/register, completes the PKCE > handshake, and reconnects with a bearer token. Watch the logs to see resources, prompts, > and tools (including the new elicitation flow) being exercised end-to-end. When the > Warhammer inquisitor tool runs you’ll be prompted to accept/decline/cancel and optionally > override fields via the console handler in the sample client.
Enable elicitation in your own client
The client advertises the elicitation capability only after you register a handler. Call SetElicitationHandler (or the builder helper) before Initialize:
var client = new McpClientBuilder()
.UseSseTransport(serverUrl)
.WithElicitationHandler(async (context, ct) =>
{
// Render UI, validate against context.RequestedSchema, then respond
return ElicitationClientResponse.Decline();
})
.Build();
await client.Initialize();
Handlers can accept, decline, or cancel and receive strongly typed schema details via ElicitationRequestContext. The SimpleClient console demo in Mcp.Net.Examples.SimpleClient shows a full end-to-end implementation.
Create your first MCP server in 2 minutes
using Mcp.Net.Core.Attributes;
using Mcp.Net.Server;
using Mcp.Net.Server.ConnectionManagers;
using Mcp.Net.Server.Extensions;
using Microsoft.Extensions.Logging;
// 1. Create a simple stdio server
var server = new McpServer(
new ServerInfo { Name = "QuickStart Server", Version = "1.0" },
new InMemoryConnectionManager(new LoggerFactory())
);
// 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 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. Connect to stdio transport and start
await server.ConnectAsync(new StdioTransport());
// Server is now running and ready to process requests!
Manual Tool Registration (Alternative style)
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;
using Mcp.Net.Server.ConnectionManagers;
using Microsoft.Extensions.Logging;
// Create server
var server = new McpServer(
new ServerInfo { Name = "Manual Server", Version = "1.0" },
new InMemoryConnectionManager(new LoggerFactory())
);
// 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}" } }
};
}
);
Connect a client to your server
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"
📊 Project Structure
- Mcp.Net.Core: Models, interfaces, and base protocol components
- Mcp.Net.Server: Server-side implementation with transports (SSE and stdio)
- Mcp.Net.Client: Client libraries for connecting to MCP servers
- Mcp.Net.Examples.SimpleServer: [Simple example server](Mcp.Net.Examples.SimpleServer/README.md) with calculator and themed tools
- Mcp.Net.Examples.SimpleClient: [Simple example client](Mcp.Net.Examples.SimpleClient/README.md) that connects to MCP servers
- Mcp.Net.LLM: [Interactive LLM demo](Mcp.Net.LLM/README.md) integrating OpenAI/Anthropic models with MCP tools
- Mcp.Net.Examples.ExternalTools: Standalone tool library that can be loaded by any MCP server
🔌 Key Features
- Two Transport Options:
- ⌨️ stdio: Perfect for CLI tools and direct model interaction
- 🌐 SSE: Ideal for web apps and browser integrations
- Tool Management:
- ✅ Dynamic tool discovery
- ✅ JSON Schema validation for parameters
- ✅ Both synchronous and async tool support
- ✅ Error handling and result formatting
- OAuth 2.1 Reference Flow:
- ✅ Dynamic client registration (
/oauth/register) with in-memory persistence - ✅ Authorization code + PKCE with enforced
resourceindicators - ✅ Refresh-token rotation and audience validation in the demo server/client samples
- Flexible Hosting:
- ✅ Use as standalone server
- ✅ Embed in ASP.NET Core applications
- ✅ Run as background service
🔧 Server Configuration Options
The MCP server provides multiple ways to configure your server, especially for controlling network settings when using the SSE transport:
Using the Builder Pattern
// 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 above
Using Command Line Arguments
When 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.0
The 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;
}
Using Environment Variables
# 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.Server
The 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();
}
}
Using appsettings.json
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);
}
Configuration Priority
The server uses this priority order when resolving configuration:
- Command line arguments (highest priority)
- Environment variables
- appsettings.json configuration
- Default values (lowest priority)
This allows for flexible deployment in various environments, from local development to cloud platforms.
Health Checks and Observability
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 orchestrators
Future Planned Features
- HTTPS/TLS support enhancements
- Advanced metrics and telemetry
- Authentication integration
- Resource quota management
🛠️ Transport Implementations
Server-Sent Events (SSE)
Perfect for web applications, the SSE transport:
- Maintains a persistent HTTP connection
- Uses standard event streaming
- Supports browser-based clients
- Enables multiple concurrent connections
Standard I/O (stdio)
Ideal for CLI tools and AI model integration:
- Communicates via standard input/output
- Works great with Claude, GPT tools
- Simple line-based protocol
- Lightweight and efficient
- Requests now honour newline-delimited framing and expose a configurable
StdioClientTransport.RequestTimeout(default 60s, set toTimeout.InfiniteTimeSpanto disable). Pending requests are cancelled automatically when the transport closes so callers can surface clean shutdown errors. - Inbound stdio traffic is host-driven: a host component (for example
StdioIngressHost) reads newline-delimited JSON-RPC and callsMcpServerentry points.IServerTransportis outbound-only.
🧩 Advanced Usage
ASP.NET Core 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();
Custom Content Types
// 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)
}
}
};
📋 Current Status
This implementation is currently at version 0.9.0:
Fully Implemented Features
- ✅ Core JSON-
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: SamFold
- Source: SamFold/Mcp.Net
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.