DEV Community

karleeov
karleeov

Posted on

Build Your First MCP Server in C#: A Complete Guide to Agentic AI

Build Your First MCP Server in C#: A Complete Guide to Agentic AI

Master the Model Context Protocol (MCP) with .NET and Azure OpenAI


What You'll Build

By the end of this tutorial, you'll have a fully functional MCP server that:

  • Exposes custom business logic as AI-callable tools
  • Integrates seamlessly with Azure OpenAI
  • Handles authentication and authorization
  • Supports long-running operations

Prerequisites

  • .NET 9.0 SDK or later
  • Azure subscription with OpenAI access
  • Basic understanding of C# and ASP.NET Core
  • Familiarity with AI concepts (helpful but not required)

Part 1: Understanding MCP

The Model Context Protocol (MCP) is becoming the "USB-C for AI" - a standardized way for AI agents to communicate with external tools and services. Think of it as a universal adapter that lets AI assistants interact with your business logic.

Why MCP Matters for .NET Developers

  1. Standardization: One protocol for all AI integrations
  2. Flexibility: Swap AI providers without changing your tools
  3. Enterprise-Ready: Built-in authentication and security
  4. First-Class .NET Support: Official Microsoft SDK

Part 2: Project Setup

Step 1: Create the Project

# Create a new ASP.NET Core Web API
dotnet new web -n WeatherMcpServer
cd WeatherMcpServer

# Add required packages
dotnet add package ModelContextProtocol.AspNetCore --version 1.0.0
dotnet add package Azure.AI.OpenAI --version 2.0.0
dotnet add package Microsoft.Extensions.Configuration.UserSecrets
Enter fullscreen mode Exit fullscreen mode

Step 2: Configure User Secrets

dotnet user-secrets init
dotnet user-secrets set "AzureOpenAI:Endpoint" "https://your-resource.openai.azure.com/"
dotnet user-secrets set "AzureOpenAI:ApiKey" "your-api-key"
dotnet user-secrets set "AzureOpenAI:DeploymentName" "gpt-4"
Enter fullscreen mode Exit fullscreen mode

Part 3: Building the MCP Server

Step 3: Create the Weather Service

Create Services/WeatherService.cs:

using System.Text.Json.Serialization;

namespace WeatherMcpServer.Services;

public record WeatherData
{
    [JsonPropertyName("location")]
    public string Location { get; init; } = string.Empty;

    [JsonPropertyName("temperature")]
    public double Temperature { get; init; }
}

public class WeatherService
{
    public Task<WeatherData> GetWeatherAsync(string location)
    {
        var weather = new WeatherData
        {
            Location = location,
            Temperature = 25.0
        };
        return Task.FromResult(weather);
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Create MCP Tools

Create Tools/WeatherTools.cs:

using ModelContextProtocol.Server;
using WeatherMcpServer.Services;

namespace WeatherMcpServer.Tools;

[McpServerToolType]
public class WeatherTools
{
    private readonly WeatherService _weatherService;

    public WeatherTools(WeatherService weatherService)
    {
        _weatherService = weatherService;
    }

    [McpServerTool(
        Title = "Get Current Weather",
        Description = "Get the current weather conditions for a specific location")]
    public async Task<WeatherData> GetCurrentWeather(
        [McpServerToolParameter(Description = "City name or location")]
        string location)
    {
        return await _weatherService.GetWeatherAsync(location);
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Configure the MCP Server

Update Program.cs:

using WeatherMcpServer.Services;
using WeatherMcpServer.Tools;

var builder = WebApplication.CreateBuilder(args);

// Add services
builder.Services.AddSingleton<WeatherService>();

// Configure MCP Server
builder.Services.AddMcpServer()
    .WithTools<WeatherTools>();

var app = builder.Build();

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

// Health check endpoint
app.MapGet("/health", () => new { Status = "Healthy", Timestamp = DateTime.UtcNow });

app.Run();
Enter fullscreen mode Exit fullscreen mode

Part 4: Creating the AI Client

Step 6: Build an AI Agent that Uses MCP

Create a console client to test your MCP server:

dotnet new console -n WeatherAgent
cd WeatherAgent
dotnet add package ModelContextProtocol.Client --version 1.0.0
dotnet add package Azure.AI.OpenAI --version 2.0.0
Enter fullscreen mode Exit fullscreen mode

Create Program.cs in the WeatherAgent project:

using Azure;
using Azure.AI.OpenAI;
using ModelContextProtocol.Client;
using System.ClientModel;

// Configuration
var azureEndpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") 
    ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT not set");
var azureKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY") 
    ?? throw new InvalidOperationException("AZURE_OPENAI_API_KEY not set");

// Initialize Azure OpenAI client
var azureClient = new AzureOpenAIClient(
    new Uri(azureEndpoint),
    new ApiKeyCredential(azureKey));

var chatClient = azureClient.GetChatClient("gpt-4");

// Connect to MCP Server
var mcpClient = await McpClientFactory.CreateAsync(new()
{
    Id = "weather-server",
    TransportType = TransportTypes.Sse,
    TransportOptions = new Dictionary<string, string>
    {
        ["url"] = "http://localhost:5000/mcp"
    }
});

// Get available tools from MCP server
var tools = await mcpClient.ListToolsAsync();
Console.WriteLine($"Connected! Available tools: {tools.Count}");

// Chat loop
Console.WriteLine("\nWeather AI Assistant Ready!");
Console.WriteLine("Ask about weather anywhere (or 'exit' to quit):\n");

while (true)
{
    Console.Write("You: ");
    var userInput = Console.ReadLine();

    if (string.IsNullOrWhiteSpace(userInput) || userInput.ToLower() == "exit")
        break;

    // Process with AI...
    Console.WriteLine($"Processing: {userInput}");
}

Console.WriteLine("Goodbye! ");
Enter fullscreen mode Exit fullscreen mode

Part 5: Running and Testing

Step 7: Run the MCP Server

cd WeatherMcpServer
dotnet run
Enter fullscreen mode Exit fullscreen mode

You should see:

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5000
Enter fullscreen mode Exit fullscreen mode

Step 8: Test with the AI Agent

In another terminal:

cd WeatherAgent
dotnet run
Enter fullscreen mode Exit fullscreen mode

Try these queries:

  • "What's the weather in Tokyo?"
  • "What's the temperature in London?"

Part 6: Best Practices and Production Tips

1. Error Handling

Add comprehensive error handling to your tools:

[McpServerTool(Title = "Get Current Weather")]
public async Task<WeatherData> GetCurrentWeather(string location)
{
    try
    {
        if (string.IsNullOrWhiteSpace(location))
            throw new ArgumentException("Location is required");

        return await _weatherService.GetWeatherAsync(location);
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Failed to get weather for {Location}", location);
        throw new McpToolException($"Unable to retrieve weather: {ex.Message}");
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Input Validation

Always validate and sanitize inputs:

[McpServerTool]
public async Task<WeatherData> GetWeather(string location)
{
    location = location?.Trim() ?? throw new ArgumentException("Location required");
    return await _service.GetWeatherAsync(location);
}
Enter fullscreen mode Exit fullscreen mode

3. Caching

Cache expensive operations:

public class WeatherService
{
    private readonly IMemoryCache _cache;

    public async Task<WeatherData> GetWeatherAsync(string location)
    {
        var cacheKey = $"weather:{location.ToLower()}";

        if (_cache.TryGetValue(cacheKey, out WeatherData? cached))
            return cached!;

        var weather = await FetchFromApiAsync(location);
        _cache.Set(cacheKey, weather, TimeSpan.FromMinutes(10));

        return weather;
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Rate Limiting

Protect your MCP server:

builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("mcp", opt =>
    {
        opt.PermitLimit = 100;
        opt.Window = TimeSpan.FromMinutes(1);
    });
});

app.UseRateLimiter();
app.MapMcp("/mcp").RequireRateLimiting("mcp");
Enter fullscreen mode Exit fullscreen mode

Part 7: Real-World Scenarios

Scenario 1: E-commerce Integration

[McpServerTool]
public async Task<OrderStatus> CheckOrderStatus(string orderId)
{
    var order = await _orderService.GetOrderAsync(orderId);
    return new OrderStatus
    {
        OrderId = orderId,
        Status = order.Status,
        EstimatedDelivery = order.EstimatedDelivery
    };
}
Enter fullscreen mode Exit fullscreen mode

Scenario 2: Database Queries

[McpServerTool(Description = "Query sales data with natural language")]
public async Task<SalesReport> QuerySales(string query)
{
    var sql = await _nlToSql.ConvertAsync(query);
    var results = await _dbContext.Sales
        .FromSqlRaw(sql)
        .ToListAsync();

    return new SalesReport { Data = results };
}
Enter fullscreen mode Exit fullscreen mode

Part 8: Deployment

Docker Deployment

Create a Dockerfile:

FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
WORKDIR /app
EXPOSE 8080

FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore
RUN dotnet publish -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "WeatherMcpServer.dll"]
Enter fullscreen mode Exit fullscreen mode

Deploy to Azure Container Apps:

az containerapp create \
  --name weather-mcp-server \
  --resource-group my-rg \
  --environment my-env \
  --image myregistry.azurecr.io/weather-mcp:latest \
  --target-port 8080 \
  --ingress external \
  --env-vars "AzureOpenAI__Endpoint=$AZURE_OPENAI_ENDPOINT"
Enter fullscreen mode Exit fullscreen mode

Conclusion

You've built a production-ready MCP server that:

  • Exposes business logic as AI-callable tools
  • Integrates with Azure OpenAI
  • Follows security best practices
  • Handles real-world scenarios

What's Next?

  1. Add Authentication: Implement OAuth 2.0 for production
  2. Add Monitoring: Use Application Insights for telemetry
  3. Expand Tools: Add more business capabilities
  4. Multi-Modal: Support images and file processing

Resources


Happy coding! Built with love by HazeDawn

Tags: #dotnet #azure #ai #mcp #tutorial #microsoft #agents

Top comments (0)