Remote MCP Servers using .NET
Remote MCP Servers using .NET: Getting Started
Built using .NET 10 and the Model Context Protocol
A .NET 10 implementation of a Model Context Protocol (MCP) server that demonstrates how to build scalable, stateless, remote MCP servers using ASP.NET Core with support for Azure Functions deployment.
Features
- HTTP Transport: Stateless HTTP-based MCP server for remote access and horizontal scalability
- Server-Sent Events (SSE): Supports streaming responses using SSE with JSON-RPC 2.0 protocol
- Diagnostic Tools: Built-in health check and ping functionality for server monitoring
- Structured Logging: Console logging with configurable trace levels for debugging
- Modern .NET: Built on .NET 10 with C# 14.0 and minimal APIs
- JSON-RPC 2.0: Standard protocol implementation for tool invocation
Prerequisites
- .NET 10 SDK or later
- Visual Studio 2026, VS Code, or any .NET-compatible IDE
- PowerShell 5.1+ or PowerShell Core 7+ (for testing scripts)
Project Structure
remote-MCP-servers-using-dotnet/
├── .github/
│ └── workflows/ # CI/CD workflows
├── .vscode/
│ └── mcp.json # VS Code MCP configuration
├── src/
│ └── McpServer/
│ ├── McpServer/
│ │ ├── Program.cs # Application entry point and configuration
│ │ ├── McpServerTools.cs # MCP tool definitions (Ping, etc.)
│ │ ├── McpServer.csproj # Project file with dependencies
│ │ ├── appsettings.json # Application configuration
│ │ └── Properties/
│ │ └── launchSettings.json # Development launch profiles
│ ├── McpServer.slnx # Solution file
│ └── README.md # This documentation
├── test-mcp-server.ps1 # Quick test script
├── list-mcp-server-tools.ps1 # Tool listing script
├── README.md # Project overview
└── LICENSE # MIT License
Core Files
Program.cs – Application Bootstrap and Configuration
The main entry point that configures and runs the MCP server using ASP.NET Core minimal APIs.
Key Responsibilities:
- Web Host Configuration: Configures the server to listen on all network interfaces (
0.0.0.0) on port 8081 (default) or the port specified byMCP_PORTenvironment variable for Azure Functions compatibility - MCP Server Registration: Registers the MCP server with HTTP transport in stateless mode for horizontal scalability
- Tool Registration: Registers
McpServerToolsclass containing tool implementations usingWithTools<McpServerTools>() - Logging Configuration: Sets up console logging with trace-level output to stderr for container/cloud compatibility
- Endpoint Mapping:
/api/healthz(GET) – Simple health check endpoint returning “Healthy” for liveness probes/mcp(POST) – Main MCP protocol endpoint for JSON-RPC 2.0 requests
Configuration Highlights:
// Port binding with Azure Functions support
var port = Environment.GetEnvironmentVariable("MCP_PORT") ?? "8081";
builder.WebHost.UseUrls($"http://0.0.0.0:{port}");
// MCP server with stateless HTTP transport
builder.Services.AddMcpServer()
.WithHttpTransport((options) => { options.Stateless = true; })
.WithTools<McpServerTools>();
// Health check and MCP endpoints
app.MapGet("/api/healthz", () => "Healthy");
app.MapMcp(pattern: "/mcp");
McpServerTools.cs – MCP Tool Implementations
Contains the tool implementations exposed via the MCP protocol. Tools are automatically discovered and registered through attributes.
Architecture:
- Class-level Attribute:
[McpServerToolType]marks the class as a tool container - Method-level Attributes:
[McpServerTool]marks methods as invocable tools - Parameter Attributes:
[Description]provides metadata for parameters
Current Tool: Ping
A diagnostic tool that serves dual purposes:
- Health Check: Returns server status when called with an empty message
- Echo Service: Returns the message back with confirmation when provided
Implementation Details:
[McpServerTool, Description(
"Health check tool. Behaves as follows:\n" +
"- If message is empty, respond with server health\n" +
"- Otherwise echo the message"
)]
public async Task<string> Ping([Description("Ping message")] string message)
{
await Task.CompletedTask;
return string.IsNullOrWhiteSpace(message)
? "✅ MCP server is alive."
: $"✅ MCP server received: {message}";
}
Tool Characteristics:
- Name:
Ping(derived from method name) - Input Parameter:
message(string) – Message to echo or empty for health check - Return Type:
string– Status message with emoji for visual confirmation - Async Support: Uses async/await pattern for consistency with I/O-bound operations
- Null Safety: Handles empty, null, and whitespace-only messages
Usage Scenarios:
- Liveness Probe: Call with empty message to verify server is responding
- Connectivity Test: Send a test message to verify request/response cycle
- Latency Measurement: Measure round-trip time for diagnostic purposes
Extensibility:
To add more tools, simply add new methods with the [McpServerTool] attribute:
[McpServerTool, Description("Description of your tool")]
public async Task<string> YourToolName(
[Description("Parameter description")] string param1)
{
// Tool implementation
return "result";
}
Tools are automatically:
- Discovered at startup via reflection
- Exposed through the
/mcpendpoint - Documented in
tools/listJSON-RPC responses - Invoked via
tools/callJSON-RPC requests
Configuration
Environment Variables
| Variable | Description | Default |
|---|---|---|
ASPNETCORE_ENVIRONMENT |
Environment (Development/Production) | Production |
ASPNETCORE_URLS |
Server binding URLs | http://0.0.0.0:{PORT} |
Application Settings
Configure in appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
MCP Server Configuration
The server is configured in Program.cs with:
- HTTP transport: RESTful API with JSON-RPC 2.0 and SSE support
- Console logging: Standard error output with trace-level diagnostics
- Health endpoint:
/api/healthzfor monitoring and liveness checks - MCP endpoint:
/mcpfor protocol communication
Architecture
Technology Stack
- ASP.NET Core Minimal APIs: Lightweight web framework for high performance
- Model Context Protocol SDK: Official MCP .NET SDK for protocol implementation
- JSON-RPC 2.0: Standard protocol for remote procedure calls
- Server-Sent Events (SSE): Streaming response format for real-time updates
Key Components
| Component | Purpose | Location |
|---|---|---|
Program.cs |
Application bootstrap, DI configuration, middleware pipeline | src/McpServer/McpServer/Program.cs |
McpServerTools.cs |
Tool implementations decorated with [McpServerTool] |
src/McpServer/McpServer/McpServerTools.cs |
Request Flow
Client Request → HTTP Transport → JSON-RPC 2.0 → MCP Server → Tool Execution → SSE Response
- Client sends POST request to
/mcpwith JSON-RPC payload - HTTP Transport validates headers (
Accept: application/json, text/event-stream) - MCP Server parses JSON-RPC request and routes to appropriate tool
- Tool executes business logic (e.g., Ping tool)
- Server formats response as Server-Sent Events (SSE)
- Client receives streaming response with result
Installation
-
Clone the repository:
git clone https://github.com/azurecorner/remote-MCP-servers-using-dotnet.git cd remote-MCP-servers-using-dotnet -
Restore dependencies:
cd src/McpServer/McpServer dotnet restore -
Build the project:
dotnet build
Running Locally
Start the MCP server:
dotnet run --project McpServer.csproj
Expected Output:
Using launch settings from Properties\launchSettings.json...
Building...
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://0.0.0.0:8081
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
The server will be available at http://localhost:8081
Usage
Health Check Endpoint
Verify the server is running:
curl http://localhost:8081/api/healthz
PowerShell:
Invoke-WebRequest -Uri "http://localhost:8081/api/healthz" -UseBasicParsing
# Output:
# StatusCode: 200
# Content: Healthy
MCP Tools Endpoint
The MCP server exposes tools via the /mcp endpoint using JSON-RPC 2.0 protocol.
** Important**: The server requires both application/json and text/event-stream in the Accept header.
Test MCP Tools Endpoint
PowerShell:
$mcpEndpoint = "http://localhost:8081/mcp"
$body = @{
jsonrpc = "2.0"
id = 1
method = "tools/list"
params = @{}
} | ConvertTo-Json
$headers = @{
"Content-Type" = "application/json"
"Accept" = "application/json, text/event-stream"
}
$response = Invoke-WebRequest -Uri $mcpEndpoint `
-Method Post `
-Headers $headers `
-Body $body `
-UseBasicParsing
Write-Host $response.Content
Response (SSE Format):
event: message
data: {"result":{"tools":[{"name":"ping","description":"Health check tool...","inputSchema":{...}}]},"id":1,"jsonrpc":"2.0"}
List Available Tools
Invoke the Ping Tool
# MCP endpoint
$mcpEndpoint = "http://localhost:8081/mcp"
# JSON-RPC request body
$body = @{
jsonrpc = "2.0"
id = 1
method = "tools/list"
params = @{}
} | ConvertTo-Json -Depth 5
# HTTP headers
$headers = @{
"Content-Type" = "application/json"
"Accept" = "application/json, text/event-stream"
}
# Send request
$response = Invoke-WebRequest `
-Uri $mcpEndpoint `
-Method Post `
-Headers $headers `
-Body $body `
-UseBasicParsing
# Read response content
$content = $response.Content
# Parse Server-Sent Events (SSE) JSON payload
if ($content -match 'data:\s*(.+)') {
$jsonData = $matches[1] | ConvertFrom-Json
$jsonData.result.tools | ForEach-Object {
Write-Host "`nTool: $($_.name)" -ForegroundColor Cyan
Write-Host "Description: $($_.description)" -ForegroundColor Gray
Write-Host "Input Schema:" -ForegroundColor Yellow
$_.inputSchema | ConvertTo-Json -Depth 10 | Write-Host -ForegroundColor White
}
}
Write-Host "Success!" -ForegroundColor Green
Testing Scripts
The repository includes PowerShell scripts for testing:
test-mcp-server.ps1: Quick test to verify server connectivity and tool listinglist-mcp-server-tools.ps1: Formatted display of all available MCP tools
Run a test:
.\test-mcp-server.ps1
./test-mcp-server.ps1
Additional Resources
- Model Context Protocol Documentation
- MCP .NET SDK
- JSON-RPC 2.0 Specification
- Server-Sent Events (SSE)
- ASP.NET Core Documentation
Source code
https://github.com/azurecorner/remote-MCP-servers-using-dotnet.git


