news 2026/9/22 10:14:30

MCP 天气 demo 的 qwen-max 调用,Base URL 改填 TaoToken

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MCP 天气 demo 的 qwen-max 调用,Base URL 改填 TaoToken

1. 为什么 MCP 天气 demo 总是卡在模型通道这一行

MCP(Model Context Protocol)这两年被聊得很多,但真正动手跑通一个最小闭环的人,往往不是卡在协议本身,而是卡在「执行工具那一端的模型通道」上。我见过太多照着教程抄weather_server.pyclient.py的朋友,Server 端mcp.run(transport="stdio")跑得好好的,session.list_tools()也能列出query_weather,结果一到client.chat.completions.create()就报 401、404 或者Connection error

问题几乎都出在同一个地方:OpenAI(api_key=..., base_url=...)这两行。教程里写的是base_url="compatible-mode/v1",很多人直接复制,结果 SDK 拼出来的请求地址缺了域名,或者多带了一层/v1,请求根本发不出去。更麻烦的是,当你想换一个模型通道(比如从默认的 qwen-max 换成别的),又不知道该动哪一行——是改model字段,还是改base_url,还是两个都要改?

这篇就专门解决这个痛点。MCP 协议、stdio 管道、weather_server.py里的query_weather逻辑,全部保持原样不动,我只替换 client 里取 Key 和写base_url的两行。改完之后,asyncio.run(main())能在「AI 最终回复」里打印出城市天气文本,就说明 tool_call 链路和模型请求都通了。适合已经会写 FastMCP Server、但被 client 模型通道卡住的人。

2. 先理清 MCP 的两条传输通道和天气 Server 的角色

在动 client 之前,得先明确一件事:MCP 的连接和模型请求是两条完全独立的链路,别把它们混在一起。

2.1 Stdio 与 Streamable HTTP 的分工

Stdio 是本地通信方式。客户端把 MCP Server 当成一个子进程启动,两者通过操作系统的 stdin/stdout 管道「递纸条」,完全绕开网络栈,延迟低、数据不出本机。天气 demo 用的就是这条通道,mcp.run(transport="stdio")就是让 Server 挂在这条管道上等消息。

Streamable HTTP 是远程通信方式,客户端发 POST 请求,服务端通过长连接推送流式响应,适合云端服务、SaaS 集成、企业多租户。早期的 SSE 因为只支持单向推送,正在被它取代。

关键点在于:这两条通道负责的是「客户端 ↔ MCP Server」之间的工具调用,跟「客户端 ↔ 大模型」之间的请求没有任何关系。天气 Server 只干一件事——收到query_weather(city)就返回天气文本。至于这个工具调用请求是谁发起的、模型是哪家的,Server 完全不关心。

2.2 weather_server.py 里 query_weather 到底做了什么

Server 端逻辑很简单,fetch_weather用 httpx 异步请求天气接口,format_weather把 JSON 拼成自然语言,@mcp.tool()装饰的query_weather把两者串起来。它对外暴露的接口就是「给我一个城市名,我还你一段天气描述」。

所以 client 里真正需要模型参与的部分只有两处:第一次create()让模型决定要不要调query_weather,第二次create()把工具结果回灌给模型生成最终回复。这两次请求走的是 OpenAI SDK,跟 MCP 的 stdio 管道是并行的两条线。

3. TaoToken 前置:只提供 Key 和 Base URL 两样东西

这里要特别说清楚 TaoToken 在这个 demo 里的定位,避免误解。

TaoToken 在这里只提供两样东西:一把 API Key,和一个 Base URL。它不参与 MCP 的连接,不参与 stdio 管道的建立,也不参与session.call_tool()的执行。工具调用那一端仍然是你的本地 Server 通过管道完成的,TaoToken 完全不碰。

换句话说,你只是把 client 里「向大模型发请求」这一段的地址和凭证换掉,其余逻辑一行不动。

操作上分两步:

第一步,打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 注册账号。注册流程很常规,邮箱验证完就能进控制台。

第二步,在控制台里创建一把 API Key。创建入口在 https://taotoken.net/api-keys ,点新建,复制出来的那串就是你要填进 client 的api_key

拿到 Key 之后,Base URL 固定填https://taotoken.net/api。注意这里有两个坑:不要带/v1,也不要带任何 utm 参数。SDK 会自己在后面拼/chat/completions,你多写一层/v1就会变成/api/v1/chat/completions,直接 404。

4. 可复制配置:只改 client 里的两行

现在进入正题。假设你的weather_server.py已经按教程写好,uv venvuv add mcpuv add openai httpx python-dotenv都装完了,我们只动 client。

4.1 改 api_key 和 base_url

原来的写法是从环境变量取DASHSCOPE_API_KEYbase_urlcompatible-mode/v1。现在替换成 TaoToken 的 Key 和地址:

import asyncio import os import json from contextlib import AsyncExitStack from openai import OpenAI from dotenv import load_dotenv from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client load_dotenv() async def main(): # 只改这两行:Key 换成 TaoToken 创建的 Key,base_url 换成 TaoToken 地址 client = OpenAI( api_key=os.getenv("TAOTOKEN_API_KEY"), base_url="https://taotoken.net/api" ) server_params = StdioServerParameters( command="D:\\python.exe", args=["e:\\mcp-servers-weather.py"] ) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools_response = await session.list_tools() openai_tools = [ { "type": "function", "function": { "name": tool.name, "description": tool.description, "parameters": tool.inputSchema } } for tool in tools_response.tools ] messages = [{"role": "user", "content": "Shanghai weather today"}] response = client.chat.completions.create( model="qwen-max", messages=messages, tools=openai_tools, tool_choice="auto" ) message = response.choices[0].message if message.tool_calls: for tool_call in message.tool_calls: if tool_call.function.name == "query_weather": args = json.loads(tool_call.function.arguments) result = await session.call_tool("query_weather", arguments=args) messages.append(message) messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": result.content[0].text }) final_response = client.chat.completions.create( model="qwen-max", messages=messages ) print("AI 最终回复:", final_response.choices[0].message.content) else: print("AI 回复:", message.content) if __name__ == "__main__": asyncio.run(main())

4.2 把 Key 放进 .env

别把 Key 硬编码在代码里。在项目根目录建一个.env

TAOTOKEN_API_KEY=你从控制台复制的那串Key

load_dotenv()会自动把它读进环境变量,os.getenv("TAOTOKEN_API_KEY")就能取到。如果你之前用的是DASHSCOPE_API_KEY,把变量名换掉即可,代码里对应的os.getenv也要跟着改。

4.3 想换模型时改哪一行

这是很多人问的问题。答案很明确:只改model字段

base_url指向的是 TaoToken 的入口,它背后能路由到哪些模型是服务端的事,你不需要为每个模型换一个地址。所以从qwen-max换成别的,只动model="..."这一处,两处create()都要改(第一次决定调工具、第二次生成最终回复)。

改动项改哪里说明
换 Key.env里的TAOTOKEN_API_KEY控制台重新创建即可
换模型两处create()model字段base_url 不动
换 Base URLOpenAI(base_url=...)固定https://taotoken.net/api,不带 /v1

5. 验证请求:看到天气文本就算通了

配置改完,直接跑:

python client.py

如果一切正常,终端会打印类似这样的内容:

AI 最终回复: 上海当前天气:多云,气温 22°C,湿度 68%

看到「AI 最终回复」后面跟着城市天气文本,就说明整条链路通了。这里其实验证了两件事:一是session.call_tool("query_weather", ...)通过 stdio 管道成功拿到了工具结果,二是两次client.chat.completions.create()都成功打到了 TaoToken 的地址并返回了内容。

如果第一次create()返回的message.tool_calls是空的,说明模型没决定调工具,可以检查tools参数有没有正确传入、tool_choice是不是"auto"。如果tool_calls有值但最终回复为空,多半是第二次create()messages里 tool 结果没拼对。

想单独验证模型通道是否通,可以先用模型对话页面发一条普通消息,确认 Key 和地址没问题,再回来跑完整 demo。

6. 本篇常见错排查

6.1 base_url 少写域名或多带 /v1

这是最高频的错。base_url="compatible-mode/v1"这种写法是相对路径,SDK 拼出来会缺域名,直接报连接错误。正确写法是完整的https://taotoken.net/api。反过来,写成https://taotoken.net/api/v1也不行,SDK 会再拼一层,变成/api/v1/chat/completions,返回 404。

记住一个判断方法:base_url 填到/api为止,后面什么都不要加

6.2 401 报错:Key 没读到或填错

openai.AuthenticationError一般是 Key 的问题。先确认.env文件在项目根目录、变量名和os.getenv里写的一致。如果.env里有多余空格或引号,也会导致读取失败。最稳妥的办法是临时print(os.getenv("TAOTOKEN_API_KEY"))看一眼,确认取到的是完整字符串。

6.3 stdio 管道报错:路径写错

StdioServerParameters里的commandargs必须是绝对路径。Windows 下反斜杠要转义,写成"D:\\python.exe"。如果command指向的 python 不是你装 mcp 依赖的那个虚拟环境,会报ModuleNotFoundError: No module named 'mcp'。建议直接用虚拟环境里的 python 解释器路径。

6.4 工具调用成功但最终回复为空

检查messages.append(message)有没有执行。第一次create()返回的message对象必须原样 append 进messages,否则第二次请求时模型看不到自己发起的 tool_call,会不知道该怎么接。同时tool_call_id要和message.tool_calls里的 id 对应上。

6.5 换模型后报模型不存在

如果model字段填了一个 TaoToken 不支持的名称,会返回模型不存在的错误。换模型前先在模型对话页面确认目标模型可用,再填进代码。base_url 不用动,只改model

7. 后续怎么接

跑通这个 demo 之后,你手里其实有了一套可复用的模板:MCP Server 负责工具,client 负责把工具结果回灌给模型,两者通过 stdio 管道解耦。要加新工具,就在 Server 里再写一个@mcp.tool();要换模型通道,就改 client 里的model字段。

如果你打算把这个模式用到长期编码或 Agent 场景,比如让模型反复调用多个工具、维护多轮上下文,可以了解一下 Coding Plan,它更适合这种持续性的调用需求。接入文档在 https://taotoken.net/doc ,里面有完整的参数说明和示例。API Key 管理还是回到 https://taotoken.net/api-keys ,随时可以新建或吊销。

最后提醒一句:TaoToken 在这个链路里只负责模型请求的 Key 和 Base URL,MCP 的连接、工具的注册与执行,全部在你本地完成。把这两条链路分清楚,后面再遇到报错,你就能一眼判断是模型通道的问题还是 MCP 管道的问题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/22 10:14:23

3分钟吃透昆特算法最佳实践面试突击

3分钟吃透昆特算法最佳实践面试突击 官方文档动辄几百页,看完脑子还是浆糊?别急,直接看这篇【昆特】算法最佳实践。 很多刚入行的同学,面对“昆特”这种听起来高大上的概念,第一反应是打开官方Wiki。结果呢?看了半小时,只记住了“分布式一致性”这几个字。面试时考官问:“为什么选择昆特而不是Raft?”你…

作者头像 李华
网站建设 2026/9/22 10:14:01

笔记本和超级本避坑速查手册:3个致命报错救急指南

笔记本和超级本避坑速查手册:3个致命报错救急指南 官方文档几百页看进去全是云里雾里,关键报错找不到重点,真的能把人逼疯。 别慌,这份【笔记本和超级本】避坑速查手册,直接把你常踩的坑和修复代码甩出来。 我们只讲干货,不整虚的,3分钟看懂原理,10分钟修好电脑。 坑一:电池健康度虚标与电源管理失效…

作者头像 李华
网站建设 2026/9/22 10:13:58

OpenSumi 适配 VS Code v1.60.0 API,Codex 侧 Base URL 填 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/22 10:13:43

3步搞定梦幻西游挤线器性能瓶颈含完整示例

3步搞定梦幻西游挤线器性能瓶颈含完整示例 官方文档翻了三遍,关键参数还是没看懂?别急,这里直接上 完整示例 ,3秒定位卡顿根源。 做梦幻西游自动挂机的都知道,挤线器就是那个帮你抢在开服前几秒把角色塞进服务器的脚本。新手常踩的坑是:以为代码写得再快就能挤进去,结果因为网络延迟、内存泄漏或者算法低效,反…

作者头像 李华
网站建设 2026/9/22 10:13:23

魔兽世界技能喊话宏性能优化:2026最新实战指南

魔兽世界技能喊话宏性能优化:2026最新实战指南 配置环境就卡半天?别急,这是老玩家和开发者的通病。很多兄弟在写宏时,只关注功能实现,忽略了底层逻辑的性能损耗,导致高帧率下延迟飙升。今天聊的 2026最新 实践,就是解决这个痛点。 性能瓶颈定位…

作者头像 李华