
1. 为什么我要自己写一个 C# MCP Client如果你最近在折腾本地 AI 工具链大概率会遇到一个很现实的问题模型通道太散了。写代码用一个平台跑 Agent 用一个平台做文档问答又换一个平台每个平台一套 Key、一套计费、一套限流规则。项目小的时候还能忍一旦要在企业内网或者自己的 C# 服务里统一管理就会变得非常难受。MCPModel Context Protocol解决的正是「工具怎么被模型调用」这件事。它把工具、资源、提示词抽象成一套标准协议Client 负责连接 Server 并暴露工具列表模型再决定调用哪个工具。市面上确实有不少现成的免费 Client但企业业务场景里免费 Client 往往没法满足需求要么不能嵌入自己的 C# 进程要么没法统一走一个模型网关要么日志和权限控制完全不可控。所以这篇就干一件事用 C# 从零写一个最小可用的 MCP Client把 settings.json 骨架、TaoToken 统一 Key 的接入位置、以及一次完整的连接与工具调用验证全部跑通。适合谁有 .NET 基础、想在本地 AI 工具链里统一模型通道、又不想被某个客户端绑死的开发者。读完你能得到一个能塞进自己项目的 MCP Client 骨架而不是一个只能看不能改的 Demo。我试过把模型 Key 散落在各个配置文件里后来统一收敛到 TaoToken 一个 Key维护成本直接降下来。下面按「先搭骨架、再接通道、最后验证」的顺序来。2. TaoToken 前置准备统一 Key 与 settings.json 骨架在写代码之前先把「模型通道」这件事定下来。MCP Client 本身只负责协议通信真正调用模型比如让模型决定调用哪个工具需要一个统一的入口。这里用 TaoToken 作为统一模型通道好处是一个 Key 覆盖多种模型C# 项目里只需要维护一份配置。官网入口https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentAPI 基址注意不带 UTMhttps://taotoken.net/api你需要先去控制台创建一个 API Key然后把它写进项目的 settings.json。这个文件我建议放在项目根目录并且加入 .gitignore避免 Key 被提交。骨架长这样{ McpClient: { ServerName: local-tools, Transport: sse, Endpoint: https://your-mcp-server.example.com/sse, RequestTimeoutSeconds: 30 }, TaoToken: { BaseUrl: https://taotoken.net/api, ApiKey: sk-你的TaoTokenKey, DefaultModel: claude-3-5-sonnet, MaxTokens: 2048 }, Logging: { LogLevel: { Default: Information } } }几个字段说明一下。McpClient.Endpoint是你 MCP Server 的 SSE 地址本地调试时换成自己的地址即可。TaoToken.BaseUrl固定为https://taotoken.net/apiApiKey从控制台获取。DefaultModel是模型通道的默认模型后面调用工具时如果没指定就用它。注意settings.json 里的 ApiKey 只用于本地开发。生产环境请走环境变量或密钥管理服务不要硬编码进镜像。创建 Key 的入口在这里https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content接入文档配置参数、鉴权方式都在里面https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content这一步做完你手里应该有两样东西一个可用的 TaoToken Key一份 settings.json 骨架。接下来才是写 C# 代码。3. 可复制配置新建项目与 MCP SDK 接入先建项目。打开终端执行dotnet new console -n McpClientDemo -f net8.0 cd McpClientDemo框架选 .NET 8这是目前长期支持版本MCP 的 C# SDK 在 .NET 8 上跑得最稳。然后添加 MCP SDK。官方提供的 C# 版本包名是ModelContextProtocol目前还是预发行版添加时记得勾选「包括预发行版」dotnet add package ModelContextProtocol --prerelease装完之后在.csproj里确认一下引用同时把 settings.json 设置为「复制到输出目录」这样运行时能读到ItemGroup None Updatesettings.json CopyToOutputDirectoryPreserveNewest/CopyToOutputDirectory /None /ItemGroup接着写配置读取。我用Microsoft.Extensions.Configuration来读 settings.json这样和 ASP.NET Core 项目的习惯一致dotnet add package Microsoft.Extensions.Configuration.Json dotnet add package Microsoft.Extensions.Configuration.Binder配置类定义如下public class McpClientOptions { public string ServerName { get; set; } local-tools; public string Transport { get; set; } sse; public string Endpoint { get; set; } string.Empty; public int RequestTimeoutSeconds { get; set; } 30; } public class TaoTokenOptions { public string BaseUrl { get; set; } https://taotoken.net/api; public string ApiKey { get; set; } string.Empty; public string DefaultModel { get; set; } claude-3-5-sonnet; public int MaxTokens { get; set; } 2048; }在Program.cs里加载配置using Microsoft.Extensions.Configuration; var config new ConfigurationBuilder() .SetBasePath(AppContext.BaseDirectory) .AddJsonFile(settings.json, optional: false, reloadOnChange: true) .Build(); var mcpOptions config.GetSection(McpClient).GetMcpClientOptions()!; var taoOptions config.GetSection(TaoToken).GetTaoTokenOptions()!; Console.WriteLine($MCP Server: {mcpOptions.ServerName}); Console.WriteLine($TaoToken BaseUrl: {taoOptions.BaseUrl});到这里项目骨架和配置就位。注意 TaoToken 的 Key 只在配置里出现一次后面所有模型调用都从这里取这就是「统一 Key」的意义。4. 实现 MCP Client 并验证工具调用现在写核心的 MCP Client。SDK 里主要用到McpClientFactory和SseClientTransport前者负责创建客户端后者负责 SSE 传输层。using ModelContextProtocol.Client; using ModelContextProtocol.Protocol.Transport; var transport new SseClientTransport( new SseClientTransportOptions { Endpoint new Uri(mcpOptions.Endpoint), Name mcpOptions.ServerName }); var client await McpClientFactory.CreateAsync(transport); var tools await client.ListToolsAsync(); Console.WriteLine(可用工具列表); foreach (var tool in tools) { Console.WriteLine($ 名称{tool.Name}说明{tool.Description}); }这段代码跑起来你会看到 MCP Server 暴露的工具列表比如网页抓取、文件读取之类的。到这里只完成了「连接」还没验证「工具调用」。下面加一段调用逻辑同时把 TaoToken 的模型通道接进来。工具调用的思路是先拿到工具列表选一个工具构造参数调用CallToolAsync。为了演示完整链路我用一个「回显」工具做验证参数是textvar toolName tools.First().Name; var arguments new Dictionarystring, object? { [text] hello mcp }; var result await client.CallToolAsync(toolName, arguments); Console.WriteLine($调用工具 {toolName} 结果); foreach (var content in result.Content) { if (content is TextContentBlock text) { Console.WriteLine(text.Text); } }如果你想让模型来决定调用哪个工具就把工具列表转成模型能理解的格式通过 TaoToken 的 API 发过去。这里用HttpClient直接调注意 BaseUrl 和鉴权头using System.Net.Http.Headers; using System.Net.Http.Json; var http new HttpClient(); http.BaseAddress new Uri(taoOptions.BaseUrl); http.DefaultRequestHeaders.Authorization new AuthenticationHeaderValue(Bearer, taoOptions.ApiKey); var toolSchema tools.Select(t new { name t.Name, description t.Description }).ToArray(); var payload new { model taoOptions.DefaultModel, max_tokens taoOptions.MaxTokens, messages new[] { new { role user, content 请从可用工具中选择一个并说明理由。 } }, tools toolSchema }; var response await http.PostAsJsonAsync(/v1/chat/completions, payload); var body await response.Content.ReadAsStringAsync(); Console.WriteLine($模型响应状态{response.StatusCode}); Console.WriteLine(body);运行后控制台会先打印工具列表再打印工具调用结果最后打印模型返回的 JSON。如果三步都正常说明 MCP Client 和 TaoToken 统一 Key 的链路已经打通。提示/v1/chat/completions是兼容 OpenAI 风格的路径具体参数以接入文档为准。模型名按你控制台里可用的填。5. 本篇常见错排查跑不通的时候八成是下面几个问题。第一个找不到 settings.json。报错通常是FileNotFoundException。原因是文件没复制到输出目录。检查.csproj里的CopyToOutputDirectory是否设置成PreserveNewest然后重新dotnet build。第二个SSE 连接超时。报错类似TaskCanceledException。先确认Endpoint地址能通本地 Server 是否启动。如果 Server 在内网注意防火墙。RequestTimeoutSeconds可以适当调大但别超过 60 秒否则排查困难。第三个401 Unauthorized。这是 TaoToken Key 的问题。检查ApiKey是否以Bearer方式放进请求头Key 有没有多余空格以及是否在控制台里被禁用。重新生成一个 Key 再试。第四个模型名不存在。返回 404 或model_not_found。DefaultModel必须是你账号下可用的模型名别照抄示例里的名字去控制台确认。第五个工具调用参数类型不匹配。CallToolAsync的参数字典值类型要和工具定义的 schema 对齐。比如 schema 要求string你传了intServer 会直接拒绝。调试时先把参数打印出来核对。第六个预发行版 SDK 版本冲突。ModelContextProtocol还在预览阶段不同版本 API 可能有差异。如果编译报方法找不到先dotnet list package看实际版本再对照官方示例调整。排查顺序建议先确认配置读取成功再确认 SSE 连接最后确认模型调用。每步都打印日志别一次性全跑。6. 后续怎么把这个骨架用起来这个最小 Client 跑通之后你可以往几个方向扩展。一是把工具列表缓存起来避免每次启动都请求 Server二是加一层重试和熔断网络抖动时不至于整个流程挂掉三是把 TaoToken 的调用封装成独立的IModelChannel接口方便以后换模型或加多通道。如果你后面要做长期编码或者 Agent 类任务可以考虑 Coding Plan把模型通道和额度统一管理https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content想直接在网页里验证模型和工具调用效果用模型对话入口最快https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content需要管理多个 Key、查看用量去控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content如果你用的是 Claude Code 这类工具Anthropic 兼容接入的说明在这里https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content最后给一个实用建议把 settings.json 里的 Key 换成环境变量读取代码里用Environment.GetEnvironmentVariable(TAOTOKEN_API_KEY)兜底。这样本地开发方便上线也不会因为误提交配置泄露 Key。骨架已经能跑剩下的就是按你的业务往里填工具和模型逻辑了。