langchain & langgraph 快速集成mcp: langchain-mcp-adapters

一、引言

在自然语言处理和人工智能应用中,LangChain 和 LangGraph 是两个强大的工具,它们分别提供了构建复杂语言模型应用和可视化图编程的能力。而 Anthropic 模型上下文协议(MCP)则为模型与外部工具之间的交互提供了一种标准化的方式。langchain-mcp-adapters 库的出现,使得 MCP 工具能够无缝集成到 LangChain 和 LangGraph 中,为开发者提供了更多的工具选择和更灵活的应用开发方式。
在这里插入图片描述

二、技术背景

2.1 LangChain

LangChain 是一个用于开发由语言模型驱动的应用程序的框架,它提供了一系列工具和组件,帮助开发者更轻松地构建和管理与语言模型的交互。LangChain 的核心功能包括工具集成、提示管理、记忆管理等,使得开发者能够构建复杂的对话系统、自动化任务等。

2.2 LangGraph

LangGraph 是一个基于图的编程工具,它允许开发者以可视化的方式构建和管理复杂的语言模型应用。通过 LangGraph,开发者可以将不同的组件(如语言模型、工具、记忆等)连接起来,形成一个流程图,从而实现更复杂的应用逻辑。

2.3 Anthropic 模型上下文协议(MCP)

MCP 是 Anthropic 提出的一种协议,用于定义模型与外部工具之间的交互方式。通过 MCP,模型可以调用外部工具执行特定的任务,并获取工具的执行结果。MCP 提供了标准化的接口和数据格式,使得不同的工具能够与模型进行无缝交互。

三、langchain-mcp-adapters 库概述

langchain-mcp-adapters 是一个功能强大的轻量级包装库,旨在让 Anthropic 模型上下文协议(MCP)工具能够与 LangChain 和 LangGraph 无缝兼容。该库具备以下显著特性:

3.1 工具转换能力

能够将 MCP 工具高效转换为 LangChain 工具。这意味着原本基于 MCP 协议开发的工具,可以直接在 LangChain 的生态系统中使用,为开发者提供了更多的工具选择,拓展了 LangChain 的功能边界。例如,在处理复杂的自然语言处理任务时,可以利用 MCP 提供的特定工具,并将其集成到 LangChain 的工作流中。 转换后的工具可以与 LangGraph 代理协同工作。LangGraph 以可视化的方式构建和管理复杂的语言模型应用,通过 langchain-mcp-adapters 转换的工具可以轻松融入到 LangGraph 的流程图中,实现更复杂的应用逻辑。

3.2 多服务器支持

提供了客户端实现,允许连接到多个 MCP 服务器。这在实际应用中非常有用,因为不同的 MCP 服务器可能提供不同类型的工具和服务,开发者可以根据需求从多个服务器加载工具,丰富自己的工具集。 支持多种传输协议,如标准输入输出(stdio)、服务器发送事件(SSE)和 WebSocket。不同的传输协议适用于不同的场景,开发者可以根据服务器的配置和网络环境选择最合适的传输方式。

3.3 提示和资源管理

支持从 MCP 服务器获取提示和资源。在自然语言处理任务中,提示信息对于引导语言模型生成准确的结果至关重要,而资源则可以为模型提供更多的背景知识。 能够将从 MCP 服务器获取的提示和资源转换为 LangChain 可用的格式,方便开发者在 LangChain 中使用这些提示和资源来优化模型的输出。

四、集成方案详细介绍

4.1 安装

首先,需要安装 langchain-mcp-adapters 库以及相关的依赖:

pip install langchain-mcp-adapters langgraph langchain-openai

同时,需要设置相应的 API 密钥:

export OPENAI_API_KEY=<your_api_key>

4.2 单个 MCP 服务器集成

4.2.1 服务器端代码

以下是一个简单的 MCP 服务器示例,它提供了加法和乘法两个工具:

# math_server.py
from 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(transport="stdio")
4.2.2 客户端代码

客户端代码通过 langchain-mcp-adapters 库连接到 MCP 服务器,并加载工具:

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

from langchain_mcp_adapters.tools import load_mcp_tools
from langgraph.prebuilt import create_react_agent

from langchain_openai import ChatOpenAI

import asyncio

model = ChatOpenAI(
    model="gpt-4o",
    api_key="sk-xxx",
    base_url="https://xxx.openai.com/v1"
)

server_params = StdioServerParameters(
    command="python",
    args=["D:/match_server.py"],
)

async def main():
    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(tools)
            print("---" * 20)
            agent = create_react_agent(model, tools)
            agent_response = await agent.ainvoke({"messages": "what's (3 + 5) x 12?"})
            print(agent_response)
            
            print("---" * 20)
           
            for message in agent_response['messages']:
                print("---" * 20)
                print(message.type + ":" + message.content)
  
        
        
if __name__ == "__main__":
    asyncio.run(main())

在上述代码中,首先创建了一个 StdioServerParameters 对象,用于指定连接到 MCP 服务器的参数。然后,使用 stdio_client 建立与服务器的连接,并创建一个 ClientSession 对象。接着,调用 load_mcp_tools 函数从服务器加载工具,并将这些工具传递给 create_react_agent 函数创建一个 LangGraph 代理。最后,调用代理的 ainvoke 方法执行任务。
执行结果:
在这里插入图片描述
可以看到

tools = await load_mcp_tools(session)

把mcp server的tools直接加载成Langchain规范的tools:

[StructuredTool(name='add', description='Add two numbers', args_schema={'properties': {'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}, 'required': [
'a', 'b'], 'title': 'addArguments', 'type': 'object'}, response_format='content_and_artifact', coroutine=<function convert_mcp_tool_to_langchain_tool.<locals>.call_tool at 0x0000017
A659C5EE0>), StructuredTool(name='multiply', description='Multiply two numbers', args_schema={'properties': {'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'int
eger'}}, 'required': ['a', 'b'], 'title': 'multiplyArguments', 'type': 'object'}, response_format='content_and_artifact', coroutine=<function convert_mcp_tool_to_langchain_tool.<loc
als>.call_tool at 0x0000017A659C6020>)]
 agent = create_react_agent(model, tools)
 agent_response = await agent.ainvoke({"messages": "what's (3 + 5) x 12?"})

使用Langgraph的create_react_agent创建ReAct agent,并把tools传进去,让模型推理和调用工具最终得出计算结果,模型的推理生成过程为:

{'messages': [HumanMessage(content="what's (3 + 5) x 12?", additional_kwargs={}, response_metadata={}, id='e46c2e26-44b2-4644-a8bd-f2f739fff952'), AIMessage(content='', additional_k
wargs={'tool_calls': [{'id': 'call_Mg32AEKQgCoOZjzJELcjG1Oc', 'function': {'arguments': '{"a": 3, "b": 5}', 'name': 'add'}, 'type': 'function'}, {'id': 'call_AHwhHVK5KgT1flLpSX6UxEv
4', 'function': {'arguments': '{"a": 8, "b": 12}', 'name': 'multiply'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 51, 'prompt_t
okens': 77, 'total_tokens': 128, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_t
okens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_ee1d74bde0', 'id': 'chatcmpl-BND7liLFJOOi57l8fUiMHWcWCwGvp', '
finish_reason': 'tool_calls', 'logprobs': None}, id='run-ade9386f-d7de-4b31-a1a4-1dfd32e8ed64-0', tool_calls=[{'name': 'add', 'args': {'a': 3, 'b': 5}, 'id': 'call_Mg32AEKQgCoOZjzJE
LcjG1Oc', 'type': 'tool_call'}, {'name': 'multiply', 'args': {'a': 8, 'b': 12}, 'id': 'call_AHwhHVK5KgT1flLpSX6UxEv4', 'type': 'tool_call'}], usage_metadata={'input_tokens': 77, 'ou
tput_tokens': 51, 'total_tokens': 128, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='8', name='a
dd', id='17177876-4603-465d-90aa-0fe52ca9ac70', tool_call_id='call_Mg32AEKQgCoOZjzJELcjG1Oc'), ToolMessage(content='96', name='multiply', id='5ffe2b1c-d3dd-49fb-8fd3-31d3250efdb6',
tool_call_id='call_AHwhHVK5KgT1flLpSX6UxEv4'), AIMessage(content='The result of \\((3 + 5) \\times 12\\) is \\(96\\).', additional_kwargs={'refusal': None}, response_metadata={'toke
n_usage': {'completion_tokens': 23, 'prompt_tokens': 143, 'total_tokens': 166, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens':
0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_ee1d74bde0', 'id
': 'chatcmpl-BND7mLW86X1uCu83LdV4BInr6uaDz', 'finish_reason': 'stop', 'logprobs': None}, id='run-696d75ea-aeac-46cc-8971-51117ffcae02-0', usage_metadata={'input_tokens': 143, 'outpu
t_tokens': 23, 'total_tokens': 166, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}

4.3 多个 MCP 服务器集成

4.3.1 服务器端代码

除了上述的数学服务器,还可以创建一个天气服务器:

# weather_server.py
from typing import List
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Weather")

@mcp.tool()
async def get_weather(location: str) -> str:
    """Get weather for location."""
    return "It's always sunny in New York"

if __name__ == "__main__":
    mcp.run(transport="sse")
4.3.2 客户端代码

客户端代码使用 MultiServerMCPClient 类连接到多个 MCP 服务器,并加载所有工具:

from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
from langchain_mcp_adapters.client import MultiServerMCPClient

import asyncio

model = ChatOpenAI(
    model="gpt-4o",
    api_key="sk-xxx",
    base_url="https://xxx.openai.com/v1"
)

async def main():
    
    async with MultiServerMCPClient(
        {
            "math": {
                "command": "python",
                "args": ["D:/math_server.py"],
                "transport": "stdio",
            },
            "weather": {
                "url": "http://localhost:8000/sse",
                "transport": "sse",
            }
        }
    ) as client:
        agent = create_react_agent(model, client.get_tools())
        math_response = await agent.ainvoke({"messages": "what's (3 + 5) x 12?"})
        weather_response = await agent.ainvoke({"messages": "what is the weather in nyc?"})
    
        print(math_response)
        
        print("---" * 20)
        
        print(weather_response)    
        
if __name__ == "__main__":
    asyncio.run(main())

在上述代码中,使用 MultiServerMCPClient 类的构造函数指定多个 MCP 服务器的连接参数。然后,通过 client.get_tools() 方法获取所有服务器的工具,并将这些工具传递给 create_react_agent 函数创建代理。最后,分别调用代理的 ainvoke 方法执行数学和天气查询任务。

测试输出结果:
在这里插入图片描述
可以看到两次调用分别推理调用了math tool 和 weather tool,得到最终的结果。

五、langchain-mcp-adapters 核心代码分析

5.1 工具转换

langchain-mcp-adapters 库中的 convert_mcp_tool_to_langchain_tool 函数用于将 MCP 工具转换为 LangChain 工具:

def convert_mcp_tool_to_langchain_tool(
    session: ClientSession,
    tool: MCPTool,
) -> BaseTool:
    async def call_tool(
        **arguments: dict[str, Any],
    ) -> tuple[str | list[str], list[NonTextContent] | None]:
        call_tool_result = await session.call_tool(tool.name, arguments)
        return _convert_call_tool_result(call_tool_result)

    return StructuredTool(
        name=tool.name,
        description=tool.description or "",
        args_schema=tool.inputSchema,
        coroutine=call_tool,
        response_format="content_and_artifact",
    )

该函数接受一个 ClientSession 对象和一个 MCPTool 对象作为参数,返回一个 StructuredTool 对象。在 call_tool 函数中,调用 session.call_tool 方法执行 MCP 工具,并将结果转换为 LangChain 工具所需的格式。

5.2 多服务器客户端

MultiServerMCPClient 类用于连接到多个 MCP 服务器,并管理这些服务器的会话和工具:

class MultiServerMCPClient:
    def __init__(
        self,
        connections: dict[str, StdioConnection | SSEConnection | WebsocketConnection] | None = None,
    ) -> None:
        self.connections = connections or {}
        self.exit_stack = AsyncExitStack()
        self.sessions: dict[str, ClientSession] = {}
        self.server_name_to_tools: dict[str, list[BaseTool]] = {}

    async def _initialize_session_and_load_tools(
        self, server_name: str, session: ClientSession
    ) -> None:
        await session.initialize()
        self.sessions[server_name] = session
        server_tools = await load_mcp_tools(session)
        self.server_name_to_tools[server_name] = server_tools

    async def connect_to_server(
        self,
        server_name: str,
        *,
        transport: Literal["stdio", "sse", "websocket"] = "stdio",
        **kwargs,
    ) -> None:
        if transport == "sse":
            ...
        elif transport == "stdio":
            ...
        elif transport == "websocket":
            ...
        else:
            raise ValueError(f"Unsupported transport: {transport}. Must be 'stdio' or 'sse'")

    def get_tools(self) -> list[BaseTool]:
        all_tools: list[BaseTool] = []
        for server_tools in self.server_name_to_tools.values():
            all_tools.extend(server_tools)
        return all_tools

该类的构造函数接受一个连接配置字典作为参数,用于指定要连接的 MCP 服务器。_initialize_session_and_load_tools 方法用于初始化会话并加载工具。connect_to_server 方法根据指定的传输协议连接到服务器。get_tools 方法返回所有服务器的工具列表。

六、总结

通过 langchain-mcp-adapters 库,开发者可以方便地将 MCP 工具集成到 LangChain 和 LangGraph 中,从而利用 MCP 协议的优势,扩展 LangChain 和 LangGraph 的功能。该库提供了工具转换、多服务器支持等核心功能,使得开发者能够更轻松地构建复杂的语言模型应用。同时,通过详细的示例代码和配置说明,开发者可以快速上手并将其应用到实际项目中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值