1. 为什么要在 C# 里自己搭一个 SSE 版 MCP Server如果你正在做 AI 工具链大概率会遇到一个很现实的问题Claude Desktop、Cline、Continue、各种 Agent 框架每个客户端都要单独配一份模型 Key换一次模型就得改一圈配置。MCP Server 的出现本来是为了解决「工具复用」但很多人卡在第一步——Server 跑不起来或者跑起来了但客户端连不上。MCP 协议目前主流有两种通信方式Stdio 和 SSE。Stdio 适合本地进程拉起客户端直接spawn一个可执行文件通过标准输入输出通信SSE 则是把 Server 做成一个 HTTP 服务客户端通过GET /sse建立长连接接收事件流再用POST发消息。SSE 的好处是可以部署在内网某台机器上多个客户端共享可以配合反向代理做鉴权调试时用 curl 就能看到流式输出不用挂调试器。这篇要交付的是一条完整链路用 C# 的 ASP.NET Core 建一个 Web API 项目引入ModelContextProtocol.AspNetCore把工具类注册进去暴露/sse端点然后把这个 Server 对外的模型调用统一指向 TaoToken 的 API 通道用一把 Key 管理多个模型最后用 curl 验证 SSE 流是否真的在推事件再用 MCP Client 跑一次端到端调用。适合谁看有 C# 基础、想在本地或内网部署 MCP Server 的后端同学手里有多个 AI 客户端、想统一 Key 管理的工具链维护者以及被 SSE 连接超时、工具列表为空这类问题折腾过的开发者。下面所有代码和配置都可以直接复制我按「建项目 → 写工具 → 配 Key → 验证 → 排障」的顺序走一遍。2. TaoToken 前置统一 Key 与 API 通道准备在写代码之前先把「模型从哪来」这件事定下来。MCP Server 本身只负责暴露工具但工具内部如果要调模型比如做一个「总结网页」的工具就需要一个稳定的 API 入口。TaoToken 在这里扮演的角色是统一 Key 和统一 API 通道你只需要在它那边生成一把 Key然后在 Server 的配置里写一次后续换模型只改模型名不用动代码。需要提前准备的东西一个 TaoToken 账号登录后进入控制台在控制台里生成 API Key建议按项目分 Key方便后面吊销记下 API 基地址https://taotoken.net/api注意这个地址不带任何查询参数直接作为 BaseUrl 用确认你要用的模型名比如claude-sonnet-4-20250514这类具体以控制台模型列表为准。控制台入口在这里https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 生成 Key 的页面在 API Keys 里https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。生成后先复制保存页面刷新后就看不到完整 Key 了。注意Key 不要硬编码进Program.cs也不要在本文示例里直接粘贴真实 Key。下面统一用环境变量或appsettings.Development.json读取提交代码前记得把本地配置文件加进.gitignore。如果你后面要做长期编码类 Agent比如让 MCP Server 里的工具去调模型做代码补全可以了解下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 它更适合高频调用场景。单纯验证模型通不通用模型对话页面就够了https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。3. 可复制配置从建项目到 SSE 端点跑起来3.1 创建 Web API 项目并引入依赖打开 Visual Studio 或直接用 dotnet CLI。命令行方式更干净dotnet new webapi -n McpServer.Sse cd McpServer.Sse dotnet add package ModelContextProtocol.AspNetCore --version 0.1.0-preview.14注意这个包目前是预览版用 Visual Studio 的 NuGet 管理器安装时要勾选「包括预发行版」否则搜不到。装完后csproj里应该能看到对应的PackageReference。3.2 Program.cs注册 MCP 服务并映射端点把Program.cs改成下面这样。核心是三件事注册 MCP Server、启用 HTTP 传输、从程序集扫描工具类。var builder WebApplication.CreateBuilder(args); // 注册 MCP Server 相关服务 builder.Services .AddMcpServer() .WithHttpTransport() // 使用 HTTP/SSE 传输 .WithToolsFromAssembly(); // 扫描当前程序集里的工具类 var app builder.Build(); app.UseHttpsRedirection(); // 映射 MCP 协议终结点默认暴露 /sse app.MapMcp(); app.Run();WithToolsFromAssembly()会扫描所有带[McpServerToolType]的类把带[McpServerTool]的方法注册成工具。MapMcp()默认把 SSE 端点挂在/sse这也是为什么启动后访问https://localhost:7130/sse就能连上。3.3 写一个 DemoTool返回服务器时间新建DemoTool.cs放在项目根目录即可。这个工具的作用是让客户端传一个时间格式Server 按格式返回当前时间方便验证参数传递和返回值。using ModelContextProtocol.Server; using System.ComponentModel; namespace McpServer.Sse; // 标记此类为 MCP 服务器工具类型 [McpServerToolType] public static class DemoTool { /// summary /// 服务器工具方法用于获取当前服务器时间。 /// /summary /// param nameformat时间格式字符串默认 yyyy-MM-dd HH:mm:ss/param [McpServerTool, Description(获取服务器时间)] public static string ServerTime( [Description(格式)] string format yyyy-MM-dd HH:mm:ss) { return DateTime.Now.ToString(format); } }几个关键点别漏类上必须有[McpServerToolType]方法上必须有[McpServerTool]参数上的[Description]会作为工具 schema 的一部分传给客户端客户端以及背后的模型靠它理解参数含义。少任何一个工具都不会出现在列表里。3.4 接入 TaoTokensettings.json / config.toml 骨架MCP Server 本身不强制要求模型配置但如果你的工具内部要调模型就需要一个统一的 API 客户端。下面给两种常见配置骨架按你项目习惯选一种。方式一appsettings.jsonASP.NET Core 原生{ TaoToken: { BaseUrl: https://taotoken.net/api, ApiKey: , DefaultModel: claude-sonnet-4-20250514, TimeoutSeconds: 60 } }本地开发时把真实 Key 放进appsettings.Development.json并确保它被.gitignore忽略。读取时用builder.Configuration.GetSection(TaoToken)绑定到一个TaoTokenOptions类。方式二config.toml如果你更习惯 TOML[taotoken] base_url https://taotoken.net/api api_key default_model claude-sonnet-4-20250514 timeout_seconds 60然后在Program.cs里用Tomlyn之类的库加载或者干脆统一走环境变量TAOTOKEN_API_KEY避免配置文件泄露。无论哪种方式Key 都只出现一次后续所有工具共用这个客户端。3.5 启动项目确认 SSE 端点dotnet run控制台会打印监听地址通常是https://localhost:7130和http://localhost:5130。SSE 端点是https://localhost:7130/sse。开发证书没信任的话先执行dotnet dev-certs https --trust否则 curl 会因为证书问题直接失败这个坑后面排障章节还会提。4. 验证请求用 curl 看 SSE 流再用 Client 跑通4.1 curl 验证 SSE 是否在推事件SSE 是长连接curl 会一直挂着看到事件就说明通了。执行curl -N -H Accept: text/event-stream https://localhost:7130/sse -k-N关闭缓冲-k跳过证书校验本地自签证书场景。正常输出类似event: endpoint data: /message?sessionIdxxxx-xxxx event: message data: {jsonrpc:2.0,method:notifications/tools/list_changed}第一段endpoint事件会告诉你后续 POST 消息要发到哪个地址里面带sessionId。这一步能出事件说明 SSE 通道本身没问题。如果卡住没有任何输出先看 Server 控制台有没有请求日志再检查端口和证书。4.2 用 MCP Client 连接并列出工具拿一个已有的 MCP Client比如之前课程里那个 C# Client把 Endpoint 改成https://localhost:7130/sse运行后 Client 会先建立 SSE 连接拿到 endpoint然后发initialize和tools/list。成功的话会打印出工具列表里面应该有ServerTime参数 schema 里能看到format字段和默认值。4.3 调用工具验证参数与返回在 Client 里发起调用参数传{format: yyyy年MM月dd日 HH:mm}Server 返回类似2025年05月20日 14:32。这一步跑通说明「SSE 连接 → 工具发现 → 参数传递 → 返回值」整条链路是通的。如果你用的是支持 MCP 的对话客户端工具调用会由模型自动触发你只需要在对话里说「用 ServerTime 工具告诉我现在几点格式用 yyyy-MM-dd」。模型会生成调用请求Client 转发给 Server结果再回填给模型。5. 本篇常见错排查5.1 工具列表为空最常见的原因是工具类没被扫描到。检查三点类是否是publicstatic不是必须但类必须可见类上有没有[McpServerToolType]方法上有没有[McpServerTool]。另外WithToolsFromAssembly()默认扫描调用它的程序集如果工具类在另一个类库项目里需要传程序集参数比如WithToolsFromAssembly(typeof(DemoTool).Assembly)。5.2 curl 连不上或证书报错本地 HTTPS 自签证书没被信任时curl 会报SSL certificate problem。临时用-k跳过正式环境应该导入证书或走反向代理。如果连http://localhost:5130/sse都连不上检查launchSettings.json里的applicationUrl以及防火墙是否拦了端口。5.3 SSE 连接建立后立刻断开多半是反向代理或中间层把长连接掐了。Nginx 需要显式关闭缓冲location /sse { proxy_pass http://127.0.0.1:5130; proxy_http_version 1.1; proxy_set_header Connection ; proxy_buffering off; proxy_cache off; proxy_read_timeout 3600s; }proxy_buffering off是关键否则事件会被缓冲住客户端迟迟收不到。5.4 调用模型时报 401 或 404401 一般是 Key 没读到或写错了检查环境变量名和配置文件路径404 多半是 BaseUrl 拼错注意 TaoToken 的 API 基地址是https://taotoken.net/api不要自己再加/v1之类的后缀具体路径以接入文档为准https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。模型名写错也会返回 404 或 400去控制台模型列表核对一下。5.5 工具调用参数对不上如果模型生成的参数名和你的方法参数名不一致调用会失败。确保[Description]写清楚参数名用英文且语义明确。C# 的默认参数值会体现在 schema 里但客户端不一定遵守最好在工具内部再做一次空值兜底。6. 继续往下走把 Key 和端点固定下来到这一步你已经有了一个能跑的 SSE 版 MCP Server/sse端点可用工具能被发现和调用模型调用统一走 TaoToken 的 API 通道。接下来建议做两件事一是把 API Key 的读取方式固定成环境变量避免本地配置误提交二是把 SSE 端点前面的反向代理配好尤其是proxy_buffering off这行很多「连上了但收不到事件」的问题都出在这里。如果你还要接 Claude Code 这类工具Anthropic 兼容层的配置可以参考https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 它和 MCP Server 是两层东西别混在一起配。下一篇会写 Stdio 通信方式的 MCP Server到时候可以把两种传输方式放在同一个项目里按客户端能力切换。