.NET MCP Server 开发教程
本教程基于实际项目经验,(注意:该教程基于实际项目反向使用ai生成,难免留有遗漏)详细介绍如何使用 .NET 8 开发 Model Context Protocol (MCP) 服务器。
目录
环境准备
1. 开发环境要求
- .NET 8.0 SDK: 确保安装了 .NET 8.0 或更高版本
- IDE: Visual Studio 2022 或 Visual Studio Code
- 操作系统: Windows 10/11 (推荐,某些库可能需要)
2. 验证 .NET 环境
bash
dotnet --version
# 应显示: 8.0.x 或更高版本
项目创建
1. 创建新的控制台项目
bash
dotnet new console -n MyMcpServer -f net8.0
cd MyMcpServer
2. 添加必要的 NuGet 包
xml
<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. 创建主程序入口
csharp
// 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. 创建工具类
csharp
// 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. 基本工具方法
csharp
[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. 异步工具方法
csharp
[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. 文件处理工具
csharp
[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. 错误处理模式
csharp
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. 日志记录
csharp
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. 基础结果模型
csharp
// 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. 复杂数据模型
csharp
// 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. 编译项目
bash
# Release 编译
dotnet build --configuration Release
# 发布自包含应用
dotnet publish --configuration Release --self-contained -r win-x64
2. 本地测试
bash
# 直接运行
./bin/Release/net8.0/MyMcpServer.exe
# 或使用 dotnet 运行
dotnet run --configuration Release
3. Claude Code 配置
在 Claude Code 的配置文件中添加你的 MCP 服务器:
json
{
"mcpServers": {
"myMcpServer": {
"command": "D:\\path\\to\\MyMcpServer.exe",
"args": []
}
}
}
最佳实践
1. 代码组织
- 单一职责: 每个工具方法只做一件事
- 命名规范: 使用清晰的方法和参数名
- 文档注释: 为所有工具方法添加 Description 特性
2. 错误处理
- 输入验证: 总是验证输入参数
- 异常捕获: 捕获并处理预期的异常
- 错误信息: 提供有意义的错误消息
3. 性能考虑
- 异步操作: 对于耗时操作使用异步方法
- 资源管理: 正确使用 using 语句管理资源
- 缓存: 对于重复计算考虑使用缓存
4. 安全考虑
- 输入验证: 防止注入攻击
- 路径验证: 验证文件路径的合法性
- 权限检查: 确保只访问授权的资源
常见问题
1. 编译错误
问题 : 找不到 McpToolkit 相关类型
解决: 确保正确安装了 NuGet 包
xml
<PackageReference Include="McpToolkit" Version="0.1.3" />
<PackageReference Include="McpToolkit.Server" Version="0.1.3" />
2. 运行时错误
问题 : MCP 服务器无法启动
解决: 检查 Program.cs 中的服务配置
csharp
builder.Services.AddMcpServerToolType<MyTools>();
3. 通信问题
问题 : Claude Code 无法连接到 MCP 服务器
解决:
- 确保服务器程序路径正确
- 检查服务器是否正常输出 JSON-RPC 响应
- 验证 stdio 通信是否正常
4. 工具调用失败
问题 : 工具方法返回错误
解决:
- 检查方法签名是否正确
- 确保参数类型匹配
- 添加详细的错误日志
实际项目示例
基于我们开发的 OCR MCP 服务器,这里展示一个完整的实际项目结构:
1. 项目文件 (OCRMCP.Server.csproj)
xml
<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)
csharp
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)
csharp
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 服务器开发是一个不断发展的领域,建议持续关注官方文档和社区更新。希望这个教程对你有所帮助!
参考资源:
项目示例: 本教程基于实际的 OCR MCP 服务器项目,你可以参考完整的实现代码。