ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

langchain_mcp_adapters 多服务接入:用 MultiServerMCPClient 把 MCP 端点改到 TaoToken

langchain_mcp_adapters 多服务接入:用 MultiServerMCPClient 把 MCP 端点改到 TaoToken 1. 多服务 MCP 接入的真实痛点为什么单客户端配置会崩如果你正在用 LangGraph 编排 Agent大概率已经踩过这个坑一个MultiServerMCPClient里塞了 GitHub、SQL、文件系统三个 MCP 服务本地跑得好好的一换机器或者一上 CI 就报local proxy failed或者401 Unauthorized。问题往往不在你的 Agent 逻辑而在每个 MCP 端点各自为政的鉴权方式——有的走 stdio 本地进程有的走远程 HTTP有的还要单独配一套 Key。langchain_mcp_adapters里的MultiServerMCPClient本身设计得很干净你给它一个字典key 是服务别名value 是传输配置它负责把每个 MCP Server 暴露的工具转成 LangChain 的Tool对象再交给create_react_agent去调度。但干净的前提是每个端点的连接参数都正确。多服务场景下最容易出问题的三个地方是第一远程 HTTP 端点的 Base URL 和鉴权头不统一。你从社区拿到的 MCP 服务示例有的写http://127.0.0.1:8000/mcp有的写https://some-host/sse鉴权有的放Authorization头有的放 query 参数。多服务混在一起时你很难记住每个端点的规则。第二stdio 模式在不同操作系统上的行为差异。macOS 上 Python 脚本可能被 Gatekeeper 拦Linux 上command路径不对直接FileNotFoundErrorWindows 上又是另一套。第三工具名冲突。两个 MCP 服务都暴露了read_fileget_tools()返回的列表里就会出现两个同名 ToolLangGraph 调度时行为不可预期。这篇要解决的问题就是把多个 MCP 端点的连接配置统一到一条 Key/API 通道上让MultiServerMCPClient的配置片段可以复制即用并且附一次真实的工具列表拉取和调用验证。适合已经会用 LangGraph 写 ReAct Agent、但被多服务配置卡住的开发者。2. TaoToken 前置统一 Key 与 API 通道的接入准备在改MultiServerMCPClient配置之前先把统一通道准备好。TaoToken 在这里扮演的角色是给你的多个 MCP 远程端点提供一个统一的 Base URL 和 Key这样你不需要为每个 MCP 服务单独申请凭证也不用在代码里散落一堆不同的鉴权逻辑。你需要先拿到两样东西一个 API Key和一个统一的 Base URL。API Key 在控制台的 API Keys 页面创建地址是https://taotoken.net/api-keys带归因参数完整链接https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite。创建时建议按用途命名比如langgraph-mcp-dev方便后面排查是哪个环境在用。Base URL 统一用https://taotoken.net/api注意这个地址不带任何 UTM 参数直接写进配置里。如果你要确认当前支持的模型 ID 和通道状态可以打开模型对话页面https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite做一次快速对话验证确认 Key 本身是通的再去配 MCP。这里有个容易忽略的点MultiServerMCPClient的远程 HTTP 传输走的是 MCP 协议本身的 JSON-RPC不是 OpenAI 兼容接口。所以你不能把https://taotoken.net/api直接当成ChatOpenAI的base_url塞进去——那是给 LLM 用的。MCP 端点的 URL 需要指向具体的 MCP 服务路径而 TaoToken 的统一通道在这里的作用是给这些 MCP 服务提供一致的鉴权入口。换句话说你的 LLM 调用和 MCP 工具调用可以共用同一个 Key但配置位置不同。如果你打算长期跑 Agent 任务而不是一次性验证建议看一下 Coding Plan 页面https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite它更适合持续性的编码和 Agent 场景配额和稳定性比按次调用更可控。环境变量准备export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api把这两个变量写进.env后面配置片段里直接引用避免硬编码。3. 可复制配置MultiServerMCPClient 多服务连接片段这一节给出可以直接复制的配置。核心思路是把远程 MCP 端点的 URL 和鉴权头统一成一套模板stdio 本地服务单独处理然后用prefixToolNameWithServerNameTrue解决工具重名。先看完整的 Python 配置片段包含三个服务一个远程 HTTP 的 SQL MCP、一个远程 HTTP 的搜索 MCP、一个本地 stdio 的文件系统 MCP。import asyncio import os from dotenv import load_dotenv from langchain_openai import ChatOpenAI from langchain_mcp_adapters.client import MultiServerMCPClient from langgraph.prebuilt import create_react_agent load_dotenv() TAOTOKEN_KEY os.getenv(TAOTOKEN_API_KEY) TAOTOKEN_BASE os.getenv(TAOTOKEN_BASE_URL) async def main(): client MultiServerMCPClient( { sql: { transport: streamable_http, url: https://taotoken.net/api/mcp/sql, headers: { Authorization: fBearer {TAOTOKEN_KEY}, Content-Type: application/json, }, }, search: { transport: streamable_http, url: https://taotoken.net/api/mcp/search, headers: { Authorization: fBearer {TAOTOKEN_KEY}, Content-Type: application/json, }, }, filesystem: { transport: stdio, command: python, args: [/abs/path/filesystem_mcp.py], env: {TAOTOKEN_API_KEY: TAOTOKEN_KEY}, }, }, prefixToolNameWithServerNameTrue, ) tools await client.get_tools() print(工具列表, [t.name for t in tools]) llm ChatOpenAI( modelgpt-4o, temperature0, base_urlTAOTOKEN_BASE, api_keyTAOTOKEN_KEY, ) agent create_react_agent(llm, tools) result await agent.ainvoke( {messages: [(user, 列出当前数据库里的表名)]} ) print(result[messages][-1].content) await client.close() if __name__ __main__: asyncio.run(main())关键参数说明用表格对照参数作用多服务场景建议transport指定通信方式远程用streamable_http本地用stdiourl远程 MCP 端点地址统一走 TaoToken 通道路径区分服务headers.Authorization鉴权头所有远程服务共用同一个 Keycommand/argsstdio 启动命令用绝对路径避免 cwd 问题prefixToolNameWithServerName工具名加服务前缀生产环境必须开避免重名如果你用的是 Cline 或 Claude Code 这类客户端配置形态会不一样。Cline 的 MCP 配置是 JSON放在cline_mcp_settings.json里{ mcpServers: { sql: { url: https://taotoken.net/api/mcp/sql, headers: { Authorization: Bearer ${TAOTOKEN_API_KEY} } }, search: { url: https://taotoken.net/api/mcp/search, headers: { Authorization: Bearer ${TAOTOKEN_API_KEY} } } } }Claude Code 的配置走~/.claude/settings.json或项目级.mcp.json形态类似但字段名是mcpServers下的command/args或url。Codex 的auth.json则是另一套结构主要放OPENAI_API_KEY和base_urlMCP 部分单独在mcp_servers字段里配。三件套始终是Base URL、Key、Model ID缺一个都会在验证阶段报错。4. 验证请求工具列表拉取与一次真实调用配置写完不算完必须验证两件事工具列表能不能拉全以及工具能不能被 Agent 真正调用。很多人卡在配置看起来对但get_tools()返回空列表。第一步单独跑工具列表拉取不要一上来就跑 Agentasync def check_tools(): client MultiServerMCPClient( { sql: { transport: streamable_http, url: https://taotoken.net/api/mcp/sql, headers: {Authorization: fBearer {TAOTOKEN_KEY}}, }, }, prefixToolNameWithServerNameTrue, ) tools await client.get_tools() for t in tools: print(fname{t.name} desc{t.description[:60]}) await client.close() asyncio.run(check_tools())预期输出类似namesql_list_tables desc列出当前数据库中的所有表 namesql_describe_table desc返回指定表的字段结构 namesql_run_query desc执行只读 SQL 查询如果这里返回空列表先别改 Agent 代码去查 MCP 端点本身。用 curl 直接打一次 MCP 的初始化请求curl -X POST https://taotoken.net/api/mcp/sql \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d {jsonrpc:2.0,id:1,method:tools/list,params:{}}正常会返回一个 JSON-RPC 响应result.tools里是工具数组。如果这里就报 401说明 Key 或 header 格式有问题如果报 404说明 URL 路径不对。第二步跑一次真实调用。用create_react_agent加一个明确的任务result await agent.ainvoke( {messages: [(user, 查询 users 表的前 5 行只返回 id 和 name)]} ) print(result[messages][-1].content)成功的话你会看到 Agent 先调用sql_run_query拿到结果后再用 LLM 组织成自然语言。中间过程可以通过result[messages]逐条打印确认工具调用确实发生了而不是 LLM 在编答案。实测下来多服务场景最容易在工具列表拉到了但调用时报reading choices错误这一步翻车。这个报错通常不是 MCP 的问题而是 LLM 返回格式和 Agent 预期不匹配检查ChatOpenAI的base_url是否指向了正确的通道以及model名是否在通道支持列表里。5. 常见报错排查401、local proxy failed、reading choices、OAuth这一节按真实报错对照排查。每个报错给出触发条件和修复动作。401 Unauthorized最常见。触发条件通常是 header 里 Key 没带、Key 过期、或者 Key 前面多了Bearer之外的空格。检查headers字典里Authorization的值确保是Bearer sk-xxx格式。如果你用的是环境变量确认.env被load_dotenv()正确加载os.getenv返回的不是None。local proxy failed这个报错多出现在 stdio 模式。触发条件是command或args路径不对或者 Python 解释器不在 PATH 里。修复把command改成绝对路径比如/usr/bin/python3args里的脚本路径也用绝对路径。macOS 上如果报无法验证此 APP 是否包含恶意软件执行xattr -cr /abs/path/filesystem_mcp.pyreading choices 相关报错通常形如KeyError: choices或AttributeError: NoneType object has no attribute choices。触发条件是 LLM 返回体不是 OpenAI 兼容格式或者base_url配错。检查ChatOpenAI的base_url是否指向https://taotoken.net/api以及model参数是否是通道支持的 ID。如果你在模型对话页面能正常对话但代码里报这个错多半是base_url末尾多了或少了斜杠。OAuth 相关报错形如OAuth token expired或invalid_grant。触发条件是某些 MCP 服务要求 OAuth 流程而你只配了静态 Key。修复确认该 MCP 服务是否支持静态 Key 鉴权如果不支持需要走 OAuth 授权流程或者换用支持 Key 鉴权的等价服务。TaoToken 通道下的 MCP 端点默认走 Key 鉴权不需要额外 OAuth。工具重名导致调度异常不报错但 Agent 行为诡异。触发条件是多个 MCP 服务暴露同名工具且prefixToolNameWithServerNameFalse。修复初始化时显式传prefixToolNameWithServerNameTrue工具名会变成sql_run_query、search_run_query这种带前缀的形式。stdio 服务在 CI 里启动失败触发条件是 CI 环境没有对应的 Python 依赖。修复在 CI 配置里先pip install该 MCP 服务的依赖或者改用远程 HTTP 传输把 stdio 服务部署成独立进程。排查顺序建议先 curl 打 MCP 端点确认通道通再跑get_tools()确认工具列表最后跑 Agent 确认调用链。每一步单独验证不要混在一起调。6. 语义一致 CTA把配置落到你的项目里到这里MultiServerMCPClient的多服务配置、工具列表拉取、真实调用验证和常见报错排查都过了一遍。接下来最直接的动作是把上面的配置片段复制到你的项目里把url换成你实际要接的 MCP 服务路径Key 用你自己的。如果你还没创建 Key去 API Keys 页面建一个https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite。创建后先别急着写代码用模型对话页面https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite发一条消息确认 Key 和通道是通的再回到MultiServerMCPClient配置。接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite里面有各传输方式的参数说明和示例配 stdio 或 streamable_http 时对照着看能少走弯路。如果你要接的是 Claude Code 或 Anthropic 风格的 MCP 服务参考https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecode_anthropicutm_campaignrewrite里的配置形态。长期跑 Agent 任务的话Coding Plan 页面https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite更适合持续性场景。控制台在https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite可以看调用量和配额。最后一个实操建议把MultiServerMCPClient的配置抽成一个独立的mcp_config.py用函数返回字典不同环境本地、CI、生产通过环境变量切换 URL 和 Key。这样你的 Agent 代码不用动只改配置就能换端点。工具列表拉取也单独写成一个check_mcp.py脚本每次改配置后先跑它确认工具列表正常再跑 Agent。这个习惯能帮你把排查时间从半小时压到两分钟。
返回列表