1. 从零理解 MCP 服务器它到底是什么、能做什么、适合谁如果你最近在折腾大模型应用大概率听过 MCP 这个词。MCP 全称 Model Context Protocol翻译过来叫“模型上下文协议”说白了就是一套让模型和外部世界对话的标准接口。你可以把它想象成 USB-C以前每个设备都有自己的充电口现在统一成一个标准谁都能插。MCP 干的就是这件事——把工具调用、数据读取、提示词模板这三类能力用统一的协议暴露给模型客户端。那 MCP 服务器又是什么简单讲它是一个独立运行的服务进程对外声明“我有哪些工具可以调、哪些资源可以读、哪些提示词可以用”然后等客户端来连。客户端可能是 Claude Desktop、Cursor、Cline也可能是你自己写的 Agent。服务器不关心谁来连只负责把能力描述清楚、把请求处理正确。这篇文章适合谁如果你已经会用 Node.js 写点脚本听说 MCP 但还没亲手跑通过一个服务器或者你照着官方示例抄了一遍却发现 tools 列表是空的、resources 读不出来、prompts 调用报错那这篇就是写给你的。我会带你从 SDK 初始化开始一步步把 tools、resources、prompts 三类能力都注册上最后用 TaoToken 统一 Key 通道完成本地联调验证。全程可复制不需要你提前理解 JSON-RPC 的细节。我试过直接啃协议文档结果被 method 名和 schema 转换绕晕。后来发现只要抓住一个核心MCP SDK 帮你把协议层全包了你只需要用 registerTool、registerResource、registerPrompt 这三个方法把业务逻辑挂上去。剩下的握手、发现、路由、序列化SDK 自己处理。理解这一点后面就顺了。2. TaoToken 前置准备统一 Key 通道怎么配、为什么需要它在动手写代码之前先把“通道”这件事说清楚。MCP 服务器本身不绑定任何模型厂商它只负责暴露能力。但你要验证这些能力是否真的能被模型调用就需要一个能连上模型的客户端。问题来了不同厂商的 API Key 格式不一样Base URL 不一样模型 ID 也不一样。如果你每换一个客户端就重新配一遍光记这些参数就够头疼。TaoToken 在这里扮演的角色是统一入口。它提供一个兼容 OpenAI 风格的 API 通道你拿一个 Key配一个 Base URL就能在多个客户端里复用。对于 MCP 联调来说这意味着你可以把精力放在服务器逻辑上而不是反复折腾鉴权配置。具体怎么拿 Key访问 https://taotoken.net/api-keys 这个 deep link登录后创建一个 API Key。注意保存好页面关闭后不会再完整显示。然后你需要记下三个核心参数Base URL 填https://taotoken.net/apiAPI Key 填你刚创建的那串Model ID 根据你要用的模型填比如claude-sonnet-4-20250514或gpt-4o这类。这三个参数在后面配置 MCP 客户端时会反复用到。注意Base URL 不要加 UTM 参数直接写https://taotoken.net/api就行。带参数的链接是给网页访问用的API 调用不需要。如果你用的是 Claude Code 这类工具它有自己的配置文件。以 Claude Code 为例配置文件通常在~/.claude/settings.json或项目级的.claude/settings.json。你需要把 Base URL 和 Key 写进去格式类似这样{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的Key } }如果你用的是 Cline 或 Roo Code 这类 VS Code 插件配置入口在插件的设置面板里。选择 “OpenAI Compatible” 作为 ProviderBase URL 填https://taotoken.net/apiAPI Key 填你的 KeyModel ID 填你要用的模型。保存后插件会发一个测试请求通了就说明通道没问题。对于 Codex 用户配置文件在~/.codex/auth.json。你需要把OPENAI_BASE_URL指向https://taotoken.net/apiOPENAI_API_KEY填你的 Key。这个文件是 JSON 格式改完保存即可。提示如果你不确定自己的工具该改哪个文件先去 https://taotoken.net/doc 看接入文档里面按客户端分类列了配置路径和示例。别凭记忆改改错了排查起来很费时间。配好通道之后你的 MCP 服务器就可以专注于“暴露能力”这件事了。客户端通过 TaoToken 通道连上模型模型决定调用哪个工具请求打到你的 MCP 服务器服务器返回结果模型再组织语言回复用户。整条链路里TaoToken 负责的是模型侧的统一接入MCP 服务器负责的是能力侧的标准暴露。两者各司其职你只需要把中间那层业务逻辑写对。3. 可复制配置SDK 初始化与 tools/resources/prompts 三类能力注册现在进入正题。先建项目。我习惯把 MCP 服务器放在独立目录里和前端应用平级这样职责清晰部署也灵活。mkdir mcp-server cd mcp-server npm init -y npm install typescript ts-node-dev zod express modelcontextprotocol/sdk npx tsc --inittsconfig.json用最普通的配置就行target设es2022module设esnextmoduleResolution设nodestrict打开。这些是 TypeScript 项目的常规设置不展开。接下来建两个文件src/gifts.ts放业务逻辑src/server.ts放 MCP 服务器入口。先写业务逻辑它完全不依赖 MCP纯函数方便测试和复用。// src/gifts.ts export type GiftIdea { id: string; title: string; price: number; occasion: string; }; export type SuggestGiftsInput { age: number; relationship: friend | partner | child | coworker; budget: number; }; export function suggestGifts(input: SuggestGiftsInput): GiftIdea[] { return [ { id: book-1, title: 与对方爱好相关的书, price: Math.min(input.budget, 30), occasion: generic, }, { id: game-1, title: 适合聚会的桌游, price: Math.min(input.budget, 50), occasion: party, }, ]; } export const giftCatalog: GiftIdea[] [ { id: book-1, title: 编程类图书, price: 25, occasion: learning }, { id: lego-1, title: LEGO 积木套装, price: 60, occasion: fun }, ];然后是服务器入口。这里的关键是理解 SDK 的注册模式每个能力都对应一个 register 方法传入名称、描述对象、以及处理函数。// src/server.ts import express from express; import { McpServer } from modelcontextprotocol/sdk/server/mcp.js; import { StreamableHTTPServerTransport } from modelcontextprotocol/sdk/server/streamableHttp.js; import { z } from zod; import { suggestGifts, giftCatalog } from ./gifts.js; const app express(); app.use(express.json()); const mcpServer new McpServer({ name: gift-assistant-mcp, version: 0.1.0, }); // 注册工具 const suggestGiftsInputSchema z.object({ age: z.number().int().min(0).max(120), relationship: z.enum([friend, partner, child, coworker]), budget: z.number().min(0), }); mcpServer.registerTool( suggest_gifts, { title: Suggest gift ideas, description: 根据年龄、关系类型与预算推荐礼物创意。, inputSchema: suggestGiftsInputSchema, }, async ({ input }) { const ideas suggestGifts(input); const text ideas .map((g) • ${g.title} — ~${g.price} USD (occasion: ${g.occasion}, id: ${g.id})) .join(\n); return { content: [{ type: text, text }], structuredContent: { ideas }, }; } ); // 注册资源 mcpServer.registerResource( gift_catalog, { title: Gift catalog, description: 用于演示与调试的简单礼物目录。, mimeType: application/json, }, async () { return { contents: [ { uri: mcp://gift-catalog, mimeType: application/json, text: JSON.stringify(giftCatalog, null, 2), }, ], }; } ); // 注册提示词 mcpServer.registerPrompt(birthday_gift, { title: Birthday gift helper, description: 用于挑选生日礼物的请求模板。, messages: [ { role: system, content: 你是一名礼物搜索助手。请提出澄清性问题并提供若干备选方案。, }, { role: user, content: 我需要一份生日礼物。请先询问必要的细节并帮助我选择。, }, ], }); // 接入 HTTP 传输 const transport new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); app.post(/mcp, async (req, res) { await transport.handleRequest(req, res, req.body); }); app.get(/mcp, async (req, res) { await transport.handleRequest(req, res); }); await mcpServer.connect(transport); const PORT process.env.PORT ?? 4000; app.listen(PORT, () { console.log(MCP server listening on http://localhost:${PORT}/mcp); });这段代码里有几个点值得展开。第一inputSchema用的是 Zod 模式SDK 会自动把它转成 JSON Schema 给模型看。你不需要手写 JSON SchemaZod 的类型推导和校验能力直接复用。第二处理函数的返回值里content是模型可见的文本structuredContent是给前端组件消费的结构化数据两者可以同时返回。第三资源注册的返回值必须是contents数组每项包含uri、mimeType、text这是 MCP 资源的标准格式别自己发明字段。package.json里加个脚本{ scripts: { dev: ts-node-dev --esm src/server.ts } }到这里三类能力都注册完了。tools 有suggest_giftsresources 有gift_catalogprompts 有birthday_gift。服务器启动后客户端连接时会自动发现这些能力。4. 验证请求与成功结果用 curl 和 MCP Inspector 确认三类能力可用代码写完了得验证。最直接的方式是先跑起来看控制台有没有报错。npm run dev看到MCP server listening on http://localhost:4000/mcp就说明进程起来了。接下来用 curl 发一个发现请求看看 tools 列表里有没有suggest_gifts。curl -X POST http://localhost:4000/mcp \ -H Content-Type: application/json \ -H Accept: application/json, text/event-stream \ -d {jsonrpc:2.0,id:1,method:tools/list,params:{}}如果返回的 JSON 里能看到suggest_gifts及其 inputSchema说明工具注册成功。同样的方式可以查 resources 和 promptscurl -X POST http://localhost:4000/mcp \ -H Content-Type: application/json \ -H Accept: application/json, text/event-stream \ -d {jsonrpc:2.0,id:2,method:resources/list,params:{}} curl -X POST http://localhost:4000/mcp \ -H Content-Type: application/json \ -H Accept: application/json, text/event-stream \ -d {jsonrpc:2.0,id:3,method:prompts/list,params:{}}curl 适合快速 smoke test但真正调试建议用 MCP Inspector。它是官方提供的可视化调试工具能列出所有能力、手动调用工具、查看原始请求响应。启动方式npx modelcontextprotocol/inspector然后在 Inspector 界面里填你的服务器地址http://localhost:4000/mcp传输类型选 “Streamable HTTP”。连上之后左侧会显示 Tools、Resources、Prompts 三个面板。点开 Tools能看到suggest_gifts点进去填参数比如 age 填 30、relationship 选 friend、budget 填 100点运行。如果返回了礼物列表文本说明工具链路通了。Resources 面板里点gift_catalog应该能看到 JSON 格式的礼物目录。Prompts 面板里点birthday_gift能看到预设的 system 和 user 消息。这三样都正常你的第一个 MCP 服务器就算跑通了。提示Inspector 默认连的是 stdio 传输如果你用 HTTP 传输记得在连接设置里切换。这个坑我踩过连了半天连不上后来发现是传输类型选错了。验证通过后你可以把 MCP 服务器接到实际客户端里。以 Cline 为例在插件设置里添加 MCP Server类型选 “Streamable HTTP”URL 填http://localhost:4000/mcp。保存后 Cline 会自动发现工具你在对话里说“帮我推荐一个给朋友的生日礼物预算 100 以内”模型就会调用suggest_gifts工具。这时候模型侧的请求走的是 TaoToken 通道能力侧的请求走的是你的 MCP 服务器两条链路各走各的互不干扰。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth 报错对照联调过程中最容易卡住的不是业务逻辑而是配置和协议层的报错。我把几个高频错误和排查路径列出来你对照着看。401 Unauthorized。这个通常出现在模型侧不是 MCP 服务器侧。如果你在客户端里调用工具时报 401先检查 TaoToken 的 API Key 有没有填对、有没有过期。Base URL 是不是写成了https://taotoken.net/api注意结尾没有斜杠。如果 Key 没问题还是 401去 https://taotoken.net/api-keys 重新生成一个排除 Key 被误删的情况。local proxy failed。这个报错一般出现在客户端尝试连接 MCP 服务器时。原因可能是服务器没启动、端口被占用、或者 URL 路径写错了。先确认npm run dev还在跑然后curl http://localhost:4000/mcp看有没有响应。如果端口冲突改PORT环境变量换个端口。路径一定要带/mcp别只写http://localhost:4000。reading choices 报错。这个通常和模型返回格式有关。如果你用的模型 ID 不对或者客户端把非 OpenAI 兼容的响应当 OpenAI 格式解析就会在读取choices字段时失败。检查 Model ID 是不是 TaoToken 支持的模型比如claude-sonnet-4-20250514或gpt-4o。别填一个不存在的模型名那样请求会直接失败。OAuth 相关报错。MCP 规范里定义了鉴权流程但本地开发阶段你大概率不需要 OAuth。如果你在客户端里看到 OAuth 相关的错误先确认是不是误开了鉴权选项。本地联调时MCP 服务器不加鉴权是正常的客户端也不应该强制走 OAuth。把客户端的鉴权开关关掉或者选 “None” 作为认证方式。tools 列表为空。服务器启动了但客户端发现不到工具。先确认registerTool的调用在mcpServer.connect(transport)之前。如果顺序反了连接建立时工具还没注册客户端自然看不到。另外检查一下inputSchema有没有语法错误Zod 模式写错了会导致注册失败但控制台不一定报错。resources 读取返回空。检查contents数组的格式。每项必须有uri、mimeType、text三个字段缺一个都可能导致客户端解析失败。uri用mcp://开头是惯例但具体格式客户端可能不校验关键是字段齐全。prompts 调用没反应。确认messages数组里的role和content字段拼写正确。role只能是system、user、assistant这几个值写错了客户端会忽略。另外 prompts 是客户端主动获取的不是模型自动调用的你需要在客户端界面里手动选择或触发。排查的时候记住一个原则先隔离再联调。MCP 服务器的问题用 Inspector 查模型通道的问题用 curl 直接打 TaoToken API 查。两边都单独通了再合起来跑。这样出问题时你能快速定位是哪一侧的锅。6. 语义一致 CTA把统一 Key 通道用起来继续深入 MCP 开发跑通第一个 MCP 服务器之后你手里已经有了一个可扩展的骨架。tools、resources、prompts 三类能力的注册模式你都摸过了接下来无非是往里填更多业务逻辑。但要让这个服务器真正用起来模型侧的通道得配好。如果你还没配 TaoToken 的 Key现在是个好时机。访问 https://taotoken.net/api-keys 创建一个然后在你的客户端里把 Base URL 和 Key 填上。具体每个客户端怎么填https://taotoken.net/doc 里有分门别类的说明照着改就行。配好之后你可以把 MCP 服务器接到 Cline、Claude Code 或者自己写的 Agent 里让模型真正调用你注册的工具。如果只是想先试试模型对话效果https://taotoken.net/chat 可以直接体验。如果你打算长期做编码类 Agent 开发https://taotoken.net/coding-plan 里有更完整的方案说明。MCP 的生态还在快速演进SDK 的类名和方法签名可能会变但核心模式不会变注册能力、处理请求、返回标准格式。把这篇文章里的代码跑通你就有了一个能继续迭代的起点。后面加鉴权、加日志、加流式响应都是在这个骨架上做增量。遇到报错先回来看第 5 节大部分坑我都替你踩过了。
阅读完成 · 觉得有帮助?