用 Node.js 写一个获取实时股票价格的 MCP Server 示例:TaoToken 统一 Key 接入与配置骨架 1. 为什么要在本地跑一个股票行情 MCP ServerMCP Server 这个词最近出现频率很高但很多人第一次接触会有点懵它到底解决什么问题简单说MCPModel Context Protocol是一套让大语言模型调用外部工具的协议你可以把它理解成「给 AI 装插件的标准接口」。模型本身不知道今天的股价但通过 MCP Server 暴露一个get-quote工具模型就能在对话里直接查行情。这篇要做的是用 Node.js 从零写一个能获取实时股票价格的 MCP Server然后把它接到 TaoToken 的统一 Key/API 通道上让本地 AI 工具比如 Claude Code、Cursor 这类支持 MCP 的客户端真正调用一次行情数据。适合谁看会一点 JavaScript、想让 AI 助手能查股票、但不想折腾一堆鉴权和网络配置的人。我试过把行情查询塞进普通脚本里问题是每次都要手动跑换成 MCP Server 之后模型自己决定什么时候调、调哪只股票体验完全不一样。下面从环境准备开始一步步跑通。2. TaoToken 前置准备统一 Key 与接入地址在写代码之前先把「通道」准备好。TaoToken 的作用是提供一个统一的 API 入口和 Key你不需要为每个模型或工具单独配一套鉴权。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 这个不加 UTM。你需要做两件事第一拿到 API Key。登录后进控制台在 API Keys 页面创建一个新 Key复制保存。这个 Key 后面会写进 MCP 客户端的配置里用来让模型侧请求走 TaoToken 通道。第二确认你要用的模型对话入口。如果你只是想验证 MCP Server 能不能被调用用模型对话页面就够了如果你打算长期在编码工具里用建议看 Coding Plan它更适合 Agent 场景。注意Key 只创建一次就够不要把它硬编码进要提交到 Git 的源码里。用环境变量或客户端配置文件承载。这里有个容易踩的坑很多人以为 MCP Server 自己要去请求 TaoToken其实不是。MCP Server 只负责「查行情」这一件事它请求的是行情数据源TaoToken 的 Key 是配在 MCP 客户端也就是调用方那一侧的用来让模型能正常对话并触发工具调用。两者职责分开别混在一起。3. 可复制配置项目骨架与 MCP Server 代码先建项目。Node.js 版本建议 18 以上因为要用到内置的fetch。mkdir stock-mcp-server cd stock-mcp-server npm init -y npm install modelcontextprotocol/sdk zod然后在package.json里加上type: module因为下面用的是 ESM 语法。接着创建server.js核心逻辑分三块请求行情、解析数据、注册工具。#!/usr/bin/env node import { McpServer } from modelcontextprotocol/sdk/server/mcp.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; import { z } from zod; const SINA_API_BASE https://hq.sinajs.cn/list; const server new McpServer({ name: stock, version: 1.0.0, capabilities: { resources: {}, tools: {} }, }); async function makeStockRequest(symbol) { const url ${SINA_API_BASE}${symbol}; const headers { Referer: https://finance.sina.com.cn }; try { const response await fetch(url, { headers }); if (!response.ok) throw new Error(HTTP error! status: ${response.status}); const buffer await response.arrayBuffer(); const decoder new TextDecoder(gb2312); const text decoder.decode(buffer); return parseSinaStockData(text, symbol); } catch (error) { console.error(Error making stock request:, error); return null; } } function parseSinaStockData(text, symbol) { const matches text.match(/(.*)/); if (!matches || !matches[1]) return null; const values matches[1].split(,); if (values.length 32) return null; return { 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], }; } function formatQuote(quote) { return [ Stock Code: ${quote.symbol}, Stock Name: ${quote.name}, Current Price: ¥${quote.price}, Change Rate: ${(((Number(quote.price) - Number(quote.close)) / Number(quote.close)) * 100).toFixed(2)}%, Open Price: ¥${quote.open}, High Price: ¥${quote.high}, Low Price: ¥${quote.low}, Volume: ${(Number(quote.volume) / 100).toFixed(0)} lots, Turnover: ¥${(Number(quote.amount) / 10000).toFixed(2)} million, Update Time: ${quote.date} ${quote.time}, ].join(\n); } server.tool( get-quote, Get real-time stock quote, { symbol: z.string().describe(Stock symbol (e.g.: sh600000, sz000001)) }, async ({ symbol }) { const quoteData await makeStockRequest(symbol); if (!quoteData) { return { content: [{ type: text, text: Failed to retrieve stock data for ${symbol} }] }; } return { content: [{ type: text, text: formatQuote(quoteData) }] }; } ); async function main() { const transport new StdioServerTransport(); await server.connect(transport); console.error(Stock MCP Server running on stdio); } main().catch((error) { console.error(Fatal error in main():, error); process.exit(1); });几个关键点解释一下。StdioServerTransport表示这个 Server 通过标准输入输出和客户端通信这是本地 MCP 最常见的模式。server.tool注册了一个名为get-quote的工具参数用 zod 定义模型看到描述后就知道该传什么。行情源返回的是 GB2312 编码所以要用TextDecoder(gb2312)解码直接当 UTF-8 读会乱码——这是实测下来最容易忽略的一步。4. 接入配置config.toml 与 settings.json 骨架代码写完了接下来是让客户端认识它。不同客户端配置格式不一样这里给两个常见骨架。Claude Code 用的是config.toml在项目或用户目录下[mcp_servers.stock] command node args [/absolute/path/to/stock-mcp-server/server.js]Cursor 这类用settings.json{ mcpServers: { stock: { command: node, args: [/absolute/path/to/stock-mcp-server/server.js] } } }路径一定要写绝对路径相对路径在客户端启动时工作目录不确定会找不到文件。如果你希望模型侧走 TaoToken 通道把 API Key 配在客户端的模型设置里而不是这个 MCP 配置块里。两者是并列关系一个管「模型怎么连」一个管「工具怎么跑」。启动命令很简单node /absolute/path/to/stock-mcp-server/server.js正常的话终端不会有输出因为 stdio 被占用了只有出错才会打印到 stderr。看到Stock MCP Server running on stdio说明启动成功。5. 验证请求跑通一次真实行情查询配置好之后重启客户端在对话里直接问「帮我查一下 sh600000 的实时价格」。模型应该会触发get-quote工具返回类似这样的结果Stock Code: sh600000 Stock Name: 浦发银行 Current Price: ¥10.25 Change Rate: 0.59% Open Price: ¥10.20 High Price: ¥10.31 Low Price: ¥10.18 Volume: 123456 lots Turnover: ¥12345.67 million Update Time: 2025-01-15 14:30:00如果模型没有调用工具先确认客户端是否识别到了 MCP Server一般在设置里能看到已连接的工具列表。如果工具被调用了但返回Failed to retrieve stock data多半是 symbol 格式不对——A 股要带sh或sz前缀比如sh600000、sz000001。想单独测试 Server 本身可以用 MCP Inspectornpx modelcontextprotocol/inspector node /absolute/path/to/stock-mcp-server/server.js它会打开一个网页界面你可以手动调用get-quote并传参看到原始返回。这一步能把「Server 问题」和「客户端配置问题」分开排障效率高很多。6. 本篇常见错排查报错Cannot find module modelcontextprotocol/sdk没装依赖或者客户端启动时工作目录不对。回到项目目录跑npm install配置里用绝对路径。行情返回乱码忘了用TextDecoder(gb2312)。行情源是 GB2312 编码用默认 UTF-8 解码中文名会变成问号。HTTP error! status: 403请求头缺Referer。行情接口对来源有校验加上Referer: https://finance.sina.com.cn就好。模型不调用工具检查客户端是否加载了 MCP 配置以及工具描述是否清晰。描述写「Get real-time stock quote」比写「stock」更容易被模型正确触发。Key 相关报错如果你在客户端里配了 TaoToken 的 Key 但对话报鉴权失败去控制台确认 Key 是否有效、是否复制完整。接入文档里有各客户端的详细配置说明对照检查一遍通常能定位。排障时优先看 API Keys 和接入文档这两处大部分问题都在配置层。验证模型是否正常响应可以用模型对话页面单独测如果你是要长期在编码工具里跑 AgentCoding Plan 的额度模型更适合持续调用。把上面这套跑通之后你其实已经掌握了 MCP Server 的通用套路注册工具、处理请求、返回结构化文本。换成天气、汇率、数据库查询结构几乎一样改的是makeStockRequest里的数据源和解析逻辑。真正花时间的往往不是代码而是编码、请求头、路径这些细节——踩过一次就记住了。