主题
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);
}
}