尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

使用 .NET 构建 STDIO 传输的 MCP 服务器:ModelContextProtocol 2.x 实战指南

使用 .NET 构建 STDIO 传输的 MCP 服务器:ModelContextProtocol 2.x 实战指南 使用 .NET 构建 STDIO 传输的 MCP 服务器ModelContextProtocol 2.x 实战指南【免费下载链接】awesome-copilotCommunity-contributed instructions, agents, skills, and configurations to help you make the most of GitHub Copilot.项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-copilotSTDIO标准输入/输出是 MCPModel Context Protocol服务器最简单、最本地的传输方式客户端Claude Desktop、VS Code、MCP Inspector 或自定义 CLI将服务器作为子进程启动双方通过 stdin/stdout 交换 JSON-RPC 帧。本文以awesome-copilot仓库中 dotnet-mcp-builder 技能的transport-stdio参考文档为核心结合同一技能下的包选型、工具定义与测试文档带你从零搭建、配置、调试一个生产可用的 .NET STDIO MCP 服务器并避开最常见的 stdout 污染陷阱。什么时候选择 STDIO 而不是 HTTPSTDIO 适合服务器作为客户端子进程运行的一切场景判断标准很简单客户端是否负责拉起你的可执行文件、并通过管道与它对话。以下场景优先选择 STDIO本地优先的服务器需要访问文件系统、本地开发工具、CLI 集成单文件或 NuGet 包分发作为单个可执行文件或以dnx可运行的 NuGet 包分发追求最简单的部署故事无需网络、无需认证客户端配置一条command即可需要服务器到客户端server-to-client能力elicitation向用户提问、通知notifications以及已废弃的 sampling/roots——STDIO 始终完整支持这些双向通道无需关心 HTTP 模式下的Stateless标志。如果目标是远程、多租户服务器需要 OAuth、网关认证、水平扩展则应改用 Streamable HTTP 传输详见 transport-http.md。在技能文档的决策树中这两者被明确列为新建服务器的两个分叉新 STDIO 服务器加载transport-stdio.md新 HTTP 服务器加载transport-http.md见 SKILL.md。搭建最小 STDIO 服务器创建项目与安装包针对官方ModelContextProtocolNuGet 包当前稳定线为2.x写作时最新为2.2.0对齐 MCP 2026-07-28 规范STDIO 服务器需要两个包dotnet new console -n MyStdioServer -f net10.0 cd MyStdioServer dotnet add package ModelContextProtocol --version 2.2.0 dotnet add package Microsoft.Extensions.Hosting --version 10.0.11关于包选型packages.md 给出了明确规则新 STDIO 服务器 →ModelContextProtocolMicrosoft.Extensions.HostingModelContextProtocol.AspNetCore仅用于 HTTPStreamable服务器纯客户端则用ModelContextProtocol.Core。SDK 面向.NET 8.0与netstandard2.0可在 .NET 8LTS、.NET 9、.NET 10当前 LTS新项目推荐上运行STDIO 本身对 TFM 没有 HTTP 那样的 ASP.NET Core 要求但仍建议新项目默认 .NET 10。需要特别警惕的是版本陷阱0.x-preview系列是预览版API 与 2.x 存在破坏性差异1.x虽仍可编译互操作但缺少 v2 的诸多行为HTTP 默认无状态、discovery-first 协商、roots/sampling/logging 废弃等。若想确认最新版本可执行dotnet search ModelContextProtocol --prerelease完整最小代码将Program.cs替换为如下内容即可得到一个可被客户端发现并调用工具的最小服务器// Program.cs using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using ModelContextProtocol.Server; using System.ComponentModel; var builder Host.CreateApplicationBuilder(args); // CRITICAL: stdout 是 JSON-RPC 通道所有日志必须发送到 stderr。 builder.Logging.AddConsole(o o.LogToStandardErrorThreshold LogLevel.Trace); builder.Services .AddMcpServer() .WithStdioServerTransport() .WithToolsFromAssembly(); await builder.Build().RunAsync(); [McpServerToolType] public static class EchoTool { [McpServerTool, Description(Echoes the message back to the client.)] public static string Echo(string message) $hello {message}; }这段代码的精髓在于注册链AddMcpServer()把 MCP 服务器接入 Microsoft.Extensions.Hosting 的 DI 容器WithStdioServerTransport()选择 STDIO 传输WithToolsFromAssembly()扫描程序集内所有带[McpServerToolType]标注的类并暴露其中带[McpServerTool]的方法。技能文档强调的精神模型正是如此.NET MCP 服务器就是一个普通的 Hosting 应用通过 DI 装配 MCP 服务器原始类型tools/prompts/resources就是普通的 C# 方法加上特性标注见 SKILL.md。工具的 JSON Schema 由 SDK 从方法签名与[Description]特性自动生成——这也是 tool-primitive.md 反复强调务必为每个工具和参数写[Description]的原因这是 LLM 在挑选和构造调用时所看到的全部信息含糊的描述是工具不被使用的最主要原因。方法也可以是实例方法并依赖注入如ILoggerT、外部服务客户端SDK 会识别非载荷参数类型并从 DI 解析。stdout/stderr 陷阱STDIO 服务器最常犯的错误STDIO 服务器最常见的 bug 就是有非 JSON-RPC 帧的内容写入了 stdout。客户端解析失败后会直接断开连接。任何落到 stdout 的杂讯都会让整个协议失效。会让 STDIO 静默崩溃的典型来源代码中任何位置的Console.WriteLine(...)使用默认 console sink写入 stdout配置的日志器挂载了默认 trace listener 时的Trace.WriteLine(...)启动时打印 banner 的第三方库。防御性检查清单最先配置日志重定向到 stderr上文代码片段已演示LogToStandardErrorThreshold LogLevel.Trace确保所有等级的日志都走 stderr不要在工具方法或启动代码中使用Console.Write*改为将ILogger注入工具类工具类构造注入ILoggerT然后_log.LogInformation(...)第三方库噪音通过ILogger重定向其日志或在启动时抑制。这一点在 server-features.md 的日志章节被再次强调STDIO 服务器的 console 日志必须走 stderr否则会污染 JSON-RPC 流。同时注意MCP 通道日志loggingcapability 与客户端的setLevel已在 2026-07-28 规范中废弃SDK 2.x 将其标记为[Obsolete]警告MCP9005ILogger方式的日志不受影响仍是最佳默认。技能文档的卡规则第 2 条也把它列为必须始终遵守的规则之一见 SKILL.md。服务器身份Server identity协商阶段2026-07-28 规范的server/discover交换或对旧版客户端的传统initialize响应SDK 会自动处理两者SDK 会发送serverInfo名称 版本。默认从程序集元数据派生。需要覆盖时builder.Services .AddMcpServer(options { options.ServerInfo new() { Name my-stdio-server, Version 1.0.0, Title My STDIO MCP Server // 可选的人类可读名称 }; }) .WithStdioServerTransport() .WithToolsFromAssembly();Title是可选的展示名Name与Version是协议中客户端用于标识服务器的主要字段建议设置为稳定且有意义的值便于客户端配置与日志排查。从客户端读取参数与环境变量客户端如 Claude Desktop 的配置通常以参数和环境变量的形式拉起服务器。读取方式与普通 .NET 应用完全一致string apiKey Environment.GetEnvironmentVariable(MY_API_KEY) ?? throw new InvalidOperationException(MY_API_KEY not set); string configPath args.ElementAtOrDefault(0) ?? Path.Combine(Environment.CurrentDirectory, config.json);务必在项目的 README 中记录预期的环境变量与命令行参数让用户知道该在客户端配置里填什么。这是保证配置即文档的关键一步。接入 Claude Desktop在 Claude Desktop 的配置文件claude_desktop_config.json中注册服务器{ mcpServers: { my-server: { command: dotnet, args: [run, --project, C:/path/to/MyStdioServer], env: { MY_API_KEY: ... } } } }针对不同分发形态command/args有几种变体自包含self-contained发布将command/args替换为可执行文件路径即可NuGet 包 dnx分发以包名和版本直接拉起无需克隆源码command: dnx, args: [MyMcpServer, --version, 1.2.3]关于dnx分发模式packages.md 指出这是把服务器发布为 NuGet 包、让用户免克隆直接运行的合法分发模型它与服务器本身的构建方式正交——代码完全一致只需更改启动命令。接入 VS CodeGitHub Copilot Chat在项目的.vscode/mcp.json中注册{ servers: { my-server: { type: stdio, command: dotnet, args: [run, --project, ${workspaceFolder}/src/MyMcpServer] } } }注意 VS Code 配置使用type: stdio显式声明传输类型并可用${workspaceFolder}这类占位符指代工作区路径——这一点与 Claude Desktop 配置不同后者通过mcpServers键直接表达command/args/env。本地调试MCP InspectorSTDIO 服务器最干净的调试工作流是 MCP Inspectornpx modelcontextprotocol/inspector dotnet run --project ./MyStdioServerInspector 会以子进程方式拉起你的服务器打开一个 UI让你交互式地列出并调用工具、查看资源、触发 elicitation、查看日志以及检视原始 JSON-RPC 帧。若需向服务器传递额外参数或环境变量可在--之后追加见 testing.mdnpx modelcontextprotocol/inspector \ dotnet run --project ./MyMcpServer -- \ --some-flag valueInspector 的典型用途包括验证工具描述是否清晰——Inspector 以 LLM 消费它们的方式渲染在真实 LLM 之外走通 elicitation 流程提交 bug 报告时捕获精确的 JSON-RPC 载荷。除 Inspector 外testing.md 还推荐在开发/CI 中使用InMemoryTransport或更底层的StreamServerTransport/StreamClientTransport在同一进程内将真实服务器与真实客户端对接用Pipe模拟双向流从而在dotnet test环境中断言客户端视角的可观察行为无需子进程、无需网络、无需 Node/Docker对纯逻辑则直接单测工具方法[McpServerTool]特性不影响 MCP 之外的运行时行为。优雅关闭Graceful shutdownbuilder.Build().RunAsync()已自动处理 SIGINT/SIGTERM 信号。若存在需要冲刷的后台工作使用IHostApplicationLifetimevar host builder.Build(); var lifetime host.Services.GetRequiredServiceIHostApplicationLifetime(); lifetime.ApplicationStopping.Register(() { // flush、关闭句柄等 —— 保持快速5s避免客户端挂起。 }); await host.RunAsync();回调里应只做轻量、快速的清理冲刷缓冲区、关闭文件/网络句柄5 秒内完成是经验上限客户端在等待进程退出拖得越久越可能被客户端判定为超时而挂起连接。与 HTTP 传输的对照与选型总结为了帮助你在架构决策时做出正确选择这里给出 STDIO 与 Streamable HTTP 的快速对照HTTP 细节见 transport-http.md维度STDIOStreamable HTTP适用场景本地、单用户、子进程启动远程、多租户、可水平扩展部署复杂度无网络、无认证command一行接入需要端点、认证OAuth/网关、反向代理配置服务器→客户端能力elicitation/通知始终支持当前规范2026-07-28无 HTTP 会话需用多轮InputRequiredException模式包依赖ModelContextProtocolMicrosoft.Extensions.HostingModelContextProtocol.AspNetCore关键陷阱任何写入 stdout 的杂讯都会破坏协议SSE 缓冲、超时、路径前缀MapMcp不匹配导致 404常见故障速查结合技能文档的排障清单见 SKILL.mdSTDIO 场景下遇到问题时按此顺序排查工具不出现类上缺少[McpServerToolType]或没有注册.WithToolsFromAssembly()/.WithToolsT()连接被断开/解析失败几乎可以肯定是 stdout 被污染日志 sink、Console.WriteLine、库的 banner参数未绑定参数名必须与 JSON-RPCarguments的键一致复杂类型通过System.Text.Json绑定工具一直在被 LLM 误用多半是[Description]缺失或含糊——打开 Inspector 查看 LLM 视角的 schema/描述。若涉及 sampling/elicitation/roots 这类传统 server-to-client 调用失败注意在 STDIO 上它们始终可用STDIO 没有Stateless标志而当前规范的 Streamable HTTP 无状态模式下这些遗留调用会在运行时直接抛错——这正是本文档开头强调需要 server-to-client 功能时选 STDIO的原因。至此你已经掌握了从零搭建、正确配日志、注册到两大主流客户端、本地调试、优雅关闭到排障的完整 STDIO MCP 服务器开发闭环可以直接基于官方ModelContextProtocol2.x 包构建自己的本地工具服务器。【免费下载链接】awesome-copilotCommunity-contributed instructions, agents, skills, and configurations to help you make the most of GitHub Copilot.项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-copilot创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表