
1. 为什么 LangChain 项目接 MCP 总卡在适配层如果你正在用 LangChain 或 LangGraph 搭 Agent大概率遇到过这种尴尬MCP 生态里已经有现成的工具服务比如文件系统、数据库查询、浏览器操作但 LangChain 的BaseTool和 MCP 的Tool是两套接口直接塞进去要么参数对不上要么异步调用链断掉。langchain-mcp-adapters就是专门解决这个断层的小库它把 MCP 服务端的工具转换成 LangChain 规范的StructuredTool让 LangGraph 的create_react_agent能直接消费。这篇面向的是已经写过 LangChain 基础链、想把手头 MCP 服务接进 Agent 的开发者。核心检索词就三个langchain、langgraph、langchain-mcp-adapters。我会从依赖安装讲到多服务器连接再给一次端到端调用验证最后把常见的 401、连接失败、工具加载为空这些坑逐个拆开。整个过程不需要你改 MCP 服务端代码适配层全在客户端完成。先说清楚适配器到底做了什么。MCP 服务端暴露的工具带有name、description、inputSchema而 LangChain 的StructuredTool需要args_schema、coroutine、response_format。convert_mcp_tool_to_langchain_tool这个函数把session.call_tool包成一个协程再把inputSchema直接当args_schema用返回content_and_artifact格式。这意味着工具调用的原始返回会被拆成文本内容和附加产物两部分LangGraph 的 ToolMessage 能正确接住。我试过在一个已有 LangGraph 项目里直接手写适配结果args_schema的 JSON Schema 和 Pydantic 模型对不上工具调用一直报参数校验失败。换成langchain-mcp-adapters之后load_mcp_tools(session)一行就把所有工具转好了省掉大量胶水代码。下面按落地顺序展开。2. TaoToken 前置给适配器一个稳定的模型出口适配器负责工具转换但 Agent 推理还得靠模型。LangGraph 的create_react_agent需要一个BaseChatModel你可以用ChatOpenAI指向任意兼容 OpenAI 接口的服务。这里用 TaoToken 作为模型出口原因是它的接口路径和 OpenAI SDK 完全一致base_url填https://taotoken.net/api就能用不需要额外装 SDK 或改适配器代码。先拿 Key。打开 https://taotoken.net/api-keys 登录后创建一个 API Key复制出来形如sk-开头的一串。这个 Key 后面要填进ChatOpenAI的api_key参数。注意别把 Key 硬编码进提交到 Git 的脚本里用环境变量或者.env文件管理。模型 ID 这块TaoToken 的模型列表在 https://taotoken.net/models 可以查。选一个支持 function calling 的模型因为 ReAct Agent 依赖工具调用能力。如果模型不支持 tool_callscreate_react_agent会一直返回纯文本工具永远不会被触发。这一点在排障章节会再展开。环境变量建议这样设Linux/macOS 用 exportWindows PowerShell 用$env:export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api然后在代码里用os.getenv读取。这样本地调试和 CI 环境可以共用同一份代码只换环境变量。如果你用的是 Claude Code 这类工具做辅助开发它的配置里 Base URL 填https://taotoken.net/apiKey 填同一个Model ID 选支持工具调用的那个三件套保持一致避免调试时怀疑是模型出口的问题。需要说明的是TaoToken 在这里的角色是模型 API 出口不是 MCP 服务端。MCP 服务端仍然是你自己用FastMCP写的那个 Python 进程两者通过适配器在客户端汇合。理清这条链路后面排障时才能快速定位是模型侧还是工具侧的问题。3. 可复制配置依赖、适配器初始化与多服务器连接这一节给可直接粘贴的配置片段。先装依赖建议用虚拟环境隔离python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate pip install langchain-mcp-adapters langgraph langchain-openai mcpmcp包是服务端和客户端共用的langchain-mcp-adapters依赖它。版本上langchain-mcp-adapters0.1.x 对应mcp1.x如果装完 import 报错先pip list看版本是否匹配。先写一个最小 MCP 服务端保存为math_server.pyfrom mcp.server.fastmcp import FastMCP mcp FastMCP(Math) mcp.tool() def add(a: int, b: int) - int: Add two numbers return a b mcp.tool() def multiply(a: int, b: int) - int: Multiply two numbers return a * b if __name__ __main__: mcp.run(transportstdio)再写一个 SSE 传输的天气服务端weather_server.pyfrom mcp.server.fastmcp import FastMCP mcp FastMCP(Weather) mcp.tool() async def get_weather(location: str) - str: Get weather for location. return fIts always sunny in {location} if __name__ __main__: mcp.run(transportsse)客户端配置用MultiServerMCPClient它接受一个字典每个 key 是服务器名value 是连接参数。stdio 用commandargsSSE 用urltransport。下面这段可以直接存成client.pyimport asyncio import os from langchain_openai import ChatOpenAI from langchain_mcp_adapters.client import MultiServerMCPClient from langgraph.prebuilt import create_react_agent model ChatOpenAI( modelgpt-4o, api_keyos.getenv(TAOTOKEN_API_KEY), base_urlos.getenv(TAOTOKEN_BASE_URL, https://taotoken.net/api), ) async def main(): async with MultiServerMCPClient( { math: { command: python, args: [math_server.py], transport: stdio, }, weather: { url: http://localhost:8000/sse, transport: sse, }, } ) as client: tools client.get_tools() print(floaded tools: {[t.name for t in tools]}) agent create_react_agent(model, tools) math_resp await agent.ainvoke( {messages: whats (3 5) x 12?} ) weather_resp await agent.ainvoke( {messages: what is the weather in nyc?} ) print(math_resp[messages][-1].content) print(weather_resp[messages][-1].content) if __name__ __main__: asyncio.run(main())如果你只想接单个 stdio 服务器用stdio_clientClientSession更轻from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client from langchain_mcp_adapters.tools import load_mcp_tools server_params StdioServerParameters( commandpython, args[math_server.py], ) async def single(): 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) print([t.name for t in tools])注意load_mcp_tools必须在session.initialize()之后调用否则拿不到工具列表。这是最常见的顺序错误。另外 SSE 服务端要先启动python weather_server.py会监听 8000 端口客户端才能连上。stdio 服务端不用手动启动stdio_client会以子进程方式拉起。4. 验证请求一次端到端调用链的完整输出配置写完后跑python client.py。先看工具加载那行输出应该打印出[add, multiply, get_weather]。如果这里是空列表说明适配器没拿到工具先别往下走去第 5 节排障。工具加载正常后create_react_agent会把工具描述塞进系统提示模型据此决定调用哪个工具。数学问题(3 5) x 12会触发两次工具调用先add(3, 5)得到 8再multiply(8, 12)得到 96。天气问题触发get_weather(nyc)。验证时不要只看最终文本要把中间消息打出来。在agent.ainvoke返回的messages列表里你会看到这样的序列HumanMessage→AIMessage带tool_calls→ToolMessage工具结果→AIMessage最终回答。下面是一段打印中间步骤的代码resp await agent.ainvoke({messages: whats (3 5) x 12?}) for msg in resp[messages]: print(f[{msg.type}] {getattr(msg, content, )}) if hasattr(msg, tool_calls) and msg.tool_calls: for tc in msg.tool_calls: print(f - call {tc[name]} with {tc[args]})预期输出里AIMessage的tool_calls会包含add和multiply两个调用ToolMessage的content分别是8和96。如果tool_calls为空但最终回答直接给了数字说明模型没走工具可能是模型不支持 function calling或者工具描述没被正确注入。成功标志有三个工具列表非空、tool_calls出现、ToolMessage内容与工具返回值一致。三个都满足说明适配器链路完全打通。这时候你可以把math_server.py换成自己的业务工具比如查数据库、调内部 API只要用mcp.tool()装饰客户端不用改。如果 SSE 服务器连不上先确认weather_server.py已经启动且端口没被占用。stdio 服务器如果报FileNotFoundError检查args里的路径是绝对路径还是相对路径相对路径是相对于客户端进程的工作目录不是脚本所在目录。建议统一用绝对路径避免踩这个坑。5. 本篇常见错排查401、连接失败与工具为空排障按错误现象分。第一类是模型侧报错。如果你看到401 Unauthorized或AuthenticationError先检查TAOTOKEN_API_KEY是否设置成功echo $TAOTOKEN_API_KEY看有没有值。再确认base_url是https://taotoken.net/api末尾不要多加/v1OpenAI SDK 会自己拼路径。如果 Key 正确但仍 401去 https://taotoken.net/api-keys 确认 Key 没过期、额度没用完。第二类是 MCP 连接失败。stdio 场景常见报错是FileNotFoundError: [Errno 2] No such file or directory原因是commandpython在部分环境里找不到换成sys.executable更稳import sys server_params StdioServerParameters( commandsys.executable, args[/abs/path/math_server.py], )SSE 场景常见Connection refused或local proxy failed先确认服务端进程在跑curl http://localhost:8000/sse看有没有响应。如果服务端在容器里客户端在宿主机localhost要换成容器 IP 或映射端口。WebSocket 传输同理检查ws://地址和端口。第三类是工具加载为空。load_mcp_tools返回[]通常是session.initialize()没调用或者服务端mcp.tool()装饰的函数有语法错误导致注册失败。把服务端单独跑一遍看启动日志有没有报错。另一个原因是inputSchema里有 LangChain 不支持的字段类型比如嵌套的anyOf这种情况适配器会跳过该工具。简化参数类型为基本类型能规避。第四类是reading choices相关报错通常出现在模型返回格式不符合预期时。检查模型是否支持tool_calls不支持的话换一个。如果用的是ChatOpenAI但模型 ID 写错也会返回非结构化内容。去 https://taotoken.net/models 核对模型 ID 拼写。第五类是 OAuth 或鉴权相关报错。MCP 服务端如果配了鉴权客户端连接参数里要带headers或auth。MultiServerMCPClient的 SSE 连接支持headers字段weather: { url: http://localhost:8000/sse, transport: sse, headers: {Authorization: Bearer your-token}, }stdio 场景的鉴权一般通过环境变量传给子进程在StdioServerParameters里加env字段。如果报OAuth token expired重新生成 token 再试。排障时建议开日志。在客户端脚本开头加import logging logging.basicConfig(levellogging.DEBUG)这样能看到 MCP 协议的握手过程和工具列表的原始返回定位问题比猜快得多。如果日志里tools/list返回了工具但客户端拿到的是空那就是适配器转换阶段的问题检查工具名是否含特殊字符。6. 把适配器接进你现有的 LangGraph 工作流跑通最小示例后下一步是替换成你自己的 MCP 服务。如果你已经有 LangGraph 的StateGraph把create_react_agent换成你的图工具列表还是从client.get_tools()拿。适配器输出的StructuredTool和 LangChain 原生工具完全兼容可以直接塞进ToolNode。长期做 Agent 开发的话模型出口的稳定性比工具数量更重要。TaoToken 的 Coding Plan 适合需要持续调用模型的场景配置入口在 https://taotoken.net/coding-plan Base URL 和 Key 与前面一致。如果你只是想先验证模型对话效果https://taotoken.net/chat 可以直接试。接入文档在 https://taotoken.net/doc 里面有各语言 SDK 的配置示例。最后给一个实用技巧把 MCP 服务端的连接参数抽成独立的mcp_config.json客户端启动时读取这样切换环境不用改代码。格式如下{ math: { command: python, args: [/abs/path/math_server.py], transport: stdio }, weather: { url: http://localhost:8000/sse, transport: sse } }读取时json.load后直接传给MultiServerMCPClient。这样本地、测试、生产三套配置分开管理排障时也能快速确认连的是哪个服务端。适配器本身不复杂复杂的是连接参数和模型出口的配合把这两块配置化后面加工具就是改 JSON 的事。