1. 从 stdio 到 Streamable HTTP:MCP 三种通信方式到底怎么选
MCP(Model Context Protocol)是让大模型调用外部工具的协议层,它本身不关心你用什么模型,只关心「消息怎么传」。目前主流实现里有三种传输方式:stdio、SSE、Streamable HTTP。很多人第一次接触 MCP 时会被这三个词绕晕,其实它们解决的是同一个问题——JSON-RPC 消息怎么在客户端和服务端之间跑起来。
stdio 是本地进程管道,客户端把服务端当子进程启动,通过标准输入输出收发换行分隔的 JSON-RPC 消息。它适合本地工具调用,比如读写文件、执行命令、查本地数据库。SSE 是 HTTP 长连接单向推送,服务端通过/sse建连、客户端通过/message发请求,属于早期远程方案,现在官方推荐迁移到 Streamable HTTP。Streamable HTTP 把请求和响应统一到一个端点(通常是/mcp),客户端 POST 发请求,服务端可以一次性返回,也可以升级成 SSE 流式返回,无状态、可水平扩展、支持断点续传。
这篇文章面向三类人:刚写完第一个 MCP Server 想跑通联调的开发者、准备把本地 stdio 服务迁移到远程的团队、以及需要统一管理模型 Key 和 API 通道的工程同学。我会用 FastMCP 写服务端、用 LangChain MCP Adapter 写客户端,把三种方式的配置片段、验证动作、报错排查都过一遍。模型调用侧统一走 TaoToken 的 Key/API 通道,这样你不用在多个厂商之间来回切 Key,一个通道就能完成端到端联调。
先说结论:本地开发优先 stdio,远程共享优先 Streamable HTTP,SSE 只作为存量兼容。下面按「先跑通 stdio,再迁移到 Streamable HTTP」的顺序展开,每一步都给可复制的代码和验证命令。
2. TaoToken 统一 Key 通道前置准备
在写 MCP 服务之前,先把模型调用侧的通道准备好。MCP 本身只负责工具调用,但你的 Agent 最终要调模型来决策「什么时候调哪个工具」,所以模型 API 的 Base URL 和 Key 必须配好。TaoToken 提供统一的 Key/API 通道,官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api 。
你需要做三件事:注册账号、创建 API Key、确认要用的 Model ID。API Key 在控制台的 API Keys 页面创建,创建后只显示一次,复制保存好。Model ID 根据你实际要调用的模型填,比如claude-sonnet-4-20250514这类标识,具体以控制台模型列表为准。
配置方式有两种。第一种是环境变量,适合本地脚本和命令行工具:
export TAOTOKEN_API_KEY="sk-你的Key" export TAOTOKEN_BASE_URL="https://taotoken.net/api"第二种是写进配置文件,适合 Claude Code、Cline、Codex 这类工具。以 Claude Code 的 settings 为例,路径通常在~/.claude/settings.json:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-你的Key", "ANTHROPIC_MODEL": "claude-sonnet-4-20250514" } }如果你用的是 Codex,配置写在~/.codex/auth.json和~/.codex/config.toml里。auth.json 放 Key:
{ "OPENAI_API_KEY": "sk-你的Key" }config.toml 放 Base URL 和 Model ID:
model = "gpt-4.1" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" wire_api = "chat"Cline 或 Roo Code 这类 VS Code 插件,在设置里选「OpenAI Compatible」,Base URL 填https://taotoken.net/api,API Key 填你的 Key,Model ID 填控制台里的模型标识。Cline 如果走 MCP,还需要在 MCP 配置里单独写服务端启动命令,这个后面会讲。
这里有个容易踩的坑:Base URL 末尾不要多加/v1或/chat/completions,不同工具对路径拼接规则不一样,填根地址https://taotoken.net/api最稳。如果工具要求完整路径,再按它的文档补。Key 泄露了就在控制台删掉重建,不要复用。
前置准备做完后,用一条 curl 验证通道是否通:
curl -s https://taotoken.net/api/v1/models \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" | head -c 500能返回模型列表就说明 Key 和 Base URL 没问题。这一步过了再往下写 MCP 服务,否则后面报错你分不清是 MCP 的问题还是 Key 的问题。
3. stdio 服务端与客户端可复制配置
stdio 是三种方式里最容易跑通的。服务端就是一个普通 Python 进程,通过标准输入输出收发 JSON-RPC 消息,每条消息以换行符分隔。客户端负责启动这个子进程,然后把请求写进它的 stdin,从 stdout 读响应。
先装依赖:
pip install fastmcp langchain-mcp-adapters langchain-openai写服务端math_stdio_server.py:
from fastmcp import FastMCP mcp = FastMCP(name="math_stdio_server") @mcp.tool(name="add_numbers", description="计算两个整数的和") def add_numbers(a: int, b: int) -> int: return a + b @mcp.tool(name="multiply_numbers", description="计算两个整数的积") def multiply_numbers(a: int, b: int) -> int: return a * b if __name__ == "__main__": mcp.run(transport="stdio")直接运行python math_stdio_server.py,进程会挂起等待 stdin 输入,这是正常的。你可以手动喂一条 JSON-RPC 初始化消息测试:
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | python math_stdio_server.py能看到返回的 JSON 就说明服务端没问题。注意 stdio 模式下不要往 stdout 打日志,日志会污染 JSON-RPC 消息流,要打就打到 stderr。
客户端用 LangChain MCP Adapter,写math_stdio_client.py:
import asyncio from langchain_mcp_adapters.client import MultiServerMCPClient from langchain_openai import ChatOpenAI from langgraph.prebuilt import create_react_agent async def main(): client = MultiServerMCPClient({ "math": { "command": "python", "args": ["math_stdio_server.py"], "transport": "stdio" } }) tools = await client.get_tools() print("已加载工具:", [t.name for t in tools]) llm = ChatOpenAI( model="claude-sonnet-4-20250514", base_url="https://taotoken.net/api", api_key="sk-你的Key" ) agent = create_react_agent(llm, tools) result = await agent.ainvoke({"messages": [("user", "帮我算 12 加 30 等于多少")]}) print(result["messages"][-1].content) if __name__ == "__main__": asyncio.run(main())运行python math_stdio_client.py,如果输出「已加载工具: ['add_numbers', 'multiply_numbers']」并且模型返回了 42,说明 stdio 链路完全通了。这里模型调用走的是 TaoToken 的 Base URL,工具调用走的是本地子进程,两条链路互不干扰。
stdio 的配置要点就三个:command是启动命令,args是参数列表,transport固定stdio。如果你用 Cline 或 Claude Code 的 MCP 配置,格式类似:
{ "mcpServers": { "math": { "command": "python", "args": ["/绝对路径/math_stdio_server.py"] } } }注意路径要用绝对路径,相对路径在不同工作目录下会找不到文件。这是 stdio 最常见的报错来源。
4. 迁移到 Streamable HTTP 与连通性验证
stdio 跑通后,迁移到 Streamable HTTP 主要是改服务端的transport参数和客户端的连接方式。工具注册逻辑完全不用动,这是 MCP 协议设计得好的地方——传输层和业务层解耦。
服务端math_http_server.py:
from fastmcp import FastMCP mcp = FastMCP(name="math_streamable_http_server") @mcp.tool(name="add_numbers", description="计算两个整数的和") def add_numbers(a: int, b: int) -> int: return a + b @mcp.tool(name="multiply_numbers", description="计算两个整数的积") def multiply_numbers(a: int, b: int) -> int: return a * b if __name__ == "__main__": mcp.run(transport="streamable-http", host="127.0.0.1", port=8000)启动后服务端监听http://127.0.0.1:8000/mcp。注意端点是/mcp,不是/sse也不是/message,这是 Streamable HTTP 的统一端点设计。先用 curl 验证连通性:
curl -s -X POST http://127.0.0.1:8000/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'如果返回 JSON 或 SSE 流,说明服务端正常。这里Accept头必须同时包含application/json和text/event-stream,只写一个可能被服务端拒绝。
客户端math_http_client.py:
import asyncio from langchain_mcp_adapters.client import MultiServerMCPClient from langchain_openai import ChatOpenAI from langgraph.prebuilt import create_react_agent async def main(): client = MultiServerMCPClient({ "math": { "url": "http://127.0.0.1:8000/mcp", "transport": "streamable_http" } }) tools = await client.get_tools() print("已加载工具:", [t.name for t in tools]) llm = ChatOpenAI( model="claude-sonnet-4-20250514", base_url="https://taotoken.net/api", api_key="sk-你的Key" ) agent = create_react_agent(llm, tools) result = await agent.ainvoke({"messages": [("user", "帮我算 7 乘 8 等于多少")]}) print(result["messages"][-1].content) if __name__ == "__main__": asyncio.run(main())运行后应该输出工具列表和 56。Streamable HTTP 的配置要点:url指向/mcp端点,transport填streamable_http(注意是下划线,不是连字符)。如果你要连远程服务,把127.0.0.1换成实际域名即可,但要注意 HTTPS 和鉴权头。
三种方式的对照表:
| 维度 | stdio | SSE | Streamable HTTP |
|---|---|---|---|
| 传输层 | 进程管道 | HTTP 长连接 | HTTP 请求/流式响应 |
| 端点 | 无 | /sse+/message | /mcp统一端点 |
| 状态 | 有状态 | 有状态 | 无状态 |
| 水平扩展 | 不支持 | 困难 | 天然支持 |
| 断点续传 | 无 | 有限 | 内置 |
| 适用场景 | 本地工具 | 存量兼容 | 远程共享 |
迁移时最容易忽略的是鉴权。stdio 靠进程隔离,Streamable HTTP 暴露在网络上必须加鉴权。可以在反向代理层加 Bearer Token,或者在 FastMCP 里加中间件校验。生产环境不要裸奔。
5. 常见报错排查清单
这一节按真实报错来。你跑上面代码时大概率会遇到下面几个。
401 Unauthorized。模型调用返回 401,说明 TaoToken 的 Key 不对或没带上。检查api_key是否填了完整 Key,base_url是否是https://taotoken.net/api。如果 Key 是从控制台复制的,注意别把前后空格带进去。MCP 服务端本身的 401 则是另一回事,通常是反向代理的鉴权头没配。
local proxy failed / connection refused。客户端连不上 Streamable HTTP 服务端。先确认服务端进程在跑,curl http://127.0.0.1:8000/mcp有没有响应。如果服务端在容器里,127.0.0.1要换成容器 IP 或宿主机地址。端口被占用也会报这个,换port=8001试试。
reading 'choices' 报错。这是模型响应格式不对,通常发生在 Base URL 配错、返回了非 OpenAI 兼容格式时。检查base_url是不是填成了https://taotoken.net/api/v1导致路径重复,或者 Model ID 填错导致服务端返回错误结构。把base_url改回https://taotoken.net/api再试。
OAuth 相关报错。Claude Code 或某些工具会尝试 OAuth 流程,如果你用的是 API Key 模式,需要在配置里显式关闭 OAuth 或指定ANTHROPIC_API_KEY。检查 settings.json 里有没有ANTHROPIC_API_KEY,没有就补上。
stdio 服务端无响应。最常见的原因是 stdout 被日志污染。检查代码里有没有print()打到 stdout,有就改成sys.stderr.write()。另一个原因是路径问题,args里的脚本路径要用绝对路径。
工具列表为空。客户端连上了但get_tools()返回空。检查服务端@mcp.tool装饰器有没有漏,函数签名类型注解是否完整。FastMCP 靠类型注解生成 JSON Schema,缺注解会导致工具注册失败。
SSE 迁移到 Streamable HTTP 后连不上。端点从/sse变成了/mcp,transport从sse变成了streamable_http。两个都要改,只改一个会报 404 或协议不匹配。
排查顺序建议:先 curl 服务端确认活着,再 curl 模型 API 确认 Key 有效,最后跑客户端。这样能把问题定位到具体环节,不用瞎猜。
6. 端到端联调与后续接入
把上面几步串起来,完整链路是这样的:客户端启动 → 通过 stdio 或 Streamable HTTP 连 MCP 服务端 → 拉取工具列表 → 用户提问 → 模型(走 TaoToken 通道)决策调用哪个工具 → 客户端通过 MCP 传输层发 JSON-RPC 请求 → 服务端执行工具返回结果 → 模型生成最终回答。
本地开发用 stdio,改代码不用重启服务端,调试最快。需要多人共享或部署到服务器时,切到 Streamable HTTP,把host改成0.0.0.0,前面挂 Nginx 做 TLS 和鉴权。SSE 只在对接老系统时用,新项目不建议。
模型侧统一用 TaoToken 的 Key 通道,好处是你不用为每个模型厂商单独配 Key,Base URL 和 Key 一套就够。API 地址是 https://taotoken.net/api ,Key 在控制台创建。如果你要长期跑编码 Agent 或复杂工作流,可以看下 Coding Plan 的额度方案;只是验证模型连通性的话,用模型对话页面直接测就行。
接入文档里有各工具的详细配置示例,包括 Claude Code、Cline、Codex 的完整 settings 片段。遇到配置问题先翻文档,大部分报错都有对应说明。MCP 服务端本身的调试,建议先用 curl 把 initialize 和 tools/list 两个请求跑通,再上客户端,这样能排除掉一大半传输层问题。