从一段跑不通的 MCP 客户端代码说起
如果你最近在折腾 MCP(Model Context Protocol),大概率见过下面这段代码:ChatOpenAI(base_url="https:// /v1", api_key=" "),两个参数都是占位符,后面接着 load_mcp_tools、create_react_agent,最后 agent.ainvoke({"messages": "what's (3 + 5) x 12?"})。代码逻辑没问题,工具也定义好了,add 和 multiply 都挂在 FastMCP 上,但一运行就卡住——模型通道根本没通,Agent 拿不到 Token,自然调不动工具。
这个问题的本质不是 MCP 协议本身,而是模型客户端没配好。MCP 负责的是「Agent 怎么发现和调用工具」,但「Agent 用哪个模型来思考」这件事,仍然由 ChatOpenAI 这类模型客户端决定。base_url 空着、api_key 空着,等于告诉 LangChain「你自己猜一个模型服务」,结果就是请求发不出去,或者发到一个不存在的地址。
本文就围绕这个具体报错场景,把 stdio 和 SSE 两种传输方式下的 ChatOpenAI 配置补全,让 create_react_agent 能正常返回 (3 + 5) x 12? 的结果。注册入口在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end ,拿到 Key 之后,base_url 填 https://taotoken.net/api,模型通道就走 TaoToken。
TaoToken 前置:先把模型通道的 Key 拿到
在改代码之前,需要先有一个可用的模型服务地址和 Key。TaoToken 在这里扮演的角色就是「模型通道提供方」——它不替代你的编辑器,也不替代 MCP 服务器,只负责让 ChatOpenAI 有一个稳定的 base_url 和 api_key 可以填。
操作路径很短:
- 打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end ,完成注册。
- 进入控制台,创建一个 API Key。这个 Key 就是后面代码里
api_key要填的值。 - 记下
base_url:https://taotoken.net/api。注意这里不带/v1,也不加任何查询参数。LangChain 的ChatOpenAI会自动在末尾拼接/chat/completions等路径,所以 base_url 保持到/api这一层即可。
如果你后面还要用 Claude Code 或 Codex 这类 CLI 工具,Key 的创建入口是一样的,只是配置文件不同:Claude Code 走 settings.json 里的 ANTHROPIC_* 字段,Codex 走 config.toml。本文聚焦 MCP 客户端里的 ChatOpenAI,先把这一条通道打通。
可复制配置:stdio 与 SSE 两段客户端代码
下面直接给出改好之后的代码。核心改动只有两行:base_url 和 api_key。
stdio 客户端
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from langchain_mcp_adapters.tools import load_mcp_tools
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
import asyncio
model = ChatOpenAI(
base_url="https://taotoken.net/api",
api_key="YOUR_API_KEY",
model="gpt-4o"
)
server_params = StdioServerParameters(
command="python",
args=["F:\\PyCharm\\mcp_server.py"],
)
async def run_agent():
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await load_mcp_tools(session)
agent = create_react_agent(model, tools)
agent_response = await agent.ainvoke(
{"messages": "what's (3 + 5) x 12?"}
)
return agent_response
if __name__ == "__main__":
result = asyncio.run(run_agent())
print(result)
SSE 客户端
import asyncio
from mcp import ClientSession
from mcp.client.sse import sse_client
from langchain_openai import ChatOpenAI
from langchain_mcp_adapters.tools import load_mcp_tools
from langgraph.prebuilt import create_react_agent
model = ChatOpenAI(
base_url="https://taotoken.net/api",
api_key="YOUR_API_KEY",
model="gpt-4o"
)
server_url = "http://localhost:8080/sse"
async def run_agent():
async with sse_client(url=server_url) as streams:
async with ClientSession(*streams) as session:
await session.initialize()
tools = await load_mcp_tools(session)
agent = create_react_agent(model, tools)
agent_response = await agent.ainvoke(
{"messages": "what's (3 + 5) x 12?"}
)
return agent_response
if __name__ == "__main__":
result = asyncio.run(run_agent())
print(result)
两段代码里,YOUR_API_KEY 替换成你在 TaoToken 控制台创建的那串 Key。model 字段按你实际可用的模型 ID 填写,不要照抄示例里的名字。base_url 统一用 https://taotoken.net/api,不要写成 https://taotoken.net/api/v1,也不要带 UTM 参数——UTM 是给浏览器注册链接用的,代码里不需要。
验证请求:看 Agent 是否真的调用了工具
配置改完之后,验证分两条路。
stdio 方式:直接运行上面的客户端脚本。因为 stdio 模式下 MCP 服务器是作为子进程启动的,不需要单独开服务端。运行后观察输出,如果 create_react_agent 正常返回,你会看到类似 (3 + 5) x 12 = 96 的结果。同时,math_server.py 里 add 和 multiply 函数中写文件的逻辑会生效,output.txt 里应该出现「加法运算」和「乘法运算」的记录。这两个记录很关键——它证明 Agent 不只是自己算出了答案,而是真的通过 MCP 调用了工具。
SSE 方式:先启动 sse_server.py,终端会打印 SSE endpoint available at: http://localhost:8080/sse。然后运行 SSE 客户端脚本。客户端连上 http://localhost:8080/sse 后,同样会走 load_mcp_tools → create_react_agent → ainvoke 的流程。服务端终端里会打印 add-------------------------------- 和 multiply--------------------------------,说明工具被调用了。
如果两条路都能跑通,说明 ChatOpenAI 的 base_url 和 api_key 已经正确指向 TaoToken,模型通道和 MCP 工具通道都通了。
本篇常见错排查
报错一:Connection error 或 APIConnectionError
最常见的原因是 base_url 写错。检查三点:是不是写成了 https://taotoken.net/api/v1(多加了 /v1);是不是把注册链接的 UTM 参数复制进去了;是不是末尾多了斜杠。正确写法就是 https://taotoken.net/api。
报错二:AuthenticationError 或 401
api_key 没填、填错,或者 Key 已经被删除。回到 TaoToken 控制台重新创建一个 Key,替换代码里的 YOUR_API_KEY。注意不要把 Key 提交到公开仓库,建议用环境变量读取。
报错三:Agent 返回结果但 output.txt 没有记录
这说明模型通道通了,但 MCP 工具没被调用。检查 load_mcp_tools(session) 是否在 session.initialize() 之后执行;检查 create_react_agent(model, tools) 里的 tools 是不是从当前 session 加载的。stdio 模式下还要确认 args 里的 math_server.py 路径是绝对路径,Windows 下注意反斜杠转义。
报错四:SSE 客户端连不上 localhost:8080
先确认 sse_server.py 已经启动,并且终端打印了 SSE endpoint 地址。如果服务端没起来,客户端会直接连接拒绝。另外检查端口是否被占用,uvicorn.run 里的 port=8080 和客户端 server_url 里的端口要一致。
报错五:模型名称不存在
model 字段填了一个当前通道不支持的模型 ID。换成你确认可用的模型 ID,不要照抄示例。
语义一致 CTA
如果你在排障过程中卡在 Key 创建或接入配置上,直接去 TaoToken 的 API Keys 页面创建一个新 Key,再对照接入文档检查 base_url 和 api_key 的填法。这两个入口能解决本文 90% 的配置类报错。
如果你已经配通了 ChatOpenAI,想验证模型本身是否正常响应,可以到模型对话页面发一条测试消息,确认通道可用。
如果你不只是跑通这个 MCP 示例,而是打算长期做编码类 Agent、把 MCP 工具链用在日常开发流程里,那 Coding Plan 更适合你——它面向的就是持续性的编码和 Agent 调用场景,不用每次单独创建临时 Key。
先把 stdio 示例跑通,看到 output.txt 里出现「加法运算」和「乘法运算」,再切到 SSE 模式验证一遍。两条都通了,MCP 客户端里 ChatOpenAI 的 base_url 配置就算彻底落地了。




被折叠的 条评论
为什么被折叠?



