.NET MCP Server 开发教程
.NET MCP Server 开发教程
本教程基于实际项目经验,(注意:该教程基于实际项目反向使用ai生成,难免留有遗漏)详细介绍如何使用 .NET 8 开发 Model Context Protocol (MCP) 服务器。
目录
- 环境准备
- 项目创建
- MCP 基础概念
- 项目结构
- 开发步骤
- 工具方法实现
- 错误处理和日志
- 部署和测试
- 最佳实践
- 常见问题
环境准备
1. 开发环境要求
- .NET 8.0 SDK: 确保安装了 .NET 8.0 或更高版本
- IDE: Visual Studio 2022 或 Visual Studio Code
- 操作系统: Windows 10/11 (推荐,某些库可能需要)
2. 验证 .NET 环境
dotnet --version
# 应显示: 8.0.x 或更高版本
项目创建
1. 创建新的控制台项目
dotnet new console -n MyMcpServer -f net8.0
cd MyMcpServer
2. 添加必要的 NuGet 包
<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net8.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable><AssemblyName>MyMcpServer</AssemblyName><RootNamespace>MyMcpServer</RootNamespace></PropertyGroup><ItemGroup><PackageReference Include="McpToolkit" Version="0.1.3" /><PackageReference Include="McpToolkit.Server" Version="0.1.3" /><PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.8" /><PackageReference Include="ModelContextProtocol-SemanticKernel" Version="0.3.0" /><PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" /></ItemGroup></Project>
MCP 基础概念
1. Model Context Protocol (MCP)
MCP 是一种标准化的协议,用于 AI 模型与外部工具和数据源之间的通信。
2. 核心组件
- MCP Server: 提供工具和资源的服务器
- MCP Client: 使用工具和资源的客户端
- Tools: 可执行的功能方法
- Resources: 可访问的数据源
- stdio: 标准输入输出通信协议
3. 通信方式
MCP 使用 stdio 进行通信:
- 输入: JSON-RPC 2.0 请求
- 输出: JSON-RPC 2.0 响应
项目结构
MyMcpServer/
├── Program.cs # 主程序入口
├── MyTools.cs # 工具方法实现
├── MyMcpServer.csproj # 项目文件
├── appsettings.json # 配置文件
├── Models/ # 数据模型
│ ├── ToolResult.cs
│ └── ...
└── Services/ # 服务层└── ...
开发步骤
1. 创建主程序入口
// Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using McpToolkit.Server;
using System.ComponentModel;namespace MyMcpServer;class Program
{static async Task Main(string[] args){var builder = Host.CreateApplicationBuilder(args);// 配置 MCP 服务builder.Services.AddMcpServer(options =>{// 配置服务器信息options.ServerInfo = new(){Name = "My MCP Server",Version = "1.0.0"};});// 注册工具类型builder.Services.AddMcpServerToolType<MyTools>();var host = builder.Build();// 启动 MCP 服务器await host.RunMcpAsync();}
}
2. 创建工具类
// MyTools.cs
using ModelContextProtocol.Server;
using System.ComponentModel;namespace MyMcpServer;[McpServerToolType]
public static class MyTools
{[McpServerTool, Description("简单的问候工具")]public static string Greet([Description("要问候的名字")] string name){return $"你好, {name}! 欢迎使用 MCP 服务器。";}[McpServerTool, Description("计算两个数字的和")]public static int Add([Description("第一个数字")] int a,[Description("第二个数字")] int b){return a + b;}[McpServerTool, Description("获取当前时间")]public static string GetCurrentTime(){return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");}
}
工具方法实现
1. 基本工具方法
[McpServerTool, Description("工具描述")]
public static ReturnType ToolName([Description("参数描述")] ParameterType parameterName,CancellationToken cancellationToken = default)
{try{// 实现逻辑var result = new ResultType{Success = true,Data = processData(parameterName)};return result;}catch (Exception ex){return new ResultType{Success = false,ErrorMessage = ex.Message};}
}
2. 异步工具方法
[McpServerTool, Description("异步工具示例")]
public static Task<AsyncResult> AsyncTool([Description("输入数据")] string input,CancellationToken cancellationToken = default)
{return Task.Run(() =>{// 模拟异步操作Task.Delay(1000, cancellationToken);return new AsyncResult{Success = true,ProcessedData = input.ToUpper()};}, cancellationToken);
}
3. 文件处理工具
[McpServerTool, Description("读取文件内容")]
public static FileResult ReadFile([Description("文件路径")] string filePath)
{var result = new FileResult { Success = false };try{if (!File.Exists(filePath)){result.ErrorMessage = $"文件不存在: {filePath}";return result;}var content = File.ReadAllText(filePath);result.Success = true;result.Content = content;result.FileSize = new FileInfo(filePath).Length;}catch (Exception ex){result.ErrorMessage = $"读取文件失败: {ex.Message}";}return result;
}
错误处理和日志
1. 错误处理模式
public static ResultType SafeOperation(string input)
{var result = new ResultType { Success = false };try{// 参数验证if (string.IsNullOrEmpty(input)){result.ErrorMessage = "输入参数不能为空";return result;}// 业务逻辑var processed = ProcessInput(input);result.Success = true;result.Data = processed;return result;}catch (ArgumentException ex){result.ErrorMessage = $"参数错误: {ex.Message}";return result;}catch (InvalidOperationException ex){result.ErrorMessage = $"操作无效: {ex.Message}";return result;}catch (Exception ex){// 记录未预期的错误LogError($"未预期的错误: {ex}");result.ErrorMessage = "系统错误,请稍后重试";return result;}
}
2. 日志记录
public static class Logger
{private static readonly string LogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"mcp_server.log");public static void LogInfo(string message){LogToFile("INFO", message);}public static void LogError(string message){LogToFile("ERROR", message);// 同时输出到 stderr (MCP 标准)Console.Error.WriteLine($"[ERROR] {message}");}private static void LogToFile(string level, string message){try{var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");var logMessage = $"[{timestamp}] [{level}] {message}";File.AppendAllText(LogPath, logMessage + Environment.NewLine);}catch{// 忽略日志写入错误}}
}
数据模型定义
1. 基础结果模型
// Models/OperationResult.cs
public class OperationResult
{public bool Success { get; set; }public string? ErrorMessage { get; set; }public long ElapsedMilliseconds { get; set; }
}public class DataResult<T> : OperationResult
{public T? Data { get; set; }
}public class FileResult : OperationResult
{public string? Content { get; set; }public long FileSize { get; set; }public string? FileName { get; set; }
}
2. 复杂数据模型
// Models/TextBlock.cs
public class TextBlock
{public string Text { get; set; } = string.Empty;public double Confidence { get; set; }public BoundingBox BoundingBox { get; set; } = new();
}public class BoundingBox
{public int X { get; set; }public int Y { get; set; }public int Width { get; set; }public int Height { get; set; }
}
部署和测试
1. 编译项目
# Release 编译
dotnet build --configuration Release# 发布自包含应用
dotnet publish --configuration Release --self-contained -r win-x64
2. 本地测试
# 直接运行
./bin/Release/net8.0/MyMcpServer.exe# 或使用 dotnet 运行
dotnet run --configuration Release
3. Claude Code 配置
在 Claude Code 的配置文件中添加你的 MCP 服务器:
{"mcpServers": {"myMcpServer": {"command": "D:\\path\\to\\MyMcpServer.exe","args": []}}
}
最佳实践
1. 代码组织
- 单一职责: 每个工具方法只做一件事
- 命名规范: 使用清晰的方法和参数名
- 文档注释: 为所有工具方法添加 Description 特性
2. 错误处理
- 输入验证: 总是验证输入参数
- 异常捕获: 捕获并处理预期的异常
- 错误信息: 提供有意义的错误消息
3. 性能考虑
- 异步操作: 对于耗时操作使用异步方法
- 资源管理: 正确使用 using 语句管理资源
- 缓存: 对于重复计算考虑使用缓存
4. 安全考虑
- 输入验证: 防止注入攻击
- 路径验证: 验证文件路径的合法性
- 权限检查: 确保只访问授权的资源
常见问题
1. 编译错误
问题: 找不到 McpToolkit 相关类型
解决: 确保正确安装了 NuGet 包
<PackageReference Include="McpToolkit" Version="0.1.3" />
<PackageReference Include="McpToolkit.Server" Version="0.1.3" />
2. 运行时错误
问题: MCP 服务器无法启动
解决: 检查 Program.cs 中的服务配置
builder.Services.AddMcpServerToolType<MyTools>();
3. 通信问题
问题: Claude Code 无法连接到 MCP 服务器
解决:
- 确保服务器程序路径正确
- 检查服务器是否正常输出 JSON-RPC 响应
- 验证 stdio 通信是否正常
4. 工具调用失败
问题: 工具方法返回错误
解决:
- 检查方法签名是否正确
- 确保参数类型匹配
- 添加详细的错误日志
实际项目示例
基于我们开发的 OCR MCP 服务器,这里展示一个完整的实际项目结构:
1. 项目文件 (OCRMCP.Server.csproj)
<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net8.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable><AssemblyName>OCRMCP.Server</AssemblyName><RootNamespace>OCRMCP.Server</RootNamespace></PropertyGroup><ItemGroup><PackageReference Include="McpToolkit" Version="0.1.3" /><PackageReference Include="McpToolkit.Server" Version="0.1.3" /><PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.8" /><PackageReference Include="ModelContextProtocol-SemanticKernel" Version="0.3.0" /><PackageReference Include="PaddleOCRSharp" Version="5.1.0" /><PackageReference Include="PdfiumViewer" Version="2.13.0" /><PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" /><PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" /></ItemGroup>
</Project>
2. 主程序 (Program.cs)
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using McpToolkit.Server;namespace OCRMCP.Server;class Program
{static async Task Main(string[] args){var builder = Host.CreateApplicationBuilder(args);builder.Services.AddMcpServer(options =>{options.ServerInfo = new(){Name = "OCR MCP Server",Version = "1.0.0"};});builder.Services.AddMcpServerToolType<OCRTools>();var host = builder.Build();await host.RunMcpAsync();}
}
3. 核心工具类 (OCRTools.cs)
using ModelContextProtocol.Server;
using System.ComponentModel;
using PaddleOCRSharp;namespace OCRMCP.Server;[McpServerToolType]
public static class OCRTools
{private static PaddleOCREngine? _ocrEngine;private static readonly object _lockObject = new object();private static bool _isInitialized = false;static OCRTools(){InitializeOCREngine();}[McpServerTool, Description("识别图片中的文本内容")]public static Task<OCRResult> RecognizeImage([Description("图片文件路径")] string imagePath,[Description("是否预处理图片以提高识别率")] bool preprocess = true,CancellationToken cancellationToken = default){var result = new OCRResult { Success = false };try{// 参数验证if (string.IsNullOrEmpty(imagePath)){result.ErrorMessage = "缺少必需参数:imagePath";return Task.FromResult(result);}if (!File.Exists(imagePath)){result.ErrorMessage = $"图片文件不存在: {imagePath}";return Task.FromResult(result);}// 初始化 OCR 引擎InitializeOCREngine();if (!_isInitialized || _ocrEngine == null){result.ErrorMessage = "OCR引擎初始化失败";return Task.FromResult(result);}// 执行 OCR 识别var paddleResult = _ocrEngine.DetectText(imagePath);result = ConvertToOCRResult(paddleResult, imagePath);result.Success = true;return Task.FromResult(result);}catch (Exception ex){result.Success = false;result.ErrorMessage = $"识别失败: {ex.Message}";return Task.FromResult(result);}}private static void InitializeOCREngine(){// OCR 引擎初始化逻辑// ...}private static OCRResult ConvertToOCRResult(PaddleOCRSharp.OCRResult paddleResult, string sourcePath){// 结果转换逻辑// ...}
}
总结
通过本教程,你应该能够:
- ✅ 创建一个基本的 .NET 8 MCP 服务器
- ✅ 实现各种类型的工具方法
- ✅ 处理错误和日志记录
- ✅ 部署和测试 MCP 服务器
- ✅ 遵循最佳实践开发高质量的服务器
MCP 服务器开发是一个不断发展的领域,建议持续关注官方文档和社区更新。希望这个教程对你有所帮助!
参考资源:
- Model Context Protocol 官方文档
- MCP Toolkit GitHub
- .NET 8 官方文档
项目示例: 本教程基于实际的 OCR MCP 服务器项目,你可以参考完整的实现代码。