Skip to content

C# SDK

Model Context Protocol (MCP) 的 C# SDK 提供了构建 MCP 服务器和客户端的完整 .NET 解决方案,支持 .NET 6+ 和现代 C# 特性。

安装

NuGet 包管理器

bash
dotnet add package ModelContextProtocol.Sdk

Package Manager Console

powershell
Install-Package ModelContextProtocol.Sdk

PackageReference

xml
<PackageReference Include="ModelContextProtocol.Sdk" Version="1.0.0" />

快速开始

创建 MCP 服务器

csharp
using ModelContextProtocol.Sdk.Server;
using ModelContextProtocol.Sdk.Tools;
using ModelContextProtocol.Sdk.Schema;
using System.Text.Json;

namespace MyMcpServer;

public class Program
{
    public static async Task Main(string[] args)
    {
        // 创建服务器
        var server = new McpServerBuilder()
            .WithName("my-csharp-server")
            .WithVersion("1.0.0")
            .WithDescription("C# MCP 服务器示例")
            .Build();
        
        // 注册工具
        server.RegisterTool<EchoTool>();
        
        // 启动服务器
        await server.StartAsync();
    }
}

public class EchoTool : ITool
{
    public string Name => "echo";
    public string Description => "回显输入的消息";
    
    public JsonSchema InputSchema => JsonSchema.CreateObject()
        .WithProperty("message", JsonSchema.CreateString()
            .WithDescription("要回显的消息"))
        .WithRequired("message");
    
    public async Task<ToolResult> CallAsync(ToolCall call, CancellationToken cancellationToken = default)
    {
        if (!call.Arguments.TryGetValue("message", out var messageElement))
        {
            return ToolResult.Error("缺少 message 参数");
        }
        
        var message = messageElement.GetString();
        var result = new { echo = message };
        
        return ToolResult.Success(JsonSerializer.SerializeToElement(result));
    }
}

创建 MCP 客户端

csharp
using ModelContextProtocol.Sdk.Client;
using ModelContextProtocol.Sdk.Transport;
using System.Text.Json;

namespace MyMcpClient;

public class Program
{
    public static async Task Main(string[] args)
    {
        // 创建传输层
        var transport = new StdioTransport();
        
        // 创建客户端
        var client = new McpClientBuilder()
            .WithTransport(transport)
            .WithConnectTimeout(TimeSpan.FromSeconds(10))
            .WithRequestTimeout(TimeSpan.FromSeconds(30))
            .Build();
        
        try
        {
            // 连接到服务器
            await client.ConnectAsync();
            
            // 列出可用工具
            var tools = await client.ListToolsAsync();
            Console.WriteLine($"可用工具: {string.Join(", ", tools.Select(t => t.Name))}");
            
            // 调用工具
            var args = new Dictionary<string, JsonElement>
            {
                ["message"] = JsonSerializer.SerializeToElement("Hello from C#!")
            };
            
            var result = await client.CallToolAsync("echo", args);
            Console.WriteLine($"工具调用结果: {result}");
        }
        finally
        {
            await client.CloseAsync();
        }
    }
}

核心功能

服务器功能

工具注册

csharp
// 简单计算器工具
public class CalculatorTool : ITool
{
    public string Name => "calculator";
    public string Description => "执行基本数学运算";
    
    public JsonSchema InputSchema => JsonSchema.CreateObject()
        .WithProperty("operation", JsonSchema.CreateString()
            .WithEnum("add", "subtract", "multiply", "divide"))
        .WithProperty("a", JsonSchema.CreateNumber()
            .WithDescription("第一个数字"))
        .WithProperty("b", JsonSchema.CreateNumber()
            .WithDescription("第二个数字"))
        .WithRequired("operation", "a", "b");
    
    public async Task<ToolResult> CallAsync(ToolCall call, CancellationToken cancellationToken = default)
    {
        var operation = call.Arguments["operation"].GetString();
        var a = call.Arguments["a"].GetDouble();
        var b = call.Arguments["b"].GetDouble();
        
        var result = operation switch
        {
            "add" => a + b,
            "subtract" => a - b,
            "multiply" => a * b,
            "divide" when b != 0 => a / b,
            "divide" => throw new InvalidOperationException("除数不能为零"),
            _ => throw new ArgumentException($"不支持的运算: {operation}")
        };
        
        return ToolResult.Success(JsonSerializer.SerializeToElement(new { result }));
    }
}

// 注册工具
server.RegisterTool<CalculatorTool>();

// 或者注册实例
server.RegisterTool(new CalculatorTool());

资源管理

csharp
using ModelContextProtocol.Sdk.Resources;

public class FileResourceProvider : IResourceProvider
{
    private readonly string _basePath;
    
    public FileResourceProvider(string basePath)
    {
        _basePath = basePath;
    }
    
    public async Task<Resource> GetResourceAsync(ResourceUri uri, CancellationToken cancellationToken = default)
    {
        var filePath = Path.Combine(_basePath, uri.Path.TrimStart('/'));
        
        if (!File.Exists(filePath))
        {
            throw new FileNotFoundException($"资源不存在: {uri}");
        }
        
        var content = await File.ReadAllTextAsync(filePath, cancellationToken);
        
        return new Resource
        {
            Uri = uri,
            MimeType = "text/plain",
            Content = content
        };
    }
    
    public async Task<IEnumerable<ResourceUri>> ListResourcesAsync(CancellationToken cancellationToken = default)
    {
        var files = Directory.GetFiles(_basePath, "*", SearchOption.AllDirectories);
        
        return files.Select(file =>
        {
            var relativePath = Path.GetRelativePath(_basePath, file);
            return new ResourceUri($"file:///{relativePath.Replace('\\', '/')}");
        });
    }
}

// 注册资源提供者
server.RegisterResourceProvider(new FileResourceProvider("/path/to/files"));

提示模板

csharp
using ModelContextProtocol.Sdk.Prompts;

public class CodeReviewPromptProvider : IPromptProvider
{
    public async Task<Prompt> GetPromptAsync(string name, Dictionary<string, JsonElement> arguments, CancellationToken cancellationToken = default)
    {
        return name switch
        {
            "code-review" => await CreateCodeReviewPrompt(arguments, cancellationToken),
            _ => throw new ArgumentException($"未知提示: {name}")
        };
    }
    
    public Task<IEnumerable<string>> ListPromptsAsync(CancellationToken cancellationToken = default)
    {
        return Task.FromResult<IEnumerable<string>>(new[] { "code-review" });
    }
    
    private async Task<Prompt> CreateCodeReviewPrompt(Dictionary<string, JsonElement> arguments, CancellationToken cancellationToken)
    {
        if (!arguments.TryGetValue("code", out var codeElement))
        {
            throw new ArgumentException("缺少 code 参数");
        }
        
        var code = codeElement.GetString();
        var language = arguments.TryGetValue("language", out var langElement) 
            ? langElement.GetString() 
            : "unknown";
        
        var content = $"""
            请审查以下 {language} 代码并提供改进建议:
            
            ```{language}
            {code}
            ```
            """;
        
        return new Prompt
        {
            Name = "code-review",
            Description = "代码审查提示",
            Content = content
        };
    }
}

// 注册提示提供者
server.RegisterPromptProvider(new CodeReviewPromptProvider());

客户端功能

连接管理

csharp
// 自动重连客户端
var client = new McpClientBuilder()
    .WithTransport(transport)
    .WithAutoReconnect(true)
    .WithMaxReconnectAttempts(5)
    .WithReconnectDelay(TimeSpan.FromSeconds(2))
    .Build();

// 连接状态事件
client.Connected += (sender, e) => Console.WriteLine("已连接到服务器");
client.Disconnected += (sender, e) => Console.WriteLine("与服务器断开连接");
client.Error += (sender, e) => Console.WriteLine($"连接错误: {e.Exception.Message}");

批量操作

csharp
// 并发调用多个工具
var tasks = new[]
{
    client.CallToolAsync("tool1", args1),
    client.CallToolAsync("tool2", args2),
    client.CallToolAsync("tool3", args3)
};

var results = await Task.WhenAll(tasks);

foreach (var (result, index) in results.Select((r, i) => (r, i)))
{
    Console.WriteLine($"结果 {index + 1}: {result}");
}

传输协议

Stdio 传输

csharp
var transport = new StdioTransportBuilder()
    .WithCommand("python")
    .WithArguments("server.py")
    .WithWorkingDirectory("/path/to/server")
    .WithEnvironmentVariable("ENV_VAR", "value")
    .Build();

WebSocket 传输

csharp
var transport = new WebSocketTransportBuilder()
    .WithUri("ws://localhost:8080/mcp")
    .WithHeader("Authorization", "Bearer token")
    .WithConnectTimeout(TimeSpan.FromSeconds(10))
    .Build();

HTTP SSE 传输

csharp
var transport = new SseTransportBuilder()
    .WithUri("http://localhost:8080/mcp")
    .WithHeader("Authorization", "Bearer token")
    .WithReadTimeout(TimeSpan.FromSeconds(30))
    .Build();

高级特性

依赖注入支持

csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

public class Program
{
    public static async Task Main(string[] args)
    {
        var host = Host.CreateDefaultBuilder(args)
            .ConfigureServices(services =>
            {
                // 注册 MCP 服务
                services.AddMcp(options =>
                {
                    options.ServerName = "my-server";
                    options.ServerVersion = "1.0.0";
                });
                
                // 注册工具
                services.AddScoped<ITool, DatabaseTool>();
                services.AddScoped<ITool, EmailTool>();
                
                // 注册资源提供者
                services.AddScoped<IResourceProvider, FileResourceProvider>();
                
                // 注册其他服务
                services.AddScoped<IEmailService, EmailService>();
                services.AddDbContext<AppDbContext>();
            })
            .Build();
        
        await host.RunAsync();
    }
}

// 带依赖注入的工具
public class DatabaseTool : ITool
{
    private readonly AppDbContext _context;
    private readonly ILogger<DatabaseTool> _logger;
    
    public DatabaseTool(AppDbContext context, ILogger<DatabaseTool> logger)
    {
        _context = context;
        _logger = logger;
    }
    
    public string Name => "database-query";
    public string Description => "执行数据库查询";
    
    public JsonSchema InputSchema => JsonSchema.CreateObject()
        .WithProperty("query", JsonSchema.CreateString()
            .WithDescription("SQL 查询语句"));
    
    public async Task<ToolResult> CallAsync(ToolCall call, CancellationToken cancellationToken = default)
    {
        var query = call.Arguments["query"].GetString();
        
        _logger.LogInformation("执行查询: {Query}", query);
        
        // 执行查询逻辑
        var results = await _context.Database.SqlQueryRaw<object>(query).ToListAsync(cancellationToken);
        
        return ToolResult.Success(JsonSerializer.SerializeToElement(results));
    }
}

中间件支持

csharp
public class LoggingMiddleware : IMiddleware
{
    private readonly ILogger<LoggingMiddleware> _logger;
    
    public LoggingMiddleware(ILogger<LoggingMiddleware> logger)
    {
        _logger = logger;
    }
    
    public async Task<Response> HandleAsync(Request request, MiddlewareDelegate next, CancellationToken cancellationToken = default)
    {
        _logger.LogInformation("处理请求: {Method}", request.Method);
        var stopwatch = Stopwatch.StartNew();
        
        try
        {
            var response = await next(request, cancellationToken);
            
            stopwatch.Stop();
            _logger.LogInformation("请求完成,耗时: {Duration}ms", stopwatch.ElapsedMilliseconds);
            
            return response;
        }
        catch (Exception ex)
        {
            stopwatch.Stop();
            _logger.LogError(ex, "请求处理失败,耗时: {Duration}ms", stopwatch.ElapsedMilliseconds);
            throw;
        }
    }
}

// 注册中间件
services.AddMcp(options =>
{
    options.UseMiddleware<LoggingMiddleware>();
    options.UseMiddleware<AuthenticationMiddleware>();
    options.UseMiddleware<RateLimitingMiddleware>();
});

配置管理

csharp
// appsettings.json
{
  "Mcp": {
    "Server": {
      "Name": "my-server",
      "Version": "1.0.0",
      "Description": "我的 MCP 服务器",
      "MaxConnections": 100,
      "RequestTimeout": "00:00:30"
    },
    "Logging": {
      "Level": "Information",
      "File": "/var/log/mcp-server.log"
    },
    "Transport": {
      "Type": "Stdio",
      "Options": {
        "BufferSize": 8192
      }
    }
  }
}

// 配置类
public class McpOptions
{
    public ServerOptions Server { get; set; } = new();
    public LoggingOptions Logging { get; set; } = new();
    public TransportOptions Transport { get; set; } = new();
}

public class ServerOptions
{
    public string Name { get; set; } = "";
    public string Version { get; set; } = "";
    public string Description { get; set; } = "";
    public int MaxConnections { get; set; } = 100;
    public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(30);
}

// 使用配置
services.Configure<McpOptions>(configuration.GetSection("Mcp"));
services.AddMcp();

测试

单元测试

csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Xunit;

public class EchoToolTests
{
    [Fact]
    public async Task CallAsync_WithValidMessage_ReturnsEcho()
    {
        // Arrange
        var tool = new EchoTool();
        var call = new ToolCall
        {
            Name = "echo",
            Arguments = new Dictionary<string, JsonElement>
            {
                ["message"] = JsonSerializer.SerializeToElement("test")
            }
        };
        
        // Act
        var result = await tool.CallAsync(call);
        
        // Assert
        Assert.True(result.IsSuccess);
        Assert.Equal("test", result.Content.GetProperty("echo").GetString());
    }
    
    [Fact]
    public async Task CallAsync_WithMissingMessage_ReturnsError()
    {
        // Arrange
        var tool = new EchoTool();
        var call = new ToolCall
        {
            Name = "echo",
            Arguments = new Dictionary<string, JsonElement>()
        };
        
        // Act
        var result = await tool.CallAsync(call);
        
        // Assert
        Assert.True(result.IsError);
        Assert.Contains("缺少 message 参数", result.ErrorMessage);
    }
}

集成测试

csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Xunit;

public class McpIntegrationTests : IAsyncLifetime
{
    private IHost _host;
    private McpClient _client;
    
    public async Task InitializeAsync()
    {
        // 启动测试服务器
        _host = Host.CreateDefaultBuilder()
            .ConfigureServices(services =>
            {
                services.AddMcp(options =>
                {
                    options.ServerName = "test-server";
                });
                services.AddScoped<ITool, EchoTool>();
            })
            .Build();
        
        await _host.StartAsync();
        
        // 创建客户端
        var transport = new StdioTransport();
        _client = new McpClientBuilder()
            .WithTransport(transport)
            .Build();
        
        await _client.ConnectAsync();
    }
    
    public async Task DisposeAsync()
    {
        await _client?.CloseAsync();
        await _host?.StopAsync();
        _host?.Dispose();
    }
    
    [Fact]
    public async Task CallTool_Echo_ReturnsExpectedResult()
    {
        // Arrange
        var args = new Dictionary<string, JsonElement>
        {
            ["message"] = JsonSerializer.SerializeToElement("test")
        };
        
        // Act
        var result = await _client.CallToolAsync("echo", args);
        
        // Assert
        Assert.Equal("test", result.GetProperty("echo").GetString());
    }
}

部署

ASP.NET Core 集成

csharp
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

// 添加 MCP 服务
builder.Services.AddMcp();
builder.Services.AddScoped<ITool, WebApiTool>();

var app = builder.Build();

// 配置 MCP 端点
app.MapMcp("/mcp");

// 启动应用
app.Run();

Docker 部署

dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["MyMcpServer.csproj", "."]
RUN dotnet restore "MyMcpServer.csproj"
COPY . .
WORKDIR "/src"
RUN dotnet build "MyMcpServer.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "MyMcpServer.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyMcpServer.dll"]

Windows 服务

csharp
using Microsoft.Extensions.Hosting.WindowsServices;

var builder = Host.CreateApplicationBuilder(args);

// 配置为 Windows 服务
builder.Services.AddWindowsService();

// 添加 MCP 服务
builder.Services.AddMcp();
builder.Services.AddHostedService<McpServerService>();

var host = builder.Build();
await host.RunAsync();

public class McpServerService : BackgroundService
{
    private readonly McpServer _server;
    
    public McpServerService(McpServer server)
    {
        _server = server;
    }
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await _server.StartAsync(stoppingToken);
    }
}

相关资源

社区支持

🚀 探索模型上下文协议的无限可能 | 连接 AI 与世界的桥梁 | 让智能更智能