# Mcp Net

> A fully featured C# implementation of Anthropic's Model Context Protocol (MCP)

- **Type:** MCP server
- **Install:** `agentstack add mcp-samfold-mcp-net`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [SamFold](https://agentstack.voostack.com/s/samfold)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [SamFold](https://github.com/SamFold)
- **Source:** https://github.com/SamFold/Mcp.Net

## Install

```sh
agentstack add mcp-samfold-mcp-net
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## 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:

```bash
# 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

```bash
# 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

```bash
# 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`:

```csharp
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

```csharp
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:

```csharp
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

```csharp
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 `resource` indicators
  - ✅ 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

```csharp
// 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:

```bash
# 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:

```csharp
// 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

```bash
# 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:

```csharp
// 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:

```json
{
  "Server": {
    "Port": 8080,
    "Hostname": "0.0.0.0",
    "Scheme": "http"
  }
}
```

The configuration is loaded with a tiered priority approach:

```csharp
// 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:

1. Command line arguments (highest priority)
2. Environment variables
3. appsettings.json configuration
4. 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 to `Timeout.InfiniteTimeSpan` to 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 calls `McpServer` entry points. `IServerTransport` is outbound-only.

## 🧩 Advanced Usage

### ASP.NET Core Integration

```csharp
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

```csharp
// 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](https://github.com/SamFold)
- **Source:** [SamFold/Mcp.Net](https://github.com/SamFold/Mcp.Net)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-samfold-mcp-net
- Seller: https://agentstack.voostack.com/s/samfold
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
