
1. 为什么你的智能体需要一个 MCP很多人搭 AI 智能体时都会卡在同一个地方模型本身很聪明但它只能聊天碰不到真实世界的数据。你想让它查一下 GitHub 仓库的 issue、读一下本地文件、调一下内部接口就得自己写一堆函数再手动塞进 prompt 里工具一多就乱成一锅粥。MCPModel Context Protocol模型上下文协议就是来解决这个问题的。它把「外部系统能干什么」标准化成一个个 Tool让大模型用统一的方式去发现和调用。你可以把它理解成智能体世界的 USB-C 接口以前每个设备一个专用口现在统一成一个标准插上就能用。这篇文章面向已经会写 Python、跑过 LangChain 或类似框架但还没真正跑通过一个 MCP Server 的同学。我会先拆清楚 Host / Client / Server 三方到底谁在干什么再带你手写一个最小可运行的本地 MCP Server用 TaoToken 的统一 Key 接上模型最后验证工具列表能不能被拉出来、调用能不能回显。全程可复制踩坑点我也会标出来。2. MCP 的通信模型Host、Client、Server 到底谁是谁刚接触 MCP 最容易懵的就是这三个角色。我用一个生活化的类比帮你定住Host 是「宿主应用」也就是你最终在用的那个智能体程序比如一个命令行助手、一个 IDE 插件、一个聊天机器人。它负责跟用户交互决定什么时候该调工具。Client 是「能力接入层」它住在 Host 里面专门负责跟各个 MCP Server 建立连接、拉取工具列表、把工具翻译成 Host 能用的格式比如 LangChain Tool。一个 Host 可以同时挂多个 Client每个 Client 连一个 Server。Server 是「工具提供方」它把某个外部系统GitHub、文件系统、数据库、你自己的业务接口封装成标准 Tool 对外暴露。Server 不关心谁在调它只负责按协议响应「你有哪些工具」和「帮我执行这个工具」。一次完整的工具调用流程是这样的用户提问 → Host 把问题交给模型 → 模型决定要用某个工具 → Host 通过 Client 找到对应 Server → Client 发请求给 Server → Server 执行真实操作并返回结果 → 结果回灌给模型 → 模型生成最终回答。这里有个关键点模型本身不直接连 Server它只是「决定调哪个工具、传什么参数」。真正的连接和执行由 Client 和 Server 完成。这个分层设计的好处是你换模型、换 Host 都不用动 Server工具生态可以复用。MCP Server 的获取方式主要有四种我列个表方便你选方式适用场景部署成本稳定性远程托管服务通用工具想零部署最低依赖服务商包管理器一键安装npm/pip/go 生态的通用工具低高Docker 容器运行需要环境隔离中很高源码克隆 编译二次开发、私有工具高自己掌控新手建议从包管理器一键安装起步跑通流程后再考虑 Docker 或自研。3. 前置准备用 TaoToken 统一 Key 管住所有模型调用在写 Server 之前先把模型接入这块理顺。智能体开发最烦的事情之一就是今天用这个模型、明天换那个模型Key 和 base_url 到处散落改一处漏一处。我的做法是用 TaoToken 做统一入口。它提供一个兼容 OpenAI 协议的 API 地址你只要把 base_url 指过去用同一个 Key 就能切换不同模型代码里不用改来改去。先拿到你的 Key打开 https://taotoken.net/api-keys 登录后创建一个 API Key复制保存好。这个 Key 就是你后面所有模型调用的凭证。然后在项目里配置环境变量别把 Key 硬编码进代码export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api如果你用的是 Python装好依赖pip install langchain langchain-openai langchain-mcp-adapters mcp这里langchain-mcp-adapters是关键它负责把 MCP Server 暴露的工具转成 LangChain Tool省得你自己写适配层。mcp是官方 SDK写 Server 要用。注意base_url 结尾不要多加/v1TaoToken 的兼容层已经处理好了路径多写反而会 404。这个坑我见过不少人踩。配置这块建议单独放一个config.toml把模型参数和 MCP Server 配置分开管理后面换模型只改这一处[llm] model gpt-4o-mini base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY temperature 0.2 [mcp.servers.github] transport stdio command npx args [-y, modelcontextprotocol/server-github] enabled true description GitHub MCP Server for repository operationstransport stdio表示用标准输入输出跟 Server 通信这是本地 Server 最常用的方式。command和args就是启动这个 Server 的命令跟你在终端里敲的一样。4. 手写一个最小 MCP Server 并接入智能体现在进入正题。我先带你写一个自己的本地 MCP Server功能很简单提供一个「查天气」的工具返回假数据目的是让你看清 Server 的结构。跑通之后你换成真实接口就行。新建weather_server.pyfrom mcp.server.fastmcp import FastMCP mcp FastMCP(weather-server) mcp.tool() def get_weather(city: str) - str: 查询指定城市的天气。city 是城市名比如 Beijing。 fake_data { Beijing: 晴18°C, Shanghai: 多云22°C, Shenzhen: 小雨26°C, } return fake_data.get(city, f{city} 暂无数据) if __name__ __main__: mcp.run(transportstdio)就这么几行。FastMCP是官方 SDK 提供的高层封装mcp.tool()装饰器把一个普通函数注册成 MCP 工具函数的 docstring 会自动变成工具描述模型就是靠这个描述判断什么时候该调它。所以 docstring 一定要写清楚用途和参数含义别偷懒。启动这个 Serverpython weather_server.py它不会打印什么因为它在等 stdio 输入。这说明 Server 起来了正常。接下来写 Client 端把 Server 的工具拉出来并注入智能体。新建agent.pyimport asyncio import os from langchain_openai import ChatOpenAI from langchain_mcp_adapters.client import MultiServerMCPClient from langgraph.prebuilt import create_react_agent SERVERS { weather: { transport: stdio, command: python, args: [weather_server.py], } } async def build_agent(): client MultiServerMCPClient(SERVERS) tools await client.get_tools() print(floaded {len(tools)} mcp tools) for t in tools: print(f - {t.name}: {t.description}) llm ChatOpenAI( modelgpt-4o-mini, api_keyos.environ[TAOTOKEN_API_KEY], base_urlos.environ[TAOTOKEN_BASE_URL], temperature0.2, ) agent create_react_agent(llm, tools) return agent async def main(): agent await build_agent() resp await agent.ainvoke( {messages: [{role: user, content: 深圳今天天气怎么样}]} ) print(resp[messages][-1].content) if __name__ __main__: asyncio.run(main())这里有几个设计点值得说。MultiServerMCPClient接收一个字典key 是 Server 名字value 是启动配置跟前面config.toml里的结构一致。await client.get_tools()是异步的它会把所有 Server 的工具拉下来并转成 LangChain Tool。注意build_agent是 async 函数因为拉工具列表是异步操作。Python 的__init__不支持异步所以初始化逻辑必须放在异步类方法或异步函数里这是很多人第一次写会卡住的地方。5. 验证工具列表和调用回显跑起来看看python agent.py你应该先看到工具列表输出loaded 1 mcp tools - get_weather: 查询指定城市的天气。city 是城市名比如 Beijing。然后模型会决定调用get_weather传入cityShenzhenServer 返回「小雨26°C」最终输出类似深圳今天是小雨气温 26°C。看到这个回显说明整条链路通了模型 → Client → Server → 工具执行 → 结果回灌 → 模型总结。你可以把get_weather里的假数据换成真实天气 API或者再加几个工具比如查时间、读文件验证多工具场景。如果你想更直观地调试工具调用过程可以在ainvoke后打印完整消息链for msg in resp[messages]: print(type(msg).__name__, getattr(msg, content, ))这样你能看到 AIMessage 里带的 tool_calls确认模型确实选了正确的工具和参数。6. 本篇常见报错排查报错一ModuleNotFoundError: No module named mcp说明 SDK 没装。跑pip install mcp。如果你用的是虚拟环境确认装在了当前环境里。报错二npx: command not found这是用 GitHub 官方 Server 时会遇到的需要 Node.js 环境。装好 Node 后npx就有了。如果你不想装 Node就先用我上面那个 Python 写的 weather Server 练手。报错三工具列表是空的loaded 0 mcp tools最常见的原因是 Server 启动命令写错了或者args里的路径不对。MultiServerMCPClient启动 Server 失败时不一定报错只是拉不到工具。你可以先在终端手动敲一遍command args确认 Server 能起来。报错四401 Unauthorized或模型调用失败检查TAOTOKEN_API_KEY环境变量有没有正确导出base_url是不是https://taotoken.net/api。如果你在代码里直接读os.environ确认运行脚本的终端里export过。报错五RuntimeError: no running event loop说明你在同步上下文里调了异步函数。get_tools()和ainvoke()都是异步的必须放在async def里用await最外层用asyncio.run()包起来。报错六工具被调用了但参数不对多半是 docstring 写得太模糊模型猜错了参数含义。把参数说明写具体比如「city 是城市英文名首字母大写」模型准确率会明显提升。排障时如果怀疑是 Key 或接入配置的问题可以直接去 https://taotoken.net/api-keys 重新生成一个 Key 对比测试排除凭证因素。接入细节和协议兼容性可以查 https://taotoken.net/doc 。跑通这个最小 Server 之后下一步就是把它换成真实工具比如接 GitHub 做仓库操作、接本地文件系统做读写。工具多了之后建议用 Coding Plan 来管理长期的编码和 Agent 任务把模型调用和工具编排统一起来省得每个项目重复配一遍。