我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

C# 实现实时查询股票信息的 MCP Server:TaoToken 统一 Key 接入实战

C# 实现实时查询股票信息的 MCP Server:TaoToken 统一 Key 接入实战 1. 从零搭建 C# MCP Server 实时查询股票信息为什么需要统一 Key 通道如果你正在用 C# 做 AI Agent 或者智能投研工具大概率会遇到一个很现实的问题模型能聊天但拿不到实时行情。你问它现在贵州茅台多少钱它只能给你一段训练数据里的历史价格或者干脆编一个数字。要让它真正能查股票就得给它接一个外部数据源而 MCP Server 就是目前最顺手的方案。MCP 全称 Model Context Protocol是 Anthropic 在 2024 年底推出的开放协议目标是给大语言模型提供统一的接口标准让模型能无缝调用外部数据源、工具和服务。你可以把它理解成AI 领域的 USB-C 接口——以前每个模型接每个工具都要写一套适配代码现在只要工具端实现 MCP Server模型端实现 MCP Client两边就能对上。对 C# 开发者来说官方提供了ModelContextProtocol.AspNetCore这个 NuGet 包配合 ASP.NET Core 的依赖注入和路由几十行代码就能把一个行情查询能力暴露成 MCP 工具。但光有 MCP Server 还不够。真正跑起来的时候你会发现两个坑第一行情接口本身需要处理编码、Referer、字段解析这些脏活第二如果你后面要接大模型来做自然语言查询模型调用这一层又需要一套 API Key 和鉴权通道。如果每个模型、每个工具都单独配 Key管理成本会迅速失控。我试过同时维护三四个模型的 Key改一次配置要翻好几个文件很容易漏。所以这篇的实战路线是用 C# 从零写一个能实时查 A 股行情的 MCP Server把行情接口封装成 MCP 工具然后通过 TaoToken 的统一 Key/API 通道完成模型侧的鉴权和调用。TaoToken 在这里的角色是统一入口——你不需要为每个模型单独申请和管理 Key用一套通道就能把 MCP 工具挂到模型对话或编码 Agent 上。官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 后面配置里会反复用到。这篇文章适合谁适合有 C# 基础、想给自己的 AI 工具加实时数据能力的开发者也适合已经在用 Claude Code、Cline 这类工具想通过 MCP 扩展自定义工具的玩家。你不需要事先懂 MCP 协议细节跟着步骤走就能跑通端到端链路。整篇会给出可复制的项目结构、完整的行情封装代码、MCP 工具定义、TaoToken 配置片段以及本地启动和一次真实查询的验证过程。踩过的坑我也会标出来比如 GB2312 编码在 .NET 里的注册、MCP 端点路由冲突、401 报错怎么排查。先说清楚技术选型。行情数据源我用的是新浪财经的公开查询接口仅用于学习研究演示不涉及任何商业用途。它的返回是 GB2312 编码的文本格式是var hq_str_sh600000浦发银行,10.00,...;这种需要正则提取引号内的内容再按逗号切分。MCP Server 用 ASP.NET Core 的 HTTP TransportSSE 模式这样既能本地跑也能挂到已有项目里。模型侧通过 TaoToken 的 API 通道调用Key 和 Base URL 统一配置。整个链路是这样的模型或 MCP Client发起工具调用 → MCP Server 收到GetQuote请求 → 调用新浪行情接口 → 解析成结构化数据 → 返回格式化文本 → 模型拿到结果组织成自然语言回答。TaoToken 负责的是模型这一侧的鉴权和路由让你不用为每个模型单独折腾 Key。下面从项目创建开始一步步来。2. TaoToken 前置准备统一 Key 与 API 通道配置在写代码之前先把 TaoToken 这一侧准备好。很多人卡在最后一步调不通其实不是代码问题而是 Key 没配对或者 Base URL 写错了。这一节把前置动作讲清楚后面配置片段直接抄就行。TaoToken 的核心价值是统一 Key。传统做法是你要用 Claude 就申请 Anthropic 的 Key要用 GPT 就申请 OpenAI 的 Key每个 Key 的额度、计费、限流都分开管。TaoToken 把这些收敛成一个入口你拿一个 Key通过统一的 API 通道去调用不同模型。对 MCP 场景来说这意味着你的 MCP Server 不需要关心底层是哪个模型只要按统一格式发请求就行。第一步打开 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 创建 API Key。登录后进入控制台找到 API Keys 页面点新建。生成的 Key 一般以sk-开头复制下来保存好页面刷新后就看不到了。这个 Key 就是后面配置里的TAOTOKEN_API_KEY。第二步确认 Base URL。TaoToken 的 API 入口是 https://taotoken.net/api 注意这里不带任何查询参数。很多 401 报错就是因为把带 UTM 的官网地址当成了 API 地址或者多写了一个斜杠。正确的 Base URL 就是https://taotoken.net/api后面拼/v1/chat/completions这类路径。第三步确认你要用的模型 ID。TaoToken 支持多种模型具体可用列表在控制台或文档里能查到。常见的比如claude-sonnet-4-20250514、gpt-4o这类。MCP 场景下模型 ID 要填在客户端配置里不是填在 MCP Server 里——MCP Server 只负责提供工具模型是 MCP Client 那一侧选的。这里有个容易混淆的点MCP Server 和模型调用是两层。MCP Server 暴露的是工具比如GetQuote模型是调用方它决定什么时候调这个工具。TaoToken 的 Key 是给模型调用这一层用的不是给 MCP Server 用的。所以你的 MCP Server 本身不需要 TaoToken Key需要 Key 的是连接模型的 MCP Client比如 Claude Code、Cline或者你自己写的 Agent。如果你用的是 Claude Code 这类工具配置方式是在 settings 里填 Base URL 和 Key。如果是自己写代码调用就是在 HTTP 请求头里带Authorization: Bearer 你的Key。下面给一个标准的配置片段路径和字段名按实际工具调整{ mcpServers: { stock-server: { url: http://localhost:5000/mcp, transport: sse } }, model: { baseUrl: https://taotoken.net/api, apiKey: sk-你的TaoTokenKey, modelId: claude-sonnet-4-20250514 } }注意baseUrl是https://taotoken.net/api不要写成官网首页。apiKey填你刚创建的 Key。modelId按你实际要用的模型填。MCP Server 的url指向你本地启动的服务端口按实际改。如果你用的是 Claude Code配置入口在~/.claude/settings.json或者项目级的.claude/settings.json字段名可能是env下的ANTHROPIC_BASE_URL和ANTHROPIC_API_KEY。具体以你用的版本为准但核心就三件套Base URL、Key、Model ID。这三样对齐了后面基本不会出鉴权问题。还有一个前置动作是确认 .NET 环境。这篇用的是 .NET 9你需要装好 SDK。命令行跑dotnet --version能看到 9.x 就行。如果还是 8.x大部分代码也能跑但ModelContextProtocol.AspNetCore的某些版本可能要求 9。建议直接上 9省得折腾。最后提醒一句TaoToken 的 Key 不要硬编码进代码提交到 Git。用环境变量或者用户机密dotnet user-secrets管理。后面配置片段里我会用占位符你替换成自己的就行。前置准备做完就可以开始建项目了。3. 可复制配置项目结构、行情封装与 MCP 工具定义这一节是重头戏给出完整可复制的代码。项目结构很简单一个 ASP.NET Core Web 项目加两个核心文件行情客户端和 MCP 工具类。先创建项目dotnet new web -n StockMcpServer cd StockMcpServer dotnet add package ModelContextProtocol.AspNetCore项目结构大概是这样StockMcpServer/ ├── Program.cs ├── SinaStockClient.cs ├── StockTool.cs ├── StockQuote.cs └── StockMcpServer.csproj先写行情数据模型StockQuote.csnamespace StockMcpServer; public record StockQuote { public string? Symbol { get; set; } public string? Name { get; set; } public string? Open { get; set; } public string? Close { get; set; } public string? Price { get; set; } public string? High { get; set; } public string? Low { get; set; } public string? Volume { get; set; } public string? Amount { get; set; } public string? Date { get; set; } public string? Time { get; set; } }然后是行情客户端SinaStockClient.cs。这里用IHttpClientFactory注入方便在 MCP 工具里复用。注意 GB2312 编码需要注册CodePagesEncodingProvider否则 .NET Core 默认拿不到这个编码会抛异常using System.Text; using System.Text.RegularExpressions; namespace StockMcpServer; public partial class SinaStockClient { private const string SinaApiBase https://hq.sinajs.cn/list; private readonly HttpClient _client; public SinaStockClient(IHttpClientFactory factory) { _client factory.CreateClient(); _client.DefaultRequestHeaders.Referrer new Uri(https://finance.sina.com.cn); } public async TaskStockQuote? MakeStockRequest(string symbol) { var url ${SinaApiBase}{symbol}; try { var response await _client.GetByteArrayAsync(url); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var text Encoding.GetEncoding(GB2312).GetString(response); return ParseSinaStockData(text, symbol); } catch (Exception ex) { Console.WriteLine($Error making stock request: {ex.Message}); return null; } } private static StockQuote? ParseSinaStockData(string text, string symbol) { var match StockRegex().Match(text); if (!match.Success) return null; var values match.Groups[1].Value.Split(,); if (values.Length 32) return null; return new StockQuote { Symbol symbol, Name values[0], Open values[1], Close values[2], Price values[3], High values[4], Low values[5], Volume values[8], Amount values[9], Date values[30], Time values[31] }; } public static string FormatQuote(StockQuote quote) { if (quote null) return Invalid quote data; var changeRate (Convert.ToDouble(quote.Price) - Convert.ToDouble(quote.Close)) / Convert.ToDouble(quote.Close) * 100; return string.Join(\n, ---, $Stock Code: {quote.Symbol}, $Stock Name: {quote.Name}, $Current Price: {quote.Price}, $Change Rate: {changeRate:F2}%, $Open Price: {quote.Open}, $High Price: {quote.High}, $Low Price: {quote.Low}, $Volume: {Convert.ToDouble(quote.Volume) / 100:F0} lots, $Turnover: {Convert.ToDouble(quote.Amount) / 10000:F2} million, $Update Time: {quote.Date} {quote.Time}, --- ); } [GeneratedRegex(\(.*)\)] private static partial Regex StockRegex(); }接着是 MCP 工具类StockTool.cs。[McpServerToolType]标记这个类包含 MCP 工具[McpServerTool]标记具体方法[Description]是给模型看的说明模型靠这个判断什么时候调用using System.ComponentModel; using ModelContextProtocol.Server; namespace StockMcpServer; [McpServerToolType] public static class StockTool { [McpServerTool] [Description(Get real-time stock quote by symbol, e.g. sh600000, sz000001)] public static async Taskstring GetQuote( IHttpClientFactory clientFactory, [Description(Stock symbol (e.g.: sh600000, sz000001))] string symbol) { var stockClient new SinaStockClient(clientFactory); var quote await stockClient.MakeStockRequest(symbol); if (quote ! null) return SinaStockClient.FormatQuote(quote); return 没有找到这只股票的数据; } }最后是Program.cs注册 HttpClient、MCP Server并映射端点using StockMcpServer; var builder WebApplication.CreateBuilder(args); builder.Services.AddHttpClient(); builder.Services.AddMcpServer() .WithHttpTransport() .WithToolsFromAssembly(); var app builder.Build(); app.MapMcp(); app.Run();注意WithHttpTransport()用的是 SSE 模式WithToolsFromAssembly()会自动扫描当前程序集里带[McpServerToolType]的类。app.MapMcp()默认映射到/mcp如果你要整合到已有项目里可以写成app.MapMcp(mcp)避免路由冲突。跑起来dotnet run默认监听http://localhost:5000具体端口看控制台输出。MCP 端点就是http://localhost:5000/mcp。到这里MCP Server 这一侧就完成了。下一节验证它能不能真的查到数据。4. 验证请求与成功结果本地启动到一次真实查询代码写完最关键的是验证。很多人写完直接接模型结果模型说工具调用失败回头排查很痛苦。正确做法是先单独验证 MCP Server 本身能返回数据再接模型。第一步启动服务dotnet run控制台会输出类似Now listening on: http://localhost:5000。记下这个地址。第二步先验证行情接口本身通不通。你可以临时在Program.cs里加一个测试端点或者直接写个小控制台测试。更简单的办法是用curl直接打新浪接口确认网络和编码没问题curl -H Referer: https://finance.sina.com.cn https://hq.sinajs.cn/listsh600000如果返回类似var hq_str_sh600000浦发银行,...;的文本说明行情源可达。如果返回空或者 403检查 Referer 头有没有带。第三步验证 MCP 端点。MCP 的 SSE 模式需要先建立连接再发请求用curl直接测有点麻烦。推荐用 MCP Inspector 这个官方调试工具或者用你熟悉的 MCP Client 连一下。如果你用 Claude Code直接在配置里加上{ mcpServers: { stock-server: { url: http://localhost:5000/mcp, transport: sse } } }然后在对话里问帮我查一下 sh600000 的实时行情。如果配置正确模型会调用GetQuote工具返回类似这样的结果--- Stock Code: sh600000 Stock Name: 浦发银行 Current Price: 10.25 Change Rate: 1.23% Open Price: 10.10 High Price: 10.30 Low Price: 10.05 Volume: 1234567 lots Turnover: 12345.67 million Update Time: 2025-01-15 14:30:00 ---看到这个输出说明端到端链路通了模型 → MCP Client → MCP Server → 行情接口 → 返回 → 模型组织语言。整个过程里TaoToken 负责的是模型调用那一层的鉴权你会在 MCP Client 的配置里填 Base URL 和 Key。如果你是自己写 Agent 调用核心代码是这样var mcpClient await McpClientFactory.CreateAsync( new SseClientTransport(new SseClientTransportOptions { Endpoint new Uri(http://localhost:5000/mcp) })); var tools await mcpClient.ListToolsAsync(); // 把 tools 注册到模型的 function calling 里模型侧通过 TaoToken 调用时HTTP 请求头带Authorization: Bearer sk-你的TaoTokenKey Content-Type: application/json请求体里的model字段填你要用的模型 IDmessages里带上工具定义。TaoToken 会把请求路由到对应模型返回结果。这样你就不需要为每个模型单独配 Key 了。验证通过后你可以多试几个股票代码比如sz000001平安银行、sh601318中国平安确认解析逻辑对不同股票都正常。如果某只股票返回没有找到数据先确认代码格式对不对——新浪接口要求带sh或sz前缀。5. 本篇常见错误排查401、local proxy failed、reading choices 与 OAuth这一节把实际会遇到的报错列出来对照排查。这些错误我基本都踩过按顺序检查能省很多时间。401 Unauthorized。这是最常见的。原因通常是三种Key 没填、Key 填错、Base URL 写错。先检查Authorization头是不是Bearer sk-xxx格式注意Bearer和 Key 之间有一个空格。再检查 Base URL 是不是https://taotoken.net/api不要带 UTM 参数不要多斜杠。如果你用的是 Claude Code检查ANTHROPIC_BASE_URL和ANTHROPIC_API_KEY这两个环境变量有没有生效。可以用echo $ANTHROPIC_BASE_URL确认。local proxy failed。这个报错通常出现在 MCP Client 连接 MCP Server 的时候不是模型调用那一层。意思是客户端连不上你本地的 MCP Server。检查三件事MCP Server 有没有真的启动看控制台有没有Now listening on配置里的 URL 端口对不对默认 5000但可能被占用改成别的transport 类型是不是sse。如果 MCP Server 和 Client 不在同一台机器还要确认防火墙。reading choices 相关报错。这个一般出现在模型返回结果解析阶段提示读取choices字段失败。原因通常是模型返回的不是标准 OpenAI 格式或者请求体里的model字段填了一个 TaoToken 不支持的模型 ID。解决办法是去 TaoToken 控制台确认可用模型列表把modelId换成列表里的值。另外检查请求体是不是标准的{model: ..., messages: [...]}结构。OAuth 相关报错。如果你用的工具走 OAuth 流程比如某些 Claude Code 版本可能会提示 OAuth token 无效。这种情况通常是因为工具默认走官方 OAuth你需要显式配置 Base URL 和 API Key 来覆盖。检查 settings 里有没有正确设置env字段把ANTHROPIC_BASE_URL指向 TaoToken 的 API 地址。如果工具同时支持 OAuth 和 API Key优先用 API Key 模式配置更直接。MCP 工具没被调用。模型不调工具通常是[Description]写得不够清楚。模型靠描述判断什么时候用这个工具。把描述写具体比如Get real-time stock quote by symbol, e.g. sh600000, sz000001比查询股票效果好很多。另外确认WithToolsFromAssembly()有没有生效可以在启动日志里看有没有扫描到工具。GB2312 编码异常。如果报GB2312 is not a supported encoding name说明CodePagesEncodingProvider没注册。确认Encoding.RegisterProvider(CodePagesEncodingProvider.Instance)这行在GetEncoding之前执行。这个注册只需要一次放在静态构造函数或者方法开头都行。端口被占用。dotnet run报Address already in use说明 5000 端口被占了。改launchSettings.json里的端口或者用dotnet run --urls http://localhost:5001指定。改完记得同步更新 MCP Client 配置里的 URL。排查顺序建议先确认 MCP Server 能独立返回数据用 curl 测行情接口再确认 MCP 端点能连上用 Inspector 或 Client 测最后确认模型调用通检查 Key 和 Base URL。一层一层来不要跳步。6. 语义一致 CTA把统一 Key 通道用起来链路跑通之后你会发现这套组合的扩展性不错。MCP Server 这一侧你只要加新的[McpServerTool]方法就能暴露更多工具比如查历史 K 线、查板块资金流向、查财务指标。模型侧不用改它会自动发现新工具。TaoToken 的统一 Key 通道则让你在换模型时不用重新配鉴权改一个modelId就行。如果你主要做模型对话类的验证想快速试不同模型对工具调用的支持可以从模型对话入口进去试https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 。把 MCP Server 挂上问几个股票代码看模型能不能正确调用工具并组织回答。如果你是要长期做编码 Agent 或者自动化投研流程建议走 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。这类场景调用量大统一 Key 通道在额度管理和多模型切换上省事很多。接入文档在这里https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有各模型的参数说明和示例请求配置时对照着看。控制台在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 可以看用量和额度。API Key 管理在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。最后说个实用技巧MCP Server 的[Description]尽量写英文因为大部分模型对英文工具描述的理解更稳定。行情代码的格式sh/sz前缀一定要在描述里写清楚并给例子否则模型可能传600000这种不带前缀的代码导致查询失败。这个坑我踩过加上例子之后就再没出过问题。
返回列表