Remote MCP Servers using .NET SDK – Integrating with custom data and APIs

Remote MCP Servers using .NET SDK – Integrating with custom data and APIs

A comprehensive example demonstrating how to build Model Context Protocol (MCP) servers using the .NET SDK, showcasing integration with custom data sources and APIs. This project implements a weather forecast service as an example of how to expose your own APIs through MCP tools.

Table of Contents

Overview

This project demonstrates how to create remote MCP servers that can be consumed by AI applications (like Claude Desktop, VS Code Copilot, etc.) to extend their capabilities with custom tools and data sources. The example implementation includes a weather forecast service that showcases the integration pattern.

MCP servers really shine when they’re connected to existing APIs or services, allowing clients to query real, live data. There’s an expanding ecosystem of MCP servers that can already be used by clients, including tools we rely on daily like Git, GitHub, local filesystem, etc.

With that in mind, let’s enhance our MCP server by wiring it up to an API, accepting query parameters, and returning data-driven responses.

What is MCP?

Model Context Protocol (MCP) is an open protocol that standardizes how applications provide context to Large Language Models (LLMs). It enables AI assistants to:

  • Access external data sources
  • Execute custom tools and functions
  • Integrate with your own APIs and services
  • Maintain context across multiple interactions

Features

  • HTTP Transport Support – Expose MCP servers via HTTP endpoints for remote access
  • Stdio Transport Support – Support for standard input/output communication
  • Custom Tool Integration – Easy integration of your own APIs and services
  • Health Monitoring – Built-in health check endpoints
  • Stateless Operation – Designed for scalable, stateless deployments
  • .NET 10.0 – Built on the latest .NET framework
  • Structured Logging – Comprehensive logging for debugging and monitoring

Architecture

The project follows a clean architecture pattern:

┌─────────────────┐
│   MCP Client    │ (Claude Desktop, VS Code, etc.)
└────────┬────────┘
         │ HTTP/SSE
         ▼
┌─────────────────┐
│  ASP.NET Core   │
│   Web Host      │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  MCP Protocol   │
│     Layer       │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   MCP Tools     │ (Weather, Ping, etc.)
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Your APIs &    │
│  Data Sources   │
└─────────────────┘

Prerequisites

Project Structure

remote-MCP-servers-using-dotnet-sdk-integrating-with-our-own-data-or-apis/
├── src/
│   └── McpServer/
│       ├── McpServer/
│       │   ├── Program.cs              # Application entry point
│       │   ├── appsettings.json        # Configuration settings
│       │   ├── McpServerTools.cs       # MCP tool implementations
│       │   └── Services/               # Business logic services
│       │       └── WeatherService.cs   # Weather API integration
│       └── McpServer.sln
├── docs/                               # Additional documentation
├── README.md                           # This file
└── LICENSE

Development

This section explains how to create custom MCP tools and integrate them with your own APIs and data sources.

Creating Custom MCP Tools

MCP tools are the bridge between AI clients and your backend services. Each tool represents a capability that AI assistants can invoke.

Tool Architecture

┌──────────────────┐
│   MCP Client     │ (Claude, VS Code, etc.)
└────────┬─────────┘
         │ "Get weather for Paris"
         ▼
┌──────────────────┐
│ McpServerTools   │ [McpServerTool] decorated methods
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│ Business Service │ IWeatherForecastService
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│  External API    │ Weather API, Database, etc.
└──────────────────┘

Step 1: Define Your Tool Class

Create a class decorated with [McpServerToolType]:

[McpServerToolType]
public sealed class McpServerTools
{
    private readonly IWeatherForecastService _weatherForecastService;
    private readonly ILogger<McpServerTools> _logger;

    public McpServerTools(
        IWeatherForecastService weatherForecastService, 
        ILogger<McpServerTools> logger)
    {
        _weatherForecastService = weatherForecastService;
        _logger = logger;
    }
}

Key Points:

  • Use [McpServerToolType] to mark the class as containing MCP tools
  • Inject services via constructor (supports standard .NET DI)
  • Follow .NET dependency injection patterns

Step 2: Create Tool Methods

Each method decorated with [McpServerTool] becomes an invokable tool:

[McpServerTool]
[Description("Retrieves the current weather forecast for a specified city.")]
public async Task<WeatherForecast> GetWeather(
    [Description("The name of the city to get weather forecast for.")] 
    string city)
{
    _logger.LogInformation("GetWeather called with city: {City}", city);
    
    // Call your business service/API
    var forecasts = await _weatherForecastService.GetWeatherForecast(city);

    _logger.LogInformation("GetWeather returning forecast for city: {City}", city);
    return forecasts;
}

Key Attributes:

  • [McpServerTool] – Marks method as an MCP tool
  • [Description("...")] – Provides description for AI to understand tool purpose
  • Parameter descriptions help AI choose correct arguments

Best Practices:

  • Use clear, descriptive names (e.g., GetWeather, not GW)
  • Add detailed descriptions for both tool and parameters
  • Use strongly-typed return values
  • Include logging for diagnostics
  • Handle exceptions gracefully

Step 3: Implement Your Business Service

Create a service that encapsulates your API/data logic:

public interface IWeatherForecastService
{
    Task<WeatherForecast> GetWeatherForecast(string city);
}

public class WeatherForecastService : IWeatherForecastService
{
    private readonly HttpClient _httpClient;
    private readonly ILogger<WeatherForecastService> _logger;

    public WeatherForecastService(
        HttpClient httpClient, 
        ILogger<WeatherForecastService> logger)
    {
        _httpClient = httpClient;
        _logger = logger;
    }

    public async Task<WeatherForecast> GetWeatherForecast(string city)
    {
        try
        {
            // Call external weather API
            var response = await _httpClient.GetAsync(
                $"https://api.weather.com/v1/forecast?city={city}");
            
            response.EnsureSuccessStatusCode();
            
            var forecast = await response.Content
                .ReadFromJsonAsync<WeatherForecast>();
            
            return forecast ?? throw new InvalidOperationException(
                "Failed to deserialize weather data");
        }
        catch (HttpRequestException ex)
        {
            _logger.LogError(ex, "Failed to fetch weather for {City}", city);
            throw;
        }
    }
}

Step 4: Register Services

In Program.cs, register your services:

var builder = WebApplication.CreateBuilder(args);

// Register MCP server
builder.Services.AddMcpServer();

// Register your business services
builder.Services.AddHttpClient<IWeatherForecastService, WeatherForecastService>(
    client =>
    {
        client.BaseAddress = new Uri("https://api.weather.com");
        client.DefaultRequestHeaders.Add("User-Agent", "MCP-Weather-Server/1.0");
    });

// Add logging
builder.Services.AddLogging(logging =>
{
    logging.AddConsole();
    logging.AddDebug();
});

var app = builder.Build();

// Map MCP endpoint
app.MapMcp("/mcp");

app.Run();

Quick Start

1. Clone the Repository

git clone https://github.com/azurecorner/remote-MCP-servers-using-dotnet-sdk-integrating-with-our-own-data-or-apis.git
cd remote-MCP-servers-using-dotnet-sdk-integrating-with-our-own-data-or-apis

2. Restore Dependencies

cd src/McpServer/McpServer
dotnet restore

3. Run the Server

dotnet run

The server will start on http://localhost:8081 by default.

4. Verify Health

curl http://localhost:8081/api/healthz

Expected response:

StatusCode        : 200
StatusDescription : OK
Content           : Healthy
RawContent        : HTTP/1.1 200 OK
                    Transfer-Encoding: chunked
                    Content-Type: text/plain; charset=utf-8
                    Date: Sun, 25 Jan 2026 10:20:38 GMT
                    Server: Kestrel

Available MCP Tools

This MCP server exposes tools that can be discovered and invoked by MCP clients. To see all available tools, you can query the server using the tools/list JSON-RPC method.

Current Tools

  • get_weather – Retrieves weather information for a specified city

    • Parameters: city (string) – The name of the city
    • Returns: Weather forecast data including temperature, conditions, and humidity
  • ping – Simple echo tool for testing connectivity

    • Parameters: message (string) – Message to echo back
    • Returns: The same message with a timestamp

Discovering Tools

You can discover all available tools by sending a tools/list request to the MCP server:

bash

curl -X POST http://localhost:8081/mcp \
     -H "Content-Type: application/json" \
     -H "Accept: application/json, text/event-stream" \
     -d '{
           "jsonrpc": "2.0",
           "id": 1,
           "method": "tools/list",
           "params": {}
         }'

powershell

# 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

write-Host "Received Response:" -ForegroundColor Green
Write-Host $content -ForegroundColor White

Expected Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "get_weather",
        "description": "Get weather information for a city",
        "inputSchema": {
          "type": "object",
          "properties": {
            "city": {
              "type": "string",
              "description": "The city name"
            }
          },
          "required": ["city"]
        }
      },
      {
        "name": "ping",
        "description": "Echo a message back",
        "inputSchema": {
          "type": "object",
          "properties": {
            "message": {
              "type": "string",
              "description": "Message to echo"
            }
          },
          "required": ["message"]
        }
      }
    ]
  }
}

Usage

Direct API Testing

You can test MCP tools directly by sending JSON-RPC requests to the server endpoint. This is useful for debugging, testing, and understanding how MCP clients interact with your server.

Test Weather Tool

The get_weather tool retrieves weather information for a specified city. Use the following scripts to invoke the tool:

Bash
# Test Weather

# Default parameters
MCP_ENDPOINT="${1:-http://localhost:8081/mcp}"
TOOL_NAME="${2:-get_weather}"
CITY="${3:-Paris}"

# JSON-RPC request body
read -r -d '' BODY <<EOF
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "$TOOL_NAME",
    "arguments": {
      "city": "$CITY"
    }
  }
}
EOF

# Call MCP server and output raw JSON
curl -s -X POST "$MCP_ENDPOINT" \
     -H "Content-Type: application/json" \
     -H "Accept: application/json, text/event-stream" \
     -d "$BODY"
PowerShell
Param(
    [string]$mcpEndpoint = "http://localhost:8081/mcp",
    [string]$toolName = "get_weather",
    [hashtable]$toolParams = @{ city = "Paris" }
)
# Example usage:
#  dotnet run --project .\src\McpServer\McpServer\McpServer.csproj
# .\call-mcp-tool.ps1 -toolName "get_weather" -toolParams @{ city = "Paris" }
# .\call-mcp-tool.ps1 -toolName "ping" -mcpEndpoint http://localhost:8081/mcp  -toolParams @{ message = "hello" }


$mcpEndpoint = "http://localhost:8081/mcp"

$body = @{
    jsonrpc = "2.0"
    id      = 2
    method  = "tools/call"
    params  = @{
        name = $toolName
        arguments = $toolParams
    }
} | ConvertTo-Json -Depth 5

$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 "Success!" -ForegroundColor Green
write-host $response.Content | ConvertTo-Json

Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License – see the LICENSE file for details.


Acknowledgments


Author

Gora LEYE

https://logcorner.com/

For questions or support, please open an issue on GitHub.

Support us

BMC logoBuy me a coffee