把 AsyncOpenAI 的 base_url 改到 TaoToken 通道后,让 Codex 跑通 MCP stdio
在 MCP Python SDK 里写 FastMCP 服务端和 stdio 客户端时,最容易卡住的不是@mcp.tool()的注册逻辑,而是客户端里AsyncOpenAI的base_url到底填什么。很多人把server.py跑起来了,stdio_client也连上了,list_tools()能打印出add_2_numbers,但一到chat.completions.create就报连接错误或 404。这篇就从「接入配置」视角,把AsyncOpenAI的base_url改到 TaoToken 通道(https://taotoken.net/?utm_source=taotoken_aicg_blog_end ),让 Codex 按原文的 stdio 步骤真正跑通工具调用。
一、原问题与场景:stdio 通了,模型通道没通
原文用 MCP Python SDK 写了一个 FastMCP 服务端,暴露add_2_numbers、multiply_2_numbers这类@mcp.tool(),客户端用stdio_client拉起server.py,再用AsyncOpenAI把工具列表交给大模型识别。问题出在客户端__init__里这一行:
self.client = AsyncOpenAI(api_key=api_key, base_url=base_url)api_key、base_url、model三个参数是从config.json读进来的。如果base_url还是默认的 OpenAI 地址,或者填成了带/v1的地址,AsyncOpenAI在发chat.completions.create时就会走到错误的端点。表现是:stdio 握手成功、工具列表打印正常,但模型请求直接抛异常,process_query里的while assistant_message.tool_calls永远进不去,add_2_numbers也就永远不会被调用。
所以这条的核心不是改 MCP 协议代码,而是把客户端的模型通道指向 TaoToken,让AsyncOpenAI的请求能正常返回tool_calls。
二、TaoToken 前置:先拿 Key,再定 base_url
在改config.json之前,先去 TaoToken 官网创建一个 Key:
- 官网入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end
- 创建 Key 的页面在控制台的 API Keys 里,登录后直接新建即可
- 拿到形如
YOUR_API_KEY的字符串,后面填进config.json的llm.api_key
这里要强调一个容易踩的点:TaoToken 的 API 地址是https://taotoken.net/api,不要带/v1。AsyncOpenAI内部会自己拼接路径,如果你写成https://taotoken.net/api/v1,最终请求会变成/api/v1/chat/completions这种重复路径,直接 404。原文客户端里base_url是透传给AsyncOpenAI的,所以这个值必须干净。
如果你还没创建 Key,可以先看接入文档确认当前支持的模型 ID,再决定config.json里model填哪个。模型对话页面可以用来先验证通道是否通,再回到 MCP 客户端里跑工具调用。
三、可复制配置:config.json 与客户端改动
原文客户端main()里是这样读配置的:
with open("config.json", "r") as f: config = json.load(f) client = Stdio_MCPClient( config["llm"]["api_key"], config["llm"]["base_url"], config["llm"]["model"] )所以只需要改config.json,不用动Stdio_MCPClient的构造逻辑。把文件改成:
{ "llm": { "api_key": "YOUR_API_KEY", "base_url": "https://taotoken.net/api", "model": "MODEL_ID" } }三个字段对应关系:
api_key:TaoToken 控制台创建的 Key,填YOUR_API_KEY的位置base_url:固定https://taotoken.net/api,结尾不要加/v1,也不要加斜杠model:填 TaoToken 接入文档里列出的模型 ID,不要照抄 OpenAI 的gpt-4-turbo之类
客户端代码本身不需要改AsyncOpenAI那一行,因为它已经把base_url透传进去了。唯一要确认的是connect_to_stdio_server里command="python"、args=["server.py"]的路径对,server.py里mcp.run(transport='stdio')没有被改成 sse。
如果你用的是 Codex 这类 CLI 工具去拉起这个客户端,注意 Codex 自己的config.toml和这里的config.json是两套配置,不要混。MCP 客户端的模型通道由config.json决定,Codex 只负责按 stdio 启动server.py。
四、验证请求与成功结果
改完config.json后,按原文步骤跑:
python client.py预期看到的第一段输出是 stdio 连接成功:
成功链接到testserver服务,对应的tools: ['add_2_numbers', 'multiply_2_numbers']这说明stdio_client已经拉起server.py,list_tools()也拿到了@mcp.tool()注册的函数。接着在Query:提示符下输入:
Query: 帮我算一下 3 加 5 等于多少如果模型通道配置正确,会看到:
calling tools add_2_numbers, with args {'a': 3, 'b': 5} Result: 8 AI: 3 加 5 等于 8这里的calling tools add_2_numbers就是process_query里session.call_tool(tool_name, tool_args)被触发的证据。也就是说,AsyncOpenAI返回了tool_calls,客户端把add_2_numbers的参数解析出来,通过 stdio 发给服务端执行,再把result.content[0].text塞回messages做第二轮chat.completions.create。
如果只想先验证模型通道,不想跑完整 MCP 流程,可以单独写一个最小脚本:
import asyncio from openai import AsyncOpenAI async def main(): client = AsyncOpenAI( api_key="YOUR_API_KEY", base_url="https://taotoken.net/api" ) resp = await client.chat.completions.create( model="MODEL_ID", messages=[{"role": "user", "content": "hi"}] ) print(resp.choices[0].message.content) asyncio.run(main())这个能通,再回去跑 MCP 客户端,就能把问题范围缩小到 MCP 层而不是通道层。
五、本篇常见错排查
错误 1:base_url 带了/v1
现象是chat.completions.create报 404 或Not Found。检查config.json里base_url是不是写成了https://taotoken.net/api/v1。改成https://taotoken.net/api即可。
错误 2:api_key 没换,还是占位符
现象是 401 或invalid api key。确认config.json里llm.api_key是 TaoToken 控制台创建的真实 Key,不是YOUR_API_KEY字面量。
错误 3:model 填了 OpenAI 的模型名
现象是模型不存在或 400。TaoToken 通道的model要用接入文档里列出的 ID,不要直接抄原文示例里的gpt-4-turbo。
错误 4:stdio 连上了但 tool_calls 为空
如果list_tools()打印正常,但模型回复里没有tool_calls,先确认available_tools的构造是否正确:
self.available_tools = [ { "type": "function", "function": { "name": tool.name, "description": tool.description, "parameters": tool.inputSchema } } for tool in response.tools ]这段是把 MCP 的inputSchema映射成 OpenAI function calling 的parameters。如果tool.description为空,模型可能不触发调用,给@mcp.tool()函数补上 docstring。
错误 5:server.py 路径不对
stdio_client的args=["server.py"]是相对当前工作目录的。如果从别的目录启动client.py,会报找不到server.py。用绝对路径或先cd到文件所在目录。
错误 6:Codex 拉起时环境变量没传
如果通过 Codex 的config.toml配置 MCP server,注意env字段要显式传api_key等变量,否则客户端读不到。原文connect_to_stdio_server的env={}是空字典,需要按需补。
六、语义一致 CTA
这条的改动点只有一个:把AsyncOpenAI的base_url指向 TaoToken 通道,让 MCP stdio 客户端的模型请求能正常返回tool_calls。如果你在配置config.json或创建 Key 时卡住,可以直接去 API Keys 页面和接入文档对照字段;想先确认模型通道是否通,用模型对话页面发一条最小请求即可;如果是要长期跑 Codex + MCP 这类编码 Agent 场景,Coding Plan 更适合持续调用。
把base_url改对之后,原文的stdio_client、ClientSession、call_tool这套流程就能完整跑通,add_2_numbers这类@mcp.tool()也会真正被大模型识别并调用。