一、引言
在自然语言处理和人工智能应用中,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 的功能。该库提供了工具转换、多服务器支持等核心功能,使得开发者能够更轻松地构建复杂的语言模型应用。同时,通过详细的示例代码和配置说明,开发者可以快速上手并将其应用到实际项目中。