to_langchain_tools () 完整深度详解(MCP 客户端工具转换核心方法)

官方库langchain-mcp-adapters中标准实现名为load_mcp_tools,自定义封装命名to_langchain_tools功能完全对齐官方 MCP→LangChain 工具转换逻辑,是 MCP Client 与 LangChain 工具生态的协议适配桥梁

一、方法基础定义

python

运行

def to_langchain_tools(self) -> list[BaseTool]:
  • 归属:自定义MCPClient实例方法,绑定 MCP 客户端连接信息(服务地址、RPC 会话);
  • 入参:无入参,内部依赖self.list_tools()self.call_tool()两个 MCP 底层 RPC 方法;
  • 出参list[BaseTool],LangChain 标准工具列表,原生兼容llm.bind_tools()、Runnable 执行器、LCEL 全链路;
  • 核心定位协议翻译器:MCP 服务下发 JSON 格式工具元数据 → LangChain 可被大模型识别、可被框架调度的标准 Tool 实例。

整体数据流

plaintext

MCP Server → JSON-RPC tools/list → 原始工具JSON(name/desc/inputSchema) → to_langchain_tools → BaseTool[] → bind_tools注入LLM → LLM生成tool_call → RunnableLambda执行→_run内部调用MCP tools/call远程RPC

二、逐行源码拆解 & 原理

python

运行

from langchain_core.tools import BaseTool, Tool

def to_langchain_tools(self) -> list[BaseTool]:
    # 步骤1:MCP标准RPC:拉取远端全量工具元信息
    mcp_tools_raw = self.list_tools()
    lc_tools = []

    # 步骤2:遍历单个MCP工具描述JSON
    for item in mcp_tools_raw:
        name = item["name"]                # 工具唯一标识(大模型调用name)
        desc = item["description"]          # 工具说明(LLM决策是否调用依据)
        params_schema = item["inputSchema"]# JSON参数约束(对应Function Calling入参schema)

        # 步骤3:闭包封装远程执行函数,解决循环变量捕获陷阱
        def create_tool(n=name):
            def _run(**kwargs):
                # 关键:不执行本地逻辑,转发MCP RPC请求至远端服务
                return self.call_tool(n, kwargs)
            return _run

        # 步骤4:实例化LangChain原生Tool对象
        lc_tool = Tool(
            name=name,
            description=desc,
            func=create_tool()
        )
        lc_tools.append(lc_tool)
    return lc_tools

1. self.list_tools():MCP 工具发现

发送MCP JSON-RPC 2.0 标准报文tools/list,从远程 MCP 服务获取工具目录,返回结构(MCP 协议强制规范):

json

[{
    "name": "calc_add",
    "description": "两数加法计算器,输入a、b返回求和结果",
    "inputSchema": {
        "type": "object",
        "properties": {"a":{"type":"number"},"b":{"type":"number"}},
        "required": ["a","b"]
    }
}]

注意:此处只有工具说明书,无任何业务执行代码,真正运算逻辑运行在独立 MCP 服务进程 / 远端服务器,实现工具与 Agent 代码解耦。

2. 三元组提取(bind_tools 核心三要素)

name/description/inputSchema是 LangChain 绑定大模型的必传三元组,和本地@tool自动解析函数名、文档注释、参数注解逻辑一一对应:

  1. name:大模型tool_calls["name"]匹配字段,全局唯一;
  2. description:Prompt 内嵌工具说明,LLM 根据用户问题判断何时触发工具;
  3. inputSchema:JSON 参数规范,约束大模型生成结构化参数,MCP 服务端依据该 Schema 做参数校验。

3. 闭包create_tool(n=name)关键设计

问题背景:Python for 循环变量泄漏

若直接def _run(): self.call_tool(name,kwargs),循环结束后所有_run都会复用循环最后一个name,导致全量工具调用同一个远端接口。

解决方案:默认参数固化n=name
  • 外层函数create_tool(n=name):循环迭代时把当前循环的name固化为形参默认值;
  • 内层_run(**kwargs):LangChain 执行工具时触发,内部不跑本地代码,调用call_tool发送 MCP tools/call RPC 网络请求

本地工具 VS MCP 封装工具本质区别

  • 本地 @tool:_run= 本地 Python 业务逻辑;
  • MCP 包装工具:_run=MCP 网络 RPC 代理。

4. Tool 实例化:伪装成本地原生工具

Tool(name,description,func=xxx)生成BaseTool实例,完全符合 LangChain 工具抽象规范:

  • 对外:LLM、Chain、Agent无法区分工具是本地函数还是远程 MCP 服务
  • 对内:func是代理入口,工具执行时自动穿透到 MCP 网络调用。

三、与官方 langchain-mcp-adapters 差异对照

官方库标准转换函数:load_mcp_tools(session)(异步 async)、convert_mcp_tool_to_langchain_tool()(单工具转换):

  1. 官方逻辑:MCP inputSchema → json_schema_to_pydantic → Pydantic 模型 → StructuredTool(带 args_schema 结构化参数校验);
  2. 自定义 to_langchain_tools:简化版,直接用基础 Tool,舍弃 Pydantic 自动生成,轻量化封装,HTTP/Stdio 传输通用;
  3. 使用对齐:两者返回的工具列表均可直接llm.bind_tools(tools),上层业务代码无修改。

四、接入 LangChain 全链路流程(bind_tools+RunnableLambda)

python

运行

# 1、初始化客户端转换工具
client = MCPClient("http://127.0.0.1:8080/mcp")
mcp_tools = client.to_langchain_tools()

# 2、绑定大模型(和本地工具写法完全一致)
llm = ChatOpenAI(model="gpt-3.5-turbo",temperature=0)
llm_bind = llm.bind_tools(mcp_tools)

# 3、统一执行节点:RunnableLambda通用执行器,不分本地/MCP远程
def tool_exec(msg):
    tool_map={t.name:t for t in mcp_tools}
    for call in msg.tool_calls:
        res=tool_map[call["name"]].run(call["args"])
    return res
exec_node = RunnableLambda(tool_exec)

# LCEL链路
chain = prompt | llm_bind | exec_node

链路分工对应之前定义

  1. bind_tools:接收to_langchain_tools输出的工具列表,把工具元数据塞给 LLM,让模型感知工具;
  2. RunnableLambda:执行节点调用tool.run(),内部触发_run→MCP 远程调用; to_langchain_tools 承上启下:承接 MCP 协议、适配 LangChain 框架

五、四大核心工程价值

  1. 无缝兼容存量 LangChain 架构 现有基于bind_tools+RunnableLambda的 Agent、LCEL、LangGraph 代码零改动即可接入分布式 MCP 远程工具,本地自定义工具和 MCP 工具可拼接同一张工具列表:

python

运行

all_tools = local_custom_tool + client.to_langchain_tools()
llm.bind_tools(all_tools)
  1. 动态热发现工具 MCP 服务新增 / 下线工具,只需重新执行to_langchain_tools()拉取最新列表,Agent 代码不用修改、不用重新打包部署,仅切换 MCP 服务地址即可切换整套工具集。
  2. 前后端解耦(MCP 核心优势落地)
  • MCP Server:Go/Java/Python 任意语言独立开发、独立部署、独立迭代;
  • LangChain Agent:只依赖 MCP 标准接口,不用对接第三方 SDK;
  • to_langchain_tools屏蔽底层传输(HTTP/SSE/Stdio)、协议细节。
  1. 统一工具治理 多台异构 MCP 服务(文件服务、数据库服务、第三方 API 服务),分别实例化 MCPClient、各自调用to_langchain_tools,汇总为统一工具池给大模型使用。

六、优化拓展方案(进阶改造)

优化 1:升级为 StructuredTool(对齐官方规范,增加参数运行时校验)

MCP 的inputSchema转为 Pydantic 模型,填入args_schema,实现入参自动校验:

python

运行

from langchain_core.tools import StructuredTool
from pydantic import create_model, Field
import json

def schema_to_pydantic(schema:dict):
    props=schema["properties"]
    fields={k:(eval(v["type"]),Field(...,description=v.get("description",""))) for k,v in props.items()}
    return create_model("DynamicArgs",**fields)

# 替换Tool实例化
args_model = schema_to_pydantic(params_schema)
lc_tool=StructuredTool(name=name,description=desc,args_schema=args_model,func=create_tool())

优化 2:支持异步(适配 async MCP Stdio 客户端)

封装ato_langchain_tools(),配合 aiohttp 异步 RPC,适配官方 Stdio 异步 MCP Server。

七、一句话总结

to_langchain_tools() = MCP 协议→LangChain 工具的标准化适配器,把跨网络、跨语言的远端 MCP 服务伪装成 LangChain 原生本地工具,是 MCP 落地 LangChain 生态最关键的转换层函数。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐