Skip to content

5.4 Model Context Protocol (MCP)

本节介绍 LangChain 对 MCP 协议的支持,实现标准化的工具和上下文提供。


什么是 MCP?

MCP(Model Context Protocol) 是一个开放协议,标准化应用程序向 LLM 提供工具和上下文的方式。

"MCP is an open protocol that standardizes how applications provide tools and context to LLMs."

LangChain Agent 可以通过 langchain-mcp-adapters 库使用 MCP 服务器。


安装

bash
pip install langchain-mcp-adapters

如果需要创建 MCP 服务器:

bash
pip install mcp

传输机制

MCP 支持三种通信方式:

传输方式说明适用场景
stdio本地子进程,通过标准输入输出简单本地设置
Streamable HTTP独立服务器处理 HTTP 请求远程连接
SSEHTTP 变体,优化实时流实时流式场景

访问 MCP 工具

使用 MultiServerMCPClient 访问多个 MCP 服务器的工具:

python
from langchain_mcp_adapters.client import MultiServerMCPClient

# 配置多个 MCP 服务器
client = MultiServerMCPClient({
    "math": {
        "transport": "stdio",
        "command": "python",
        "args": ["/path/to/math_server.py"],
    },
    "weather": {
        "transport": "streamable_http",
        "url": "http://localhost:8000/mcp",
    },
    "database": {
        "transport": "sse",
        "url": "http://localhost:8001/sse",
    }
})

# 获取所有工具
tools = await client.get_tools()
print(f"可用工具: {[t.name for t in tools]}")

与 Agent 集成

python
from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient

async def create_mcp_agent():
    client = MultiServerMCPClient({
        "math": {
            "transport": "stdio",
            "command": "python",
            "args": ["math_server.py"],
        }
    })

    tools = await client.get_tools()

    agent = create_agent(
        model="gpt-4o",
        tools=tools,
    )

    return agent

构建 MCP 服务器

数学服务器(stdio)

python
# math_server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Math")

@mcp.tool()
def add(a: int, b: int) -> int:
    """两数相加"""
    return a + b

@mcp.tool()
def subtract(a: int, b: int) -> int:
    """两数相减"""
    return a - b

@mcp.tool()
def multiply(a: int, b: int) -> int:
    """两数相乘"""
    return a * b

@mcp.tool()
def divide(a: int, b: int) -> float:
    """两数相除"""
    if b == 0:
        raise ValueError("除数不能为零")
    return a / b

if __name__ == "__main__":
    mcp.run(transport="stdio")

天气服务器(HTTP)

python
# weather_server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Weather")

@mcp.tool()
async def get_weather(location: str) -> str:
    """获取指定位置的天气"""
    # 实际应用中调用天气 API
    weather_data = {
        "北京": "晴天,25°C",
        "上海": "多云,28°C",
        "广州": "小雨,30°C",
    }
    return weather_data.get(location, f"{location}: 天气数据不可用")

@mcp.tool()
async def get_forecast(location: str, days: int = 3) -> str:
    """获取天气预报"""
    return f"{location} 未来 {days} 天预报: 晴转多云"

if __name__ == "__main__":
    mcp.run(transport="streamable-http")

数据库服务器

python
# database_server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Database")

# 模拟数据库
users_db = {
    "1": {"name": "张三", "email": "zhangsan@example.com"},
    "2": {"name": "李四", "email": "lisi@example.com"},
}

@mcp.tool()
async def get_user(user_id: str) -> dict:
    """获取用户信息"""
    user = users_db.get(user_id)
    if user:
        return user
    raise ValueError(f"用户 {user_id} 不存在")

@mcp.tool()
async def list_users() -> list:
    """列出所有用户"""
    return list(users_db.values())

@mcp.tool()
async def create_user(user_id: str, name: str, email: str) -> dict:
    """创建新用户"""
    if user_id in users_db:
        raise ValueError(f"用户 {user_id} 已存在")
    users_db[user_id] = {"name": name, "email": email}
    return users_db[user_id]

if __name__ == "__main__":
    mcp.run(transport="streamable-http")

有状态工具使用

对于需要保持会话状态的服务器,使用持久会话:

python
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_mcp_adapters.tools import load_mcp_tools

async def use_stateful_tools():
    client = MultiServerMCPClient({
        "stateful_service": {
            "transport": "stdio",
            "command": "python",
            "args": ["stateful_server.py"],
        }
    })

    # 使用持久会话
    async with client.session("stateful_service") as session:
        tools = await load_mcp_tools(session)

        # 在同一会话中多次调用工具
        # 状态会在调用之间保持
        result1 = await tools[0].ainvoke({"input": "first"})
        result2 = await tools[0].ainvoke({"input": "second"})

完整示例

python
import asyncio
from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient

async def main():
    # 1. 配置 MCP 客户端
    client = MultiServerMCPClient({
        "calculator": {
            "transport": "stdio",
            "command": "python",
            "args": ["math_server.py"],
        },
        "weather": {
            "transport": "streamable_http",
            "url": "http://localhost:8000/mcp",
        }
    })

    # 2. 获取所有工具
    tools = await client.get_tools()
    print(f"加载了 {len(tools)} 个工具")

    # 3. 创建 Agent
    agent = create_agent(
        model="gpt-4o",
        tools=tools,
        system_prompt="你是一个多功能助手,可以进行数学计算和查询天气。"
    )

    # 4. 使用 Agent
    result = agent.invoke({
        "messages": [{
            "role": "user",
            "content": "计算 15 + 27,然后告诉我北京的天气"
        }]
    })

    print(result["messages"][-1].content)

if __name__ == "__main__":
    asyncio.run(main())

MCP 服务器配置选项

stdio 传输

python
{
    "server_name": {
        "transport": "stdio",
        "command": "python",        # 执行命令
        "args": ["server.py"],      # 命令参数
        "env": {                    # 环境变量(可选)
            "API_KEY": "xxx"
        },
        "cwd": "/path/to/dir"       # 工作目录(可选)
    }
}

HTTP 传输

python
{
    "server_name": {
        "transport": "streamable_http",
        "url": "http://localhost:8000/mcp",
        "headers": {                # 请求头(可选)
            "Authorization": "Bearer xxx"
        },
        "timeout": 30               # 超时时间(可选)
    }
}

SSE 传输

python
{
    "server_name": {
        "transport": "sse",
        "url": "http://localhost:8000/sse",
        "headers": {}
    }
}

MCP 工具装饰器

python
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("MyServer")

# 基本工具
@mcp.tool()
def simple_tool(param: str) -> str:
    """简单工具"""
    return f"处理: {param}"

# 异步工具
@mcp.tool()
async def async_tool(param: str) -> str:
    """异步工具"""
    await asyncio.sleep(1)
    return f"异步处理: {param}"

# 带类型注解的工具
@mcp.tool()
def typed_tool(
    name: str,
    count: int = 1,
    enabled: bool = True
) -> dict:
    """带完整类型注解的工具"""
    return {
        "name": name,
        "count": count,
        "enabled": enabled
    }

架构图

┌─────────────────────────────────────────────────────────┐
│                    LangChain Agent                       │
├─────────────────────────────────────────────────────────┤
│                                                          │
│   ┌─────────────────────────────────────────────────┐   │
│   │           MultiServerMCPClient                   │   │
│   └─────────────────────────────────────────────────┘   │
│          │              │              │                 │
│          ▼              ▼              ▼                 │
│   ┌──────────┐   ┌──────────┐   ┌──────────┐           │
│   │  stdio   │   │   HTTP   │   │   SSE    │           │
│   └──────────┘   └──────────┘   └──────────┘           │
│          │              │              │                 │
└──────────│──────────────│──────────────│─────────────────┘
           │              │              │
           ▼              ▼              ▼
    ┌──────────┐   ┌──────────┐   ┌──────────┐
    │   Math   │   │ Weather  │   │ Database │
    │  Server  │   │  Server  │   │  Server  │
    └──────────┘   └──────────┘   └──────────┘

最佳实践

实践说明
选择合适传输本地用 stdio,远程用 HTTP
错误处理MCP 工具应返回清晰的错误信息
文档完善工具 docstring 会被 LLM 使用
类型注解完整的类型注解提高可靠性
会话管理需要状态时使用持久会话

资源链接


上一节5.3 Context Engineering

下一节5.5 Human-in-the-loop

基于 MIT 许可证发布。内容版权归作者所有。