MCP基础入门:从JSON-RPC到stdio与Streamable HTTP的TaoToken实践
1. 从一次工具调用失败说起MCP 到底解决什么问题如果你刚开始接触 MCP模型上下文协议Model Context Protocol大概率会遇到这样一个场景模型明明知道该调用某个工具但客户端和服务端之间就是接不上日志里只有一行local proxy failed或者reading choices之类的报错。我第一次搭 MCP 链路时卡了整整一个下午最后发现是 stdio 模式下服务端往 stdout 打了一行调试日志把 JSON-RPC 的消息流污染了。MCP 本质上是一个开源标准用来把 AI 应用和外部系统连起来。它让模型能访问数据源本地文件、数据库、调用工具搜索、计算器、复用工作流专业提示词。你可以把它理解成「AI 世界的 USB-C 接口」——不管对面是文件系统还是天气 API只要按 MCP 的协议说话客户端就能统一调用。对初次接触的开发者来说MCP 有两个必须搞懂的基础协议消息协议 JSON-RPC和传输协议 stdio / Streamable HTTP。前者决定「说什么」后者决定「怎么送」。这篇就围绕这两层给出可复制的 JSON-RPC 请求示例、两种接入配置并用 TaoToken 的统一 Key/API 通道跑通一次完整的工具调用与结果验证。适合谁适合已经会写 Python、想快速跑通最小可用 MCP 链路的开发者。2. TaoToken 前置准备统一 Key 与 API 通道怎么配在动手写 MCP 客户端之前先把模型侧的通道准备好。MCP 负责的是「工具调用」这一层但真正决定要不要调用工具、怎么解析结果的还是背后的语言模型。所以你需要一个能稳定访问模型的 API 通道TaoToken 在这里扮演的就是统一入口的角色。先到官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册账号然后在控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 里创建一个 API Key。这个 Key 就是你后面所有请求的凭证格式通常是一串以sk-开头的字符串。创建完记得复制保存页面刷新后就看不到了。接下来是 Base URL。TaoToken 的 API 入口是 https://taotoken.net/api注意这个地址不带任何查询参数直接作为 OpenAI 兼容接口的 base_url 使用。也就是说如果你用的是 openai 这个 Python SDK配置方式是这样的from openai import OpenAI client OpenAI( api_keysk-你的TaoToken密钥, base_urlhttps://taotoken.net/api ) resp client.chat.completions.create( modelclaude-sonnet-4-5, messages[{role: user, content: 你好}] ) print(resp.choices[0].message.content)这里有个容易踩的坑base_url 末尾不要加/v1。TaoToken 的 API 路径已经做了兼容处理如果你手动拼成https://taotoken.net/api/v1请求会 404。我实测下来直接用https://taotoken.net/api就能正常返回。模型 ID 方面TaoToken 支持多种主流模型你在控制台的模型列表里能看到当前可用的 ID。写配置的时候Model ID 要和列表里完全一致大小写敏感。比如claude-sonnet-4-5和Claude-Sonnet-4-5在某些客户端里会被当成两个不同的模型。如果你打算长期做编码类或 Agent 类任务可以顺手看一下 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite它针对高频调用场景做了额度优化。不过对于这篇的最小链路验证普通 API Key 就够了。准备好这三样东西——Base URL、API Key、Model ID——后面的 MCP 客户端配置就能直接填进去。这也是很多 MCP 客户端比如 Cline、Claude Code要求的「三件套」缺一个都跑不起来。3. 可复制配置JSON-RPC 请求与 stdio / Streamable HTTP 接入这一节是全文的核心我会把 JSON-RPC 的请求格式、stdio 和 Streamable HTTP 两种传输方式的配置都写成可以直接复制的片段。你不需要理解每一行的含义先跑通再回头细看。3.1 JSON-RPC 消息格式MCP 的「普通话」MCP 把所有能力都映射成 JSON-RPC 的 method。不管你是调用工具、读资源还是取提示词消息结构都是固定的三段式jsonrpc、id、method加上可选的params。先看工具调用的请求。假设你的 MCP Server 里定义了一个get_weather工具{ jsonrpc: 2.0, id: 101, method: tools/call, params: { name: get_weather, arguments: { city: 北京 } } }服务端处理完后返回{ jsonrpc: 2.0, id: 101, result: { content: [ { type: text, text: 北京晴25℃ } ] } }注意id是用来做请求-响应配对的同一个连接里可以并发多个请求靠 id 区分。method是 MCP 固定的几个值tools/call、resources/read、prompts/get以及初始化必调的initialize。初始化请求长这样{ jsonrpc: 2.0, id: 1, method: initialize, params: { protocolVersion: 2024-11-05, capabilities: {}, clientInfo: { name: my-mcp-client, version: 1.0.0 } } }protocolVersion目前常用的是2024-11-05如果你用的是较新的客户端可能会看到2025-03-26。版本不匹配时服务端会返回错误这时候要么升级客户端要么在配置里显式指定版本。错误码是通用的记住几个关键的就行-32700是 JSON 解析失败-32601是方法不存在-32602是参数错误。遇到-32601基本就是 method 拼错了或者服务端没注册这个能力。3.2 stdio 接入配置本地进程间通信stdio 是最简单的传输方式客户端以子进程形式启动 MCP Server双方通过 stdin/stdout 交换 JSON-RPC 消息每条消息以换行符分隔。配置片段以常见的 MCP 客户端 settings 为例{ mcpServers: { my-local-server: { command: python, args: [-m, my_mcp_server], env: { TAOTOKEN_API_KEY: sk-你的TaoToken密钥, TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_MODEL: claude-sonnet-4-5 } } } }这里command和args决定了怎么启动你的 Server 进程。如果你用的是 Node.js 写的 Server就换成npx加包名。env里把 TaoToken 的三件套传进去Server 内部调用模型时就能直接读环境变量。stdio 模式最大的坑就是日志污染。你的 Server 里任何print()或console.log()都会写到 stdout而 stdout 是协议通道混入非 JSON 内容后客户端解析就会失败。正确做法是把日志写到 stderrimport sys def log(msg): print(msg, filesys.stderr) log(server starting...)这样客户端能正常读取 JSON-RPC日志也能在客户端的 stderr 面板里看到。3.3 Streamable HTTP 接入配置远程与高并发场景Streamable HTTP 是 2025 年 3 月引入的传输方式用来替代早期的 SSE。它的核心特点是统一端点、支持无状态模式、支持连接恢复。配置片段{ mcpServers: { my-remote-server: { url: https://your-mcp-server.example.com/message, headers: { Authorization: Bearer sk-你的TaoToken密钥 }, transport: streamable-http } } }客户端通过 HTTP POST 往/message端点发请求服务端可以选择直接返回 JSON也可以把响应升级成 SSE 流。对于工具调用这种短请求直接返回 JSON 就够了对于需要流式输出的场景服务端会返回Content-Type: text/event-stream。Streamable HTTP 相比 stdio 的优势在于不需要本地起进程适合部署在云上支持多客户端并发连接断了可以恢复不用从头开始。如果你的 MCP Server 要给别人用优先选这个。有一点要注意Streamable HTTP 的端点路径不一定是/message具体看你的 Server 实现。有些框架默认是/mcp配置前先确认一下服务端的路由。4. 验证请求跑通一次完整的工具调用配置写完了接下来验证链路是否真的通了。我建议按「先直连模型、再走 MCP」的顺序排查这样出问题时能快速定位是哪一层的问题。第一步先用 curl 直接测 TaoToken 的 API 通道curl https://taotoken.net/api/chat/completions \ -H Authorization: Bearer sk-你的TaoToken密钥 \ -H Content-Type: application/json \ -d { model: claude-sonnet-4-5, messages: [{role: user, content: 回复OK两个字}] }如果返回的 JSON 里有choices字段说明模型通道没问题。如果返回 401检查 Key 是否复制完整如果返回 404检查 base_url 是不是多加了/v1。第二步启动你的 MCP Server用 stdio 模式手动发一条 initialize 请求。假设 Server 是 Python 写的可以这样测echo {jsonrpc:2.0,id:1,method:initialize,params:{protocolVersion:2024-11-05,capabilities:{}}} | python -m my_mcp_server正常情况下你会看到一行 JSON 响应包含result和serverInfo。如果没有任何输出说明 Server 启动就失败了先单独运行python -m my_mcp_server看报错。第三步发一条tools/list请求看看 Server 注册了哪些工具{ jsonrpc: 2.0, id: 2, method: tools/list, params: {} }响应里会列出所有工具的 name、description 和 inputSchema。确认你要调用的工具在列表里并且参数名和 schema 一致。第四步真正调用一次工具{ jsonrpc: 2.0, id: 3, method: tools/call, params: { name: get_weather, arguments: { city: 北京 } } }如果返回的result.content里有你期望的文本恭喜最小链路跑通了。这时候再回到 MCP 客户端里让模型自己决定调用哪个工具观察完整的「模型输出 JSON-RPC → 客户端转发 → Server 执行 → 结果回传 → 模型总结」流程。我在验证阶段遇到过一次「模型不调用工具」的情况排查后发现是 system prompt 里没有把工具列表描述清楚。MCP 客户端一般会自动注入工具描述但如果你自己写 prompt记得把tools的 name 和 description 带上模型才知道有哪些工具可用。5. 常见报错排查401、local proxy failed、reading choices这一节把几个高频报错单独拎出来对照真实日志给出排查路径。401 Unauthorized最常见的原因是 Key 没传对。检查三个地方Key 是否复制完整有没有漏掉尾部字符、请求头是不是Authorization: Bearer sk-xxx、环境变量有没有被覆盖。如果你在 MCP 配置里同时写了env和系统环境变量客户端可能会优先读系统变量导致配置里的 Key 不生效。local proxy failed这个报错通常出现在 stdio 模式下客户端启动子进程失败。原因可能是command路径不对、Python 环境没装依赖、或者 Server 启动时抛异常直接退出了。排查方法把command和args拼成一条命令在终端里手动跑一遍看具体报什么错。如果是ModuleNotFoundError就在对应的 Python 环境里pip install缺失的包。reading choices 相关报错这个一般出现在模型响应解析阶段说明返回的 JSON 结构不符合预期。可能的原因有模型返回了非 JSON 内容比如纯文本、响应被截断、或者你用的 SDK 版本和 API 返回格式不匹配。先用 curl 直接请求一次看原始返回长什么样。如果choices字段存在但内容为空检查max_tokens是不是设得太小。OAuth 相关报错部分远程 MCP Server 要求 OAuth 认证如果你只传了 API Key会返回 401 或 403。这种情况下需要在客户端配置里加上 OAuth 的 token 获取流程或者改用支持 API Key 认证的 Server。连接超时Streamable HTTP 模式下如果 Server 部署在防火墙后面客户端可能连不上。先用curl -v测一下端点是否可达再看是否需要配置代理或白名单。注意这里说的代理是指企业网络里的正向代理不是那种违规工具配置前先确认公司网络策略。排查的核心思路是分层定位先确认模型通道通不通再确认 MCP Server 能不能独立启动最后确认客户端和服务端的协议版本是否匹配。大部分问题都出在配置层而不是协议本身。6. 继续深入从最小链路到生产可用跑通最小链路之后你可能会想把它用到实际项目里。这时候有几个方向可以继续深入。第一是工具设计的粒度。MCP 的 Tools 应该是「原子操作」一个工具只做一件事。比如query_sale只负责查销售额不要在里面顺便做数据清洗。粒度太粗会导致模型难以组合调用粒度太细又会增加调用次数。我的经验是一个工具的参数控制在 3 到 5 个以内description 写清楚「什么时候该用」。第二是 Resources 和 Prompts 的配合。Resources 提供只读上下文适合放配置、文档、数据库记录Prompts 是可复用的指令模板适合把常用的 system prompt 固化下来。三者结合使用能让模型在调用工具前先拿到足够的背景信息减少无效调用。第三是传输方式的选择。本地开发用 stdio简单直接生产环境用 Streamable HTTP支持并发和恢复。如果你的 Server 要同时服务多个客户端Streamable HTTP 的无状态模式能省不少资源。第四是密钥管理。不要把 API Key 硬编码在代码里用环境变量或密钥管理服务。TaoToken 的控制台 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 里可以创建多个 Key给不同环境用不同的 Key方便轮换和审计。如果你在接入过程中遇到协议层面的问题可以翻一下接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite里面有完整的 API 说明和示例。想先直观感受一下模型对话的效果可以直接在模型对话 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 页面里试几条 prompt确认模型行为符合预期后再写进 MCP 的 system prompt。最后提醒一句MCP 的协议版本还在演进2024-11-05 和 2025-03-26 之间有一些字段差异。写配置时如果遇到「未知字段」的报错先检查协议版本再对照官方 schema 调整。最小链路跑通只是起点真正的价值在于把工具、资源、提示词组合成稳定的工作流。