.NET 9 C#自定义MCP Server与Claude桌面端集成后意外退出
问题:.NET 9开发的MCP服务器初始化后连接被意外关闭
我使用.NET 9开发C#自定义MCP(Model Context Protocol)服务器,用于与Claude桌面AI工具集成。服务器可正常启动,通过stdin/stdout发送了合法的JSON-RPC 2.0初始化响应,但连接随即被关闭。
Claude端日志
2025-05-31T04:27:53.587Z [masood-mcpsrv] [info] Initializing server... 2025-05-31T04:27:53.740Z [masood-mcpsrv] [info] Server started and connected successfully 2025-05-31T04:27:53.812Z [masood-mcpsrv] [info] Message from client: {"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"claude-ai","version":"0.1.0"}},"jsonrpc":"2.0","id":0} 2025-05-31T04:27:54.121Z [masood-mcpsrv] [info] Message from server: {"jsonrpc":"2.0","id":0,"result":{"serverInfo":{"name":"SimpleMCP","version":"0.1.0"}}} 2025-05-31T04:27:54.126Z [masood-mcpsrv] [info] Client transport closed 2025-05-31T04:27:54.127Z [masood-mcpsrv] [info] Server transport closed 2025-05-31T04:27:54.127Z [masood-mcpsrv] [info] Client transport closed 2025-05-31T04:27:54.127Z [masood-mcpsrv] [info] Server transport closed unexpectedly, this is likely due to the process exiting early. If you are developing this MCP server you can add output to stderr (i.e. `console.error('...')` in JavaScript, `print('...', file=sys.stderr)` in python) and it will appear in this log. 2025-05-31T04:27:54.127Z [masood-mcpsrv] [error] Server disconnected. For troubleshooting guidance, please visit our [debugging documentation](https://modelcontextprotocol.io/docs/tools/debugging) {"context":"connection"} 2025-05-31T04:27:54.138Z [masood-mcpsrv] [info] Server transport closed 2025-05-31T04:27:54.139Z [masood-mcpsrv] [info] Client transport closed
服务器代码
using System.Text.Json; using System.Text; using System.Text.Json.Nodes; using static System.Runtime.InteropServices.JavaScript.JSType; Console.OutputEncoding = Encoding.UTF8; Console.InputEncoding = Encoding.UTF8; while (true) { try { string? line = Console.ReadLine(); // If input is closed, wait instead of exiting if (line == null) { Thread.Sleep(100); // Prevent CPU spin continue; } var request = JsonNode.Parse(line); var id = request?["id"]?.GetValue<int>(); var method = request?["method"]?.GetValue<string>(); if (method == "initialize") { var result = new { jsonrpc = "2.0", id, result = new { serverInfo = new { name = "SimpleMCP", version = "0.1.0" } } }; WriteJsonResponse(result); } else if (method == "getModelContext") { var result = new { jsonrpc = "2.0", id, result = new { modelContext = new { kind = "modelContext", name = "SampleProduct", properties = new[] { new { name = "id", type = "string" }, new { name = "name", type = "string" }, new { name = "price", type = "number" } } } } }; WriteJsonResponse(result); } else { var error = new { jsonrpc = "2.0", id, error = new { code = -32601, message = "Method not found" } }; WriteJsonResponse(error); } } catch (Exception ex) { var error = new { jsonrpc = "2.0", error = new { code = -32603, message = ex.Message } }; WriteJsonResponse(error); } } static void WriteJsonResponse(object response) { // Serialize with no extra formatting (compact) string json = JsonSerializer.Serialize(response, new JsonSerializerOptions { WriteIndented = false // compact, no pretty print }); // Trim just in case, to remove any accidental whitespace/newlines json = json.Trim(); // Write single line JSON to stdout Console.WriteLine(json); // Console.WriteLine flushes automatically, but if you want to be explicit: Console.Out.Flush(); }
排查原因
- 初始化响应不符合MCP协议要求:日志显示客户端发送的initialize请求包含
protocolVersion和capabilities字段,但服务器返回的响应仅包含serverInfo。根据MCP协议规范,initialize响应必须包含capabilities字段(即使是空对象),缺少该字段会导致Claude客户端验证失败,直接关闭连接。 - 异常处理违反JSON-RPC 2.0规范:当捕获异常时,返回的错误响应未包含
id字段。JSON-RPC 2.0要求所有响应(包括错误)必须匹配请求的id,若无法获取id需设为null,否则客户端会判定响应无效。
解决方案
1. 补全initialize响应的capabilities字段
在initialize响应中添加capabilities字段,根据协议版本2024-11-05返回对应结构,示例中可先返回空对象满足基本要求。
2. 修复异常处理的错误响应
确保异常捕获时返回的错误响应包含id字段,若请求解析失败无法获取id,则设为null。
3. 优化输入读取逻辑
将同步的Console.ReadLine()改为异步读取,避免使用Thread.Sleep浪费资源,同时更优雅地处理stdin关闭情况。
修改后的完整代码
using System.Text.Json; using System.Text; using System.Text.Json.Nodes; Console.OutputEncoding = Encoding.UTF8; Console.InputEncoding = Encoding.UTF8; // 使用异步读取替代同步读取,避免Thread.Sleep while (true) { try { string? line = await Console.In.ReadLineAsync(); if (line == null) { // 输入流关闭时,短暂等待后退出循环 await Task.Delay(100); continue; } var request = JsonNode.Parse(line); var idNode = request?["id"]; // 兼容不同类型的id(JSON-RPC允许id为字符串、数字或null) object? id = idNode?.GetValue<object>(); var method = request?["method"]?.GetValue<string>(); if (method == "initialize") { var result = new { jsonrpc = "2.0", id, result = new { serverInfo = new { name = "SimpleMCP", version = "0.1.0" }, // 补全必填的capabilities字段,根据实际支持的能力填充 capabilities = new {} } }; await WriteJsonResponseAsync(result); } else if (method == "getModelContext") { var result = new { jsonrpc = "2.0", id, result = new { modelContext = new { kind = "modelContext", name = "SampleProduct", properties = new[] { new { name = "id", type = "string" }, new { name = "name", type = "string" }, new { name = "price", type = "number" } } } } }; await WriteJsonResponseAsync(result); } else { var error = new { jsonrpc = "2.0", id, error = new { code = -32601, message = "Method not found" } }; await WriteJsonResponseAsync(error); } } catch (Exception ex) { // 异常时返回包含id(null)的错误响应 var error = new { jsonrpc = "2.0", id = (object?)null, error = new { code = -32603, message = ex.Message } }; await WriteJsonResponseAsync(error); } } static async Task WriteJsonResponseAsync(object response) { string json = JsonSerializer.Serialize(response, new JsonSerializerOptions { WriteIndented = false }); json = json.Trim(); await Console.Out.WriteLineAsync(json); await Console.Out.FlushAsync(); }
内容的提问来源于stack exchange,提问作者Masood Ahmad
相关产品推荐
相关产品推荐

