1. 小智 AI 机器人接入 MCP 的真实痛点:Function Calling 为什么不够用
先说结论:MCP(Model Context Protocol)不是 Function Calling 的替代品,而是它的上一层编排协议。很多人第一次做小智 AI 机器人这类项目时,会本能地把所有能力都塞进 Function Calling 里,结果工具一多就崩。我自己踩过的坑是:当工具数量超过 8 个,模型开始乱选工具,参数也经常填错,调试起来像在黑盒里摸鱼。
小智 AI 机器人的典型任务是「接收一句话,规划步骤,调用多个模型,最后输出完整结果」。比如你说「帮我写一篇 MCP 科普、配张图、整理成 Markdown」,它内部至少要跑三步:写文案、生成图片描述、汇总输出。如果只用 Function Calling,主控模型得一次性知道所有工具的 schema,还要自己维护中间状态。工具一多,上下文就爆炸,模型注意力被稀释,调用成功率断崖式下跌。
MCP 解决的正是这个问题。它把「工具注册」「上下文共享」「调用顺序」拆成独立层。Function Calling 负责单次「模型→工具」的调用,MCP 负责「多轮、多工具、多模型」之间的状态传递。你可以理解为:Function Calling 是员工举手发言,MCP 是会议纪要和议程管理。小智 AI 机器人这种多 Agent 场景,缺了 MCP 就会各说各话。
具体到落地,小智 AI 机器人的链路是这样的:用户输入 → 主控模型规划 → 通过 MCP 把任务拆成子任务 → 每个子任务绑定一个 MCP Server 提供的工具 → 子模型执行 → 结果写回共享上下文 → 主控模型汇总。这里的关键是 MCP Server 把工具以标准协议暴露出来,主控模型不需要提前知道每个工具的实现细节,只需要知道「有这个能力」和「怎么调用」。
我实测下来,把工具从 Function Calling 迁移到 MCP 后,工具数量从 6 个扩展到 20 个,主控模型的调用准确率反而更稳。原因是 MCP 把工具描述和上下文管理分离了,模型每次只看到当前步骤需要的工具子集,而不是全部 schema。这对小智 AI 机器人这种需要动态编排的场景特别重要。
还有一个容易被忽略的点:MCP 让「上下文」变成一等公民。Function Calling 的返回值通常只回给当前模型,下一轮就丢了。MCP 会把每次调用的输入输出写进共享 context,后续任何 Agent 都能读到。小智 AI 机器人做任务回放和调试时,这个特性直接省掉一半日志工作。
所以如果你正在做小智 AI 机器人,或者任何需要多工具协作的 Agent 项目,建议先把 MCP 的接入层搭好,再往上堆 Function Calling。顺序反了,后面重构成本很高。
2. TaoToken 前置准备:MCP Server 接入的 Base URL 与 Key 怎么配
在写 MCP Server 之前,先把模型调用通道准备好。小智 AI 机器人本身不绑定具体模型供应商,它通过 OpenAI 兼容接口调用模型。这里我用 TaoToken 作为统一入口,原因是它同时支持 Claude、GPT 等模型,MCP 编排时切换模型不用改代码。
你需要准备三样东西:Base URL、API Key、Model ID。这三件套在 MCP Server 配置里会反复出现,建议先记下来。
Base URL 用https://taotoken.net/api,注意这个地址不带任何查询参数,直接作为 OpenAI 兼容的 base_url 使用。API Key 在控制台的 API Keys 页面创建,创建后只显示一次,复制保存好。Model ID 根据你要用的模型填,比如claude-sonnet-4-20250514或gpt-4o,具体以模型列表为准。
如果你还没创建 Key,可以走这个路径:先打开 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_xiaozhi 创建,然后到 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_xiaozhi 看接入文档确认参数格式。文档里有完整的 curl 示例,照着改就行。
这里有个细节:MCP Server 通常以子进程方式启动,环境变量注入是最稳的方式。不要把 Key 硬编码在代码里,也不要在 MCP 配置的 JSON 里明文写 Key 然后提交到 git。我习惯用.env文件加dotenv加载,MCP 配置里只引用环境变量名。
另外,如果你打算长期跑小智 AI 机器人这种 Agent 任务,建议直接上 Coding Plan,因为 Agent 编排的 token 消耗比单轮对话高很多,按量计费容易失控。Coding Plan 的入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_xiaozhi ,适合需要稳定跑多轮任务的场景。
配置完成后,先用一个最小请求验证通道是否通。可以用 curl 直接打:
curl https://taotoken.net/api/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -d '{ "model": "claude-sonnet-4-20250514", "messages": [{"role": "user", "content": "ping"}], "max_tokens": 16 }'如果返回正常,说明 Base URL 和 Key 没问题。这一步别跳过,后面 MCP Server 报错时,你能快速判断是通道问题还是协议问题。
3. 可复制配置:MCP Server 的 JSON 片段与工具注册示例
这一节给可直接复制的配置。小智 AI 机器人接入 MCP 时,通常有两种配置位置:一是 MCP Client 的配置文件(比如 Claude Desktop 的claude_desktop_config.json,或 Cline 的 MCP 设置),二是小智 AI 机器人自己的mcp_servers.json。格式基本一致,都是mcpServers对象。
先看 MCP Server 的启动配置。假设你写了一个 Python 的 MCP Server,文件叫xiaozhi_mcp_server.py,用 stdio 传输:
{ "mcpServers": { "xiaozhi-tools": { "command": "python", "args": ["/path/to/xiaozhi_mcp_server.py"], "env": { "TAOTOKEN_BASE_URL": "https://taotoken.net/api", "TAOTOKEN_API_KEY": "${TAOTOKEN_API_KEY}", "TAOTOKEN_MODEL_ID": "claude-sonnet-4-20250514" } } } }注意env里的${TAOTOKEN_API_KEY}是引用系统环境变量,不是字面量。如果你用的客户端不支持变量展开,就改成实际值,但别提交到公开仓库。
接下来是工具注册。MCP Server 用@server.tool()装饰器注册工具,每个工具要有清晰的 name、description 和参数 schema。小智 AI 机器人场景下,我注册了三个基础工具:plan_task、generate_text、generate_image_prompt。下面是精简后的代码:
from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent import os, httpx, json server = Server("xiaozhi-tools") @server.tool() async def plan_task(goal: str) -> list[TextContent]: """把用户目标拆解成可执行的子任务列表。""" prompt = f"把以下目标拆成3到5个子任务,每行一个:{goal}" result = await call_model(prompt) return [TextContent(type="text", text=result)] @server.tool() async def generate_text(topic: str, style: str = "科普") -> list[TextContent]: """根据主题和风格生成一段文本。""" prompt = f"用{style}风格写一段关于{topic}的内容,200字以内。" result = await call_model(prompt) return [TextContent(type="text", text=result)] async def call_model(prompt: str) -> str: base = os.environ["TAOTOKEN_BASE_URL"] key = os.environ["TAOTOKEN_API_KEY"] model = os.environ["TAOTOKEN_MODEL_ID"] async with httpx.AsyncClient(timeout=60) as client: resp = await client.post( f"{base}/v1/chat/completions", headers={"Authorization": f"Bearer {key}"}, json={ "model": model, "messages": [{"role": "user", "content": prompt}], "max_tokens": 512 } ) data = resp.json() return data["choices"][0]["message"]["content"] async def main(): async with stdio_server() as (read, write): await server.run(read, write, server.create_initialization_options()) if __name__ == "__main__": import asyncio asyncio.run(main())这段代码的关键点:工具函数用async,返回值是list[TextContent],MCP 协议要求这个格式。call_model里读的是环境变量,和上面 JSON 配置的env对应。Model ID 通过环境变量传入,切换模型不用改代码。
如果你用的是 Cline 或 Claude Code 这类客户端,MCP 配置位置不同,但mcpServers结构一样。Cline 在设置里的 MCP Servers 面板粘贴上面的 JSON 即可。Claude Code 则是在~/.claude/settings.json或项目级.mcp.json里配置。三件套(Base URL、Key、Model ID)在env里写全,缺一个都会导致工具调用时 401 或模型找不到。
还有一个容易踩的坑:MCP Server 的command路径。如果你用虚拟环境,python要写成虚拟环境里的绝对路径,比如/Users/you/venv/bin/python,否则客户端启动时找不到依赖。我因为这个报过ModuleNotFoundError: No module named 'mcp',排查了半小时。
4. 端到端验证:一次小智 AI 机器人 MCP 调用全流程
配置写完后,必须做一次端到端验证。这一步的目标是:从用户输入开始,经过 MCP 工具调用,到最终输出,整条链路跑通。下面是我实测的步骤。
第一步,启动 MCP Client 并确认 Server 已连接。如果你用 Claude Desktop,重启后看日志里有没有xiaozhi-tools的注册信息。用 Cline 的话,MCP 面板会显示工具列表。确认能看到plan_task、generate_text这几个工具名。
第二步,发一个真实任务。在小智 AI 机器人的对话入口输入:「帮我规划一篇 MCP 入门文章的写作步骤,并生成第一段开头。」主控模型应该先调用plan_task,拿到子任务列表,再调用generate_text生成开头。
第三步,观察调用日志。MCP 的调用过程会在 Client 端显示 tool_use 和 tool_result。正常流程是:模型输出一个tool_useblock,name 是plan_task,input 是{"goal": "..."};Server 执行后返回tool_result,内容是子任务列表;模型读到结果后,再发起第二次tool_use调用generate_text。
第四步,检查最终输出。如果一切正常,你会看到模型汇总了子任务和开头段落,形成完整回复。这时候去 MCP Server 的日志里确认,每次call_model都返回了 200,没有 401 或超时。
我实测时遇到过一个现象:第一次调用成功,第二次调用报reading 'choices'错误。原因是call_model里没处理非 200 响应,直接取data["choices"],而实际上返回的是错误对象。修复方法是加一层判断:
if resp.status_code != 200: return f"模型调用失败: {resp.status_code} {resp.text}" data = resp.json() if "choices" not in data: return f"响应格式异常: {json.dumps(data)[:200]}" return data["choices"][0]["message"]["content"]这个改动很小,但能让排障快很多。MCP 工具调用失败时,错误信息会通过tool_result回传给模型,模型有时会自己重试,有时直接放弃。加上明确错误信息后,模型能根据错误类型决定是否重试。
验证通过后,你可以把任务复杂度提上去。比如让机器人做「规划 → 写文案 → 生成图片描述 → 汇总成 Markdown」四步。这时候 MCP 的上下文共享优势就体现出来了:generate_text的结果会写进 context,后续步骤能直接引用,不需要模型重新描述。
如果你想让验证更直观,可以在 MCP Server 里加一个echo_context工具,把当前 context 的 history 打印出来。这样你能看到每个 Agent 读到了什么、写了什么。小智 AI 机器人做调试时,这个工具比看日志高效得多。
最后提醒一点:端到端验证时,先用小max_tokens(比如 256)跑通流程,再放大。因为 MCP 多轮调用会累积 token,一开始就用大 token 容易在调试阶段烧掉大量额度。
5. 常见报错排查:401、local proxy failed、reading choices、OAuth
这一节对照真实报错,给排查路径。MCP 接入的报错大致分四类,每类的根因不同。
401 Unauthorized:最常见。根因是 API Key 没传对。检查三处:MCP 配置的env里TAOTOKEN_API_KEY是否引用了正确的环境变量;环境变量是否在启动 Client 的 shell 里 export 了;Key 是否复制完整(有时会漏掉前缀)。如果用的是 Claude Code,还要检查settings.json里的env是否被项目级配置覆盖。我遇到过一次是.env文件没被加载,因为 MCP Server 的工作目录和.env所在目录不一致,改成绝对路径就好了。
local proxy failed:这个报错通常出现在 MCP Client 启动 Server 子进程时。根因是command或args路径不对,子进程没起来。检查command是不是虚拟环境的 python 绝对路径,args里的脚本路径是否存在。如果你在配置里写了相对路径,Client 的工作目录可能和你预期不同。改成绝对路径能解决 90% 的这类问题。另外,Windows 下command要写python.exe的完整路径,不能只写python。
reading 'choices':这是代码层面的错误,不是 MCP 协议错误。根因是call_model里直接取data["choices"],但响应里没有这个字段。可能是模型名写错、请求体格式不对、或者返回了错误对象。修复方法是加状态码和字段判断,像上一节那样。另外检查model字段是否和 TaoToken 支持的 Model ID 一致,写错模型名有时返回 404 而不是 400,错误信息里没有choices。
OAuth 相关报错:如果你用的是需要 OAuth 的 MCP Server(比如某些远程 MCP),报错通常是OAuth token expired或invalid_client。这类问题不在模型通道,而在 MCP Server 自身的鉴权。检查 OAuth 配置的 client_id、client_secret、redirect_uri 是否和提供方一致。如果是本地 stdio Server,一般不走 OAuth,遇到这个报错说明你配置了错误的传输方式。
除了这四类,还有一个隐蔽问题:MCP Server 启动成功但工具列表为空。根因通常是@server.tool()装饰器没生效,或者server.run之前没注册工具。检查工具函数是否在main之前定义,装饰器是否拼写正确。我见过有人把@server.tool()写成@server.tools(),结果工具一个都没注册,Client 显示连接成功但无工具可用。
排障时建议开两个终端:一个跑 MCP Client 看协议层日志,一个直接跑 MCP Server 看应用层日志。这样能快速定位是协议问题还是代码问题。如果协议层显示 tool_use 发出但没 tool_result,就是 Server 执行出错;如果 tool_result 返回了但模型没继续,就是模型侧的问题。
6. 从 Function Calling 到 Agent:小智 AI 机器人的 MCP 落地建议
最后聊落地路径。小智 AI 机器人这类项目,从 Function Calling 迁移到 MCP,建议分三步走,不要一次性重构。
第一步,把现有 Function Calling 工具包装成 MCP Server。不需要改工具逻辑,只需要加一层 MCP 协议适配。这样主控模型可以先通过 MCP 调用,验证协议层没问题。这一步的产出是一个可运行的 MCP Server,工具数量和原来一致。
第二步,把上下文管理从模型侧移到 MCP 侧。原来你可能在 prompt 里塞历史记录,现在改成 MCP 的 context 共享。每个工具调用后,结果自动写进 context,后续步骤按需读取。这一步能显著降低 prompt 长度,提升多轮任务的稳定性。
第三步,引入多 Agent 编排。主控模型只负责规划和汇总,子任务分发给不同模型执行。MCP 在这里的作用是保证每个 Agent 读到一致的上下文,避免「断片」。小智 AI 机器人的任务回放功能,就是靠 MCP 的 context history 实现的。
如果你要长期跑 Agent 任务,建议用 Coding Plan,因为多轮编排的 token 消耗是单轮对话的数倍,按量计费容易超预算。模型对话入口在 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_xiaozhi ,可以先用它验证模型可用性。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_xiaozhi ,里面有完整的参数说明。API Key 创建在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=mcp_xiaozhi 。
一个实用技巧:在 MCP Server 里加一个dry_run参数,让工具只返回将要执行的 prompt,不实际调用模型。这样调试编排逻辑时,不会消耗 token。等编排跑通后,再把dry_run关掉。这个技巧在小智 AI 机器人这种多步任务里特别省成本。
另一个建议是给每个工具加超时和重试。MCP 协议本身不强制超时,但 Agent 场景下,一个工具卡住会拖垮整个任务。在call_model里设timeout=60,失败后重试一次,能避免大部分偶发超时。
最后,别把 MCP 当成万能药。它解决的是多工具、多模型之间的协作标准化问题,不解决模型本身的能力问题。如果你的任务只需要单次 Function Calling,没必要上 MCP。但如果你在做小智 AI 机器人这种需要规划、执行、汇总的 Agent 系统,MCP 是目前最务实的接入层方案。