1. 为什么 .NET 开发者需要把 MCP endpoint 统一到 TaoToken
MCP(Model Context Protocol)在 .NET 生态里已经不算新词了,但真正落到项目里,很多人卡在同一个地方:服务端写好了,工具也注册了,客户端却连不上,或者连上了但模型侧根本调不到工具。问题往往不在 MCP 框架本身,而在 endpoint 和鉴权通道没有统一。
我最近在做一个内部知识库助手,服务端用 .NET 8 写,客户端是 Claude Code 和 Cline 混用。一开始每个客户端各配一套地址和 Key,结果调试时经常出现「服务端日志显示收到请求,但模型侧返回空」的情况。后来把 MCP 服务端的 endpoint 统一改到 TaoToken 的 API 通道,用同一套 Key 和 Base URL 管理,问题才收敛。
这篇文章聚焦三款 .NET MCP 实现:MCPSharp、mcpdotnet、ModelContextProtocol.NET。它们都能跑 MCP 协议,但在 endpoint 配置、传输方式、AOT 兼容性上差异很大。我会给出三者的 appsettings 和环境变量配置模板,以及一次完整的 MCP 工具调用连通性验证步骤。目标很直接:让你在本地 MCP 服务端配置和客户端联调时,知道该选哪个、怎么配、报错怎么查。
适合谁看?如果你正在用 .NET 写 MCP 服务端,或者准备把现有工具暴露给 Claude Code、Cline 这类客户端,这篇能帮你少走弯路。如果你只是听说过 MCP 但还没动手,建议先跑通一个最小计算器工具,再回来看选型对照。
核心检索词先明确:.NET MCP 服务端 endpoint 配置、MCPSharp 接入、mcpdotnet 传输、ModelContextProtocol.NET AOT。这四个词贯穿全文,也是你在搜索排障时最可能用到的。
TaoToken 在这里的角色是统一 API 通道。官网入口在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api 。注意 API 地址不加 UTM,配置时直接用这个。
2. TaoToken 前置:Key、Base URL 与 MCP 服务端的关系
在讲三款框架的配置之前,先把 TaoToken 的前置概念理清楚。很多人把 MCP 服务端和模型 API 混在一起,其实它们是两层:MCP 服务端负责暴露工具,模型 API 负责理解意图并决定调用哪个工具。TaoToken 提供的是后者,也就是模型侧的 API 通道。
你需要准备三样东西:API Key、Base URL、Model ID。这三件套在 MCPSharp、mcpdotnet、ModelContextProtocol.NET 里都会用到,只是配置位置不同。
API Key 在 TaoToken 控制台的 API Keys 页面创建,地址是 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。创建后复制保存,后面配置里用占位符sk-xxxxxxxx代替。
Base URL 统一用https://taotoken.net/api。注意不要加 UTM 参数,也不要加尾部斜杠。有些客户端对尾部斜杠敏感,会导致 404。
Model ID 根据你用的模型填,比如claude-sonnet-4-20250514或gpt-4o。MCP 工具调用对模型能力有要求,建议选支持 function calling 的模型。
如果你还没决定用哪个模型,可以先到模型对话页面试一下: https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 。在对话里发一条消息,确认 Key 和 Base URL 能通,再往下配 MCP。
对于长期编码和 Agent 场景,Coding Plan 更划算: https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。它适合需要频繁调用模型、跑 MCP 工具链的开发者。
接入文档在这里: https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。配置过程中遇到参数不确定,优先查文档。
现在把三件套记下来:
| 配置项 | 值 | 说明 |
|---|---|---|
| Base URL | https://taotoken.net/api | 不加 UTM,不加尾部斜杠 |
| API Key | sk-xxxxxxxx | 控制台创建,注意保密 |
| Model ID | claude-sonnet-4-20250514 | 按需替换 |
这三件套在后面的 appsettings.json、环境变量、settings 片段里会反复出现。建议先复制到一个临时文件,配置时直接粘贴。
有一点要注意:MCP 服务端本身不直接调用模型 API,它是被客户端调用的。但很多 .NET MCP 框架在启动时会做一次模型侧的健康检查,或者把模型配置作为工具执行的一部分。所以 Base URL 和 Key 要配在服务端能读到的地方,而不是只配在客户端。
如果你用的是 Claude Code 作为客户端,它的配置入口在 ClaudeCodeAnthropic 相关文档里: https://taotoken.net/doc/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode_anthropic&utm_campaign=rewrite 。这里会涉及 Base URL 和 Key 的填写位置。
前置准备做完,下面进入三款框架的具体配置。
3. 三款 .NET MCP 实现的可复制配置模板
这一节是全文的核心。我会分别给出 MCPSharp、mcpdotnet、ModelContextProtocol.NET 的 appsettings.json 和环境变量配置片段,路径和原文一致,可以直接复制。
3.1 MCPSharp 的 appsettings 配置
MCPSharp 的特点是属性驱动,配置相对简单。它的服务端启动时自动扫描[McpTool]标记的方法。endpoint 配置主要影响它和客户端之间的传输,以及模型侧的健康检查。
在项目根目录创建appsettings.json:
{ "McpSharp": { "ServerName": "CalculatorServer", "ServerVersion": "1.0.0", "Transport": { "Type": "stdio", "Endpoint": "https://taotoken.net/api", "ApiKey": "sk-xxxxxxxx", "ModelId": "claude-sonnet-4-20250514" }, "Logging": { "LogLevel": "Information" } } }对应的环境变量写法:
export McpSharp__Transport__Endpoint="https://taotoken.net/api" export McpSharp__Transport__ApiKey="sk-xxxxxxxx" export McpSharp__Transport__ModelId="claude-sonnet-4-20250514"在Program.cs里读取配置:
using MCPSharp; using Microsoft.Extensions.Configuration; var config = new ConfigurationBuilder() .AddJsonFile("appsettings.json", optional: false) .AddEnvironmentVariables() .Build(); var endpoint = config["McpSharp:Transport:Endpoint"]; var apiKey = config["McpSharp:Transport:ApiKey"]; var modelId = config["McpSharp:Transport:ModelId"]; Console.WriteLine($"Endpoint: {endpoint}"); Console.WriteLine($"Model: {modelId}"); await MCPServer.StartAsync("CalculatorServer", "1.0.0");注意 MCPSharp 的StartAsync默认走 stdio 传输。如果你要改成 SSE,需要在配置里把Type改成sse,并确认客户端支持。
3.2 mcpdotnet 的 appsettings 配置
mcpdotnet 的配置更偏向传输和日志。它支持 STDIO 和 SSE 双传输,日志可以对接 Serilog。
appsettings.json:
{ "McpDotNet": { "Server": { "Name": "CalculatorServer", "Version": "1.0.0" }, "Transport": { "Type": "sse", "Endpoint": "https://taotoken.net/api", "ApiKey": "sk-xxxxxxxx", "ModelId": "claude-sonnet-4-20250514", "SsePath": "/mcp/sse" }, "Logging": { "Serilog": { "MinimumLevel": "Information", "WriteTo": [ { "Name": "Console", "Args": { "outputTemplate": "[{Timestamp:HH:mm:ss} {Level}] {Message}{NewLine}{Exception}" } } ] } } } }环境变量:
export McpDotNet__Transport__Endpoint="https://taotoken.net/api" export McpDotNet__Transport__ApiKey="sk-xxxxxxxx" export McpDotNet__Transport__ModelId="claude-sonnet-4-20250514" export McpDotNet__Transport__Type="sse"启动代码:
using McpDotNet; using McpDotNet.Transports; using Serilog; Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration( new ConfigurationBuilder() .AddJsonFile("appsettings.json") .AddEnvironmentVariables() .Build()) .CreateLogger(); var options = new McpOptions { Transport = new SseTransport(), Logger = Log.Logger }; var server = new McpServer(options); server.RegisterTool<Calculator>(); await server.StartAsync();mcpdotnet 的 SSE 传输需要指定SsePath,默认是/mcp/sse。如果你的客户端要求不同路径,在这里改。
3.3 ModelContextProtocol.NET 的 appsettings 配置
ModelContextProtocol.NET 强调 AOT 兼容,配置里需要显式声明类型。它的 endpoint 配置和前面两者类似,但工具注册方式不同。
appsettings.json:
{ "ModelContextProtocol": { "Server": { "Name": "AotSafeCalculator", "Version": "1.0.0" }, "Endpoint": { "BaseUrl": "https://taotoken.net/api", "ApiKey": "sk-xxxxxxxx", "ModelId": "claude-sonnet-4-20250514" }, "Aot": { "Enabled": true, "JsonSerializerContext": "McpJsonContext" } } }环境变量:
export ModelContextProtocol__Endpoint__BaseUrl="https://taotoken.net/api" export ModelContextProtocol__Endpoint__ApiKey="sk-xxxxxxxx" export ModelContextProtocol__Endpoint__ModelId="claude-sonnet-4-20250514" export ModelContextProtocol__Aot__Enabled="true"启动代码:
using ModelContextProtocol; var config = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .AddEnvironmentVariables() .Build(); var baseUrl = config["ModelContextProtocol:Endpoint:BaseUrl"]; var apiKey = config["ModelContextProtocol:Endpoint:ApiKey"]; var server = new McpServer(); server.RegisterTool(new AotSafeCalculator()); await server.StartAsync();AOT 场景下,JsonSerializerContext必须显式定义,否则序列化会失败。这是 ModelContextProtocol.NET 和另外两款最大的差异。
3.4 三件套配置对照表
| 框架 | Base URL 配置键 | Key 配置键 | Model ID 配置键 | 传输默认 |
|---|---|---|---|---|
| MCPSharp | McpSharp:Transport:Endpoint | McpSharp:Transport:ApiKey | McpSharp:Transport:ModelId | stdio |
| mcpdotnet | McpDotNet:Transport:Endpoint | McpDotNet:Transport:ApiKey | McpDotNet:Transport:ModelId | sse |
| ModelContextProtocol.NET | ModelContextProtocol:Endpoint:BaseUrl | ModelContextProtocol:Endpoint:ApiKey | ModelContextProtocol:Endpoint:ModelId | stdio |
三者的 Base URL 都是https://taotoken.net/api,Key 都是sk-xxxxxxxx,Model ID 按需替换。配置键名不同,但值一致。这就是统一通道的好处:换框架时只改键名,不改值。
如果你用 CC Switch 或 Cline MCP 管理客户端,配置里同样要写全三件套。CC Switch 的配置片段:
{ "mcpServers": { "calculator": { "command": "dotnet", "args": ["run", "--project", "./CalculatorServer"], "env": { "McpSharp__Transport__Endpoint": "https://taotoken.net/api", "McpSharp__Transport__ApiKey": "sk-xxxxxxxx", "McpSharp__Transport__ModelId": "claude-sonnet-4-20250514" } } } }Cline MCP 的配置类似,只是字段名可能不同。核心是三件套不能缺。
4. 验证请求:一次 MCP 工具调用的连通性检查
配置写完,下一步是验证。不要直接上复杂工具,先用一个加法计算器跑通链路。
4.1 服务端启动与日志确认
以 MCPSharp 为例,启动服务端:
dotnet run --project ./CalculatorServer正常输出应该包含:
Endpoint: https://taotoken.net/api Model: claude-sonnet-4-20250514 MCP Server CalculatorServer 1.0.0 started Transport: stdio如果 endpoint 打印为空,说明配置没读到。检查 appsettings.json 的复制到输出目录设置,或者环境变量前缀是否正确。
4.2 客户端发起工具调用
用 Claude Code 作为客户端,在项目目录下创建.mcp.json:
{ "mcpServers": { "calculator": { "command": "dotnet", "args": ["run", "--project", "./CalculatorServer"], "env": { "McpSharp__Transport__Endpoint": "https://taotoken.net/api", "McpSharp__Transport__ApiKey": "sk-xxxxxxxx", "McpSharp__Transport__ModelId": "claude-sonnet-4-20250514" } } } }然后在 Claude Code 里发一条消息:
请调用 calculator 工具的 add 方法,计算 3 + 5预期返回:
工具调用结果:8如果返回空或者报错,进入下一节排查。
4.3 用 curl 直接验证 API 通道
在配 MCP 之前,先确认 TaoToken 的 API 通道本身是通的:
curl -X POST https://taotoken.net/api/v1/messages \ -H "Content-Type: application/json" \ -H "x-api-key: sk-xxxxxxxx" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "claude-sonnet-4-20250514", "max_tokens": 100, "messages": [ {"role": "user", "content": "回复 OK"} ] }'正常返回:
{ "id": "msg_xxx", "type": "message", "role": "assistant", "content": [ {"type": "text", "text": "OK"} ] }如果这一步就失败,说明 Key 或 Base URL 有问题,先解决这个,再查 MCP 配置。
4.4 验证 MCP 工具列表
有些客户端支持列出 MCP 工具。在 Claude Code 里输入:
/mcp list应该看到:
calculator: - add: Adds two numbers - calculateTax: Calculate tax based on income如果工具列表为空,说明服务端注册工具失败。检查[McpTool]标记的方法是否是 public static,以及程序集是否被扫描到。
4.5 成功结果的判断标准
一次完整的连通性验证,成功标准是:
服务端日志出现Tool add invoked with args: a=3, b=5;客户端返回8;TaoToken 控制台的 API 调用记录里能看到这次请求。
三者缺一不可。如果服务端有日志但客户端没返回,问题在传输层;如果客户端有返回但控制台没记录,问题在模型侧配置。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
这一节对照真实报错,给出排查路径。这些错误在 .NET MCP 接入 TaoToken 时出现频率最高。
5.1 401 Unauthorized
报错原文:
HTTP 401 Unauthorized: invalid api key原因:API Key 没配、配错、或者带了多余空格。
排查步骤:检查 appsettings.json 里的ApiKey值是否以sk-开头;检查环境变量是否覆盖了配置文件,且值正确;用 curl 直接测 API 通道,确认 Key 本身有效。
如果 curl 能通但 MCP 服务端报 401,说明服务端读到的 Key 不对。在启动代码里打印 Key 的前 8 位:
Console.WriteLine($"Key prefix: {apiKey?.Substring(0, 8)}");对比控制台里的 Key 前缀。
5.2 local proxy failed
报错原文:
local proxy failed: connection refused原因:客户端配置的 MCP 服务端地址不对,或者服务端没启动。
排查步骤:确认服务端进程在运行;确认客户端配置的command和args能正确启动服务端;如果是 SSE 传输,确认端口没被占用。
mcpdotnet 的 SSE 传输默认监听本地端口,如果端口冲突会报这个错。改SsePath或换端口。
5.3 reading choices 报错
报错原文:
error reading choices: unexpected end of JSON input原因:模型返回的 JSON 不完整,通常是 max_tokens 太小或者模型不支持 function calling。
排查步骤:把 max_tokens 调到 1024 以上;确认 Model ID 是支持工具调用的模型;检查请求体里 tools 字段的格式是否符合 MCP 规范。
在 MCPSharp 里,工具描述太长也会导致这个问题。把[McpTool]的描述精简到 50 字以内。
5.4 OAuth 相关报错
报错原文:
OAuth token exchange failed: invalid_grant原因:客户端配置了 OAuth 但 TaoToken 通道用的是 API Key 鉴权,两者冲突。
排查步骤:在客户端配置里禁用 OAuth,改用 API Key;确认.mcp.json里没有oauth字段;如果用的是 Claude Code,检查ClaudeCodeAnthropic配置里是否误开了 OAuth。
Claude Code 的配置参考: https://taotoken.net/doc/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode_anthropic&utm_campaign=rewrite 。
5.5 工具注册成功但调用返回空
报错表现:服务端日志显示工具已注册,客户端调用后返回空字符串。
原因:工具方法的参数类型不匹配,或者返回值没被序列化。
排查步骤:检查[McpParameter]标记的参数类型是否是基础类型;复杂对象参数在 AOT 场景下要用 JSON 字符串传递;在工具方法里加日志,确认方法被调用。
MCPSharp 的复杂对象参数在非 AOT 场景下可以直接用,但 ModelContextProtocol.NET 必须用 string 传 JSON。
5.6 三款框架的报错差异对照
| 报错 | MCPSharp | mcpdotnet | ModelContextProtocol.NET |
|---|---|---|---|
| 401 | 检查 Transport:ApiKey | 检查 Transport:ApiKey | 检查 Endpoint:ApiKey |
| local proxy failed | 检查 stdio 启动 | 检查 SSE 端口 | 检查 stdio 启动 |
| reading choices | 精简工具描述 | 调大 max_tokens | 检查 AOT 序列化 |
| OAuth | 禁用 OAuth | 禁用 OAuth | 禁用 OAuth |
排查顺序建议:先 curl 测 API 通道,再查服务端配置,最后查客户端配置。这样能快速定位问题在哪一层。
6. 选型对照与后续接入建议
三款框架都配完、跑通之后,选型其实就看你的项目约束。
MCPSharp 适合快速开发。属性驱动,零配置启动,工具注册用[McpTool]标记,代码量最少。如果你要在一天内把现有 .NET 方法暴露成 MCP 工具,选它。缺点是传输默认 stdio,SSE 支持需要额外配置。
mcpdotnet 适合企业级场景。日志集成 Serilog,传输支持 STDIO 和 SSE 双模式,协议兼容性严格。如果你需要跨平台部署、日志追踪、和现有 .NET 日志体系对接,选它。缺点是工具注册要手动实现接口,代码量比 MCPSharp 多。
ModelContextProtocol.NET 适合 AOT 部署。显式类型定义,兼容 Blazor WebAssembly 和原生 AOT。如果你要把 MCP 服务端编译成单文件、跑在边缘设备上,选它。缺点是社区活跃度低,部分功能还在开发中,遇到问题查资料难。
选型对照表:
| 维度 | MCPSharp | mcpdotnet | ModelContextProtocol.NET |
|---|---|---|---|
| 工具注册 | 属性标记 | 接口实现 | 接口实现 |
| 传输 | stdio 为主 | stdio + SSE | stdio 为主 |
| AOT 兼容 | 一般 | 一般 | 好 |
| 日志 | 基础 | Serilog 深度集成 | 基础 |
| 社区活跃 | 高 | 中 | 低 |
| 适合场景 | 快速开发 | 企业级 | AOT 部署 |
如果你还在犹豫,先用 MCPSharp 跑通一个最小工具,确认 TaoToken 通道没问题,再根据项目需求换框架。三者的 Base URL 和 Key 配置值一致,换框架时只改键名,迁移成本低。
后续接入建议:把 MCP 服务端的 endpoint 统一到https://taotoken.net/api,Key 用同一个,Model ID 按客户端需求配。这样无论你用哪款框架、哪个客户端,鉴权通道都是一套。
需要创建新 Key 或查看用量,到控制台: https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 。API Keys 管理页面: https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。
如果你要长期跑编码 Agent,Coding Plan 比按量计费更稳: https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。
最后一步实操:把本文的 appsettings.json 片段复制到你的项目,改掉sk-xxxxxxxx,启动服务端,用 curl 测一次 API 通道,再用客户端调一次 add 工具。跑通之后,你就有了一个可复用的 .NET MCP 接入模板。