Skip to main content
模型上下文协议 (MCP) 是一个开放协议,用于标准化应用程序向 LLM 提供工具和上下文的方式。LangChain 代理可以使用 langchain-mcp-adapters 库来使用 MCP 服务器上定义的工具。

快速入门

安装 langchain-mcp-adapters 库:
pip install langchain-mcp-adapters
langchain-mcp-adapters 使代理能够使用一个或多个 MCP 服务器上定义的工具。
MultiServerMCPClient 默认是无状态的。每次工具调用都会创建一个新的 MCP ClientSession,执行工具操作后清理会话。详情请参阅 有状态会话 部分。
import asyncio
from langchain_mcp_adapters.client import MultiServerMCPClient  
from langchain.agents import create_agent

async def main():
    client = MultiServerMCPClient(
        {
            "math": {
                "transport": "stdio",  # 本地子进程通信
                "command": "python",
                # 指向您的 math_server.py 文件的绝对路径
                "args": ["/path/to/math_server.py"],
            },
            "weather": {
                "transport": "http",  # 基于 HTTP 的远程服务器
                # 确保您的天气服务器在端口 8000 上启动
                "url": "http://localhost:8000/mcp",
            }
        }
    )

    tools = await client.get_tools()
    agent = create_agent(
        "claude-sonnet-4-6",
        tools  
    )
    math_response = await agent.ainvoke(
        {"messages": [{"role": "user", "content": "what's (3 + 5) x 12?"}]}
    )
    weather_response = await agent.ainvoke(
        {"messages": [{"role": "user", "content": "what is the weather in nyc?"}]}
    )
    print(math_response)
    print(weather_response)

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

自定义服务器

要创建一个自定义 MCP 服务器,使用 FastMCP 库:
pip install fastmcp
要使用 MCP 工具服务器测试您的代理,请参考以下示例:
from fastmcp import FastMCP

mcp = FastMCP("Math")

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

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

if **name** == "**main**":
mcp.run(transport="stdio")

传输方式

MCP 支持不同的传输机制用于客户端-服务器通信。

HTTP

http 传输(也称为 streamable-http)通过 HTTP 请求进行客户端与服务器之间的通信。更多细节请参阅 MCP HTTP 传输规范
client = MultiServerMCPClient(
    {
        "weather": {
            "transport": "http",
            "url": "http://localhost:8000/mcp",
        }
    }
)

传递请求头

通过 HTTP 连接 MCP 服务器时,您可以在连接配置中使用 headers 字段传入自定义请求头(例如用于认证或链路追踪)。这适用于 sse(已被 MCP 规范弃用)和 streamable_http 两种传输方式。
Passing headers with MultiServerMCPClient
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_agent

client = MultiServerMCPClient(
    {
        "weather": {
            "transport": "http",
            "url": "http://localhost:8000/mcp",
            "headers": {
                "Authorization": "Bearer YOUR_TOKEN",
                "X-Custom-Header": "custom-value"
            },
        }
    }
)
tools = await client.get_tools()
agent = create_agent("openai:gpt-4.1", tools)
response = await agent.ainvoke({"messages": "what is the weather in nyc?"})

身份认证

langchain-mcp-adapters 底层使用官方 MCP SDK,因此您可以通过实现 httpx.Auth 接口来提供自定义认证机制。
from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient(
    {
        "weather": {
            "transport": "http",
            "url": "http://localhost:8000/mcp",
            "auth": auth,
        }
    }
)

stdio

客户端以子进程方式启动服务器,并通过标准输入/输出进行通信。最适合本地工具和简单部署场景。
与 HTTP 传输不同,stdio 连接天然是有状态的——子进程会在客户端连接整个生命周期内持续存在。 但如果使用 MultiServerMCPClient 且未显式管理会话,每次工具调用仍会创建一个新会话。要管理持久连接, 请参阅 stateful sessions
client = MultiServerMCPClient(
    {
        "math": {
            "transport": "stdio",
            "command": "python",
            "args": ["/path/to/math_server.py"],
        }
    }
)

有状态会话

默认情况下,MultiServerMCPClient无状态的——每次工具调用都会创建一个新的 MCP 会话,执行完成后即清理。 如果您需要控制 MCP 会话的生命周期(例如与跨多次工具调用保持上下文的有状态服务器协作),可以通过 client.session() 创建持久化的 ClientSession
Using MCP ClientSession for stateful tool usage
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_mcp_adapters.tools import load_mcp_tools
from langchain.agents import create_agent

client = MultiServerMCPClient({...})

# 显式创建会话
async with client.session("server_name") as session:
    # 将会话传入以加载工具、资源或提示模板
    tools = await load_mcp_tools(session)
    agent = create_agent(
        "anthropic:claude-3-7-sonnet-latest",
        tools
    )

核心功能

工具

工具允许 MCP 服务器暴露可执行函数,供 LLM 调用以执行动作——例如查询数据库、调用 API 或与外部系统交互。LangChain 会将 MCP 工具转换为 LangChain tools,使其可直接用于任何 LangChain 代理或工作流。

加载工具

使用 client.get_tools() 从 MCP 服务器检索工具,并将其传递给您的代理:
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_agent

client = MultiServerMCPClient({...})
tools = await client.get_tools()
agent = create_agent("claude-sonnet-4-6", tools)

结构化内容

MCP 工具可以在可读文本响应之外返回结构化内容。当工具除了提供给模型显示的文本外,还需要返回机器可解析的数据(如 JSON)时,这一特性非常有用。 当 MCP 工具返回 structuredContent 时,适配器会将其封装为 MCPToolArtifact,并作为工具的 artifact 返回。您可以通过 ToolMessage 上的 artifact 字段访问它。您还可以使用 interceptors 自动处理或转换结构化内容。 从 artifact 提取结构化内容 调用代理后,您可以从响应中的工具消息中访问结构化内容:
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_agent
from langchain.messages import ToolMessage

client = MultiServerMCPClient({...})
tools = await client.get_tools()
agent = create_agent("claude-sonnet-4-6", tools)

result = await agent.ainvoke(
    {"messages": [{"role": "user", "content": "Get data from the server"}]}
)

# 从工具消息中提取结构化内容
for message in result["messages"]:
    if isinstance(message, ToolMessage) and message.artifact:
        structured_content = message.artifact["structured_content"]
通过 interceptor 追加结构化内容 如果您希望结构化内容出现在对话历史中(即模型可见),可以使用 interceptor 将结构化内容自动追加到工具结果中:
import json

from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_mcp_adapters.interceptors import MCPToolCallRequest
from mcp.types import TextContent

async def append_structured_content(request: MCPToolCallRequest, handler):
    """将 artifact 中的结构化内容追加到工具消息。"""
    result = await handler(request)
    if result.structuredContent:
        result.content += [
            TextContent(type="text", text=json.dumps(result.structuredContent)),
        ]
    return result

client = MultiServerMCPClient({...}, tool_interceptors=[append_structured_content])

多模态工具内容

MCP 工具可以在响应中返回多模态内容(图像、文本等)。当 MCP 服务器返回包含多个部分的内容(例如文本与图像)时,适配器会将其转换为 LangChain 的标准内容块。您可以通过 ToolMessagecontent_blocks 属性访问标准化后的表示:
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_agent

client = MultiServerMCPClient({...})
tools = await client.get_tools()
agent = create_agent("claude-sonnet-4-6", tools)

result = await agent.ainvoke(
    {"messages": [{"role": "user", "content": "Take a screenshot of the current page"}]}
)

# 从工具消息中读取多模态内容
for message in result["messages"]:
    if message.type == "tool":
        # 提供商原生格式的原始内容
        print(f"原始内容: {message.content}")

        # 标准化内容块  #
        for block in message.content_blocks:
            if block["type"] == "text":
                print(f"文本: {block['text']}")
            elif block["type"] == "image":
                print(f"图片 URL: {block.get('url')}")
                print(f"图片 base64: {block.get('base64', '')[:50]}...")
这使您能够以与提供商无关的方式处理多模态工具响应,而不受底层 MCP 服务器内容格式的影响。

资源

资源允许 MCP 服务器暴露可供客户端读取的数据——例如文件、数据库记录或 API 响应。LangChain 会将 MCP 资源转换为 Blob 对象,为文本与二进制内容提供统一的处理接口。

加载资源

使用 client.get_resources() 从 MCP 服务器加载资源:
from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient({...})

# 加载服务器上的全部资源
blobs = await client.get_resources("server_name")

# 或按 URI 加载指定资源
blobs = await client.get_resources("server_name", uris=["file:///path/to/file.txt"])

for blob in blobs:
    print(f"URI: {blob.metadata['uri']}, MIME type: {blob.mimetype}")
    print(blob.as_string())  # 文本内容可直接读取
您也可以直接在会话中使用 load_mcp_resources 以获得更细粒度的控制:
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_mcp_adapters.resources import load_mcp_resources

client = MultiServerMCPClient({...})

async with client.session("server_name") as session:
    # 加载全部资源
    blobs = await load_mcp_resources(session)

    # 或按 URI 加载指定资源
    blobs = await load_mcp_resources(session, uris=["file:///path/to/file.txt"])

提示模板

提示模板允许 MCP 服务器暴露可复用的提示模板,客户端可检索后直接使用。LangChain 会将 MCP 提示转换为 messages,便于集成到基于聊天的工作流中。

加载提示模板

使用 client.get_prompt() 从 MCP 服务器加载提示模板:
from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient({...})

# 按名称加载提示模板
messages = await client.get_prompt("server_name", "summarize")

# 传入参数加载提示模板
messages = await client.get_prompt(
    "server_name",
    "code_review",
    arguments={"language": "python", "focus": "security"}
)

# 在工作流中使用这些消息
for message in messages:
    print(f"{message.type}: {message.content}")
您也可以直接在会话中使用 load_mcp_prompt 以获得更多控制:
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_mcp_adapters.prompts import load_mcp_prompt

client = MultiServerMCPClient({...})

async with client.session("server_name") as session:
    # 按名称加载提示模板
    messages = await load_mcp_prompt(session, "summarize")

    # 传入参数加载提示模板
    messages = await load_mcp_prompt(
        session,
        "code_review",
        arguments={"language": "python", "focus": "security"}
    )

高级功能

工具拦截器

MCP 服务器以独立进程运行——它们无法直接访问 LangGraph 运行时信息,例如 storecontext 或代理状态。Interceptors 正是为了解决这道鸿沟:它们允许您在 MCP 工具执行期间访问运行时上下文。 Interceptors 还提供类似中间件的工具调用控制能力:您可以修改请求、实现重试、动态添加请求头,甚至直接短路执行。
部分说明
访问运行时上下文读取用户 ID、API 密钥、存储数据和代理状态
状态更新与命令使用 Command 更新代理状态或控制图执行流
编写拦截器修改请求、组合拦截器和处理错误的常见模式

访问运行时上下文

当 MCP 工具在 LangChain 代理(通过 create_agent)中使用时,interceptors 可以访问 ToolRuntime 上下文。这样您就能访问工具调用 ID、状态、配置和存储,从而实现读取用户数据、持久化信息和控制代理行为等高级模式。
访问调用时传入的用户配置,例如用户 ID、API 密钥或权限信息:
将用户上下文注入 MCP 工具调用
from dataclasses import dataclass
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_mcp_adapters.interceptors import MCPToolCallRequest
from langchain.agents import create_agent

@dataclass
class Context:
user_id: str
api_key: str

async def inject_user_context(
request: MCPToolCallRequest,
handler,
):
"""将用户凭据注入到 MCP 工具调用中。"""
runtime = request.runtime
user_id = runtime.context.user_id  
api_key = runtime.context.api_key  

# 将用户上下文添加到工具参数中
modified_request = request.override(
    args={**request.args, "user_id": user_id}
)
return await handler(modified_request)

client = MultiServerMCPClient(
{...},
tool_interceptors=[inject_user_context],
)
tools = await client.get_tools()
agent = create_agent("gpt-4.1", tools, context_schema=Context)

# 传入用户上下文进行调用
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "Search my orders"}]},
context={"user_id": "user_123", "api_key": "sk-..."}
)
有关更多上下文工程模式,请参阅 Context engineeringTools

状态更新与命令

Interceptors 可以返回 Command 对象来更新代理状态或控制图执行流。这对于跟踪任务进度、在代理之间切换,或提前结束执行非常有用。
标记任务完成并切换代理
from langchain.agents import AgentState, create_agent
from langchain_mcp_adapters.interceptors import MCPToolCallRequest
from langchain.messages import ToolMessage
from langgraph.types import Command

async def handle_task_completion(
    request: MCPToolCallRequest,
    handler,
):
    """标记任务完成并切换到总结代理。"""
    result = await handler(request)

    if request.name == "submit_order":
        return Command(
            update={
                "messages": [result] if isinstance(result, ToolMessage) else [],
                "task_status": "completed",
            },
            goto="summary_agent",
        )

    return result
使用 Command 并设置 goto="__end__" 可以提前结束执行:
完成后结束代理运行
async def end_on_success(
    request: MCPToolCallRequest,
    handler,
):
    """任务标记为完成时结束代理运行。"""
    result = await handler(request)

    if request.name == "mark_complete":
        return Command(
            update={"messages": [result], "status": "done"},
            goto="__end__",
        )

    return result

自定义拦截器

拦截器是包裹工具执行过程的异步函数,可用于请求/响应改写、重试逻辑和其他横切关注点。它们遵循“洋葱模型”:列表中的第一个拦截器位于最外层。 基础模式 拦截器是一个接收请求和处理器(handler)的异步函数。您可以在调用 handler 前修改请求、在调用后修改响应,或完全跳过 handler。
基础拦截器模式
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_mcp_adapters.interceptors import MCPToolCallRequest

async def logging_interceptor(
    request: MCPToolCallRequest,
    handler,
):
    """在执行前后记录工具调用日志。"""
    print(f"调用工具: {request.name}, 参数: {request.args}")
    result = await handler(request)
    print(f"工具 {request.name} 返回: {result}")
    return result

client = MultiServerMCPClient(
    {"math": {"transport": "stdio", "command": "python", "args": ["/path/to/server.py"]}},
    tool_interceptors=[logging_interceptor],
)
修改请求 使用 request.override() 创建修改后的请求。这种方式遵循不可变模式,不会改动原始请求。
修改工具参数
async def double_args_interceptor(
    request: MCPToolCallRequest,
    handler,
):
    """在执行前将所有数值参数翻倍。"""
    modified_args = {k: v * 2 for k, v in request.args.items()}
    modified_request = request.override(args=modified_args)
    return await handler(modified_request)

# 原始调用 add(a=2, b=3) 将变为 add(a=4, b=6)
运行时修改请求头 拦截器可根据请求上下文动态修改 HTTP 请求头:
动态修改请求头
async def auth_header_interceptor(
    request: MCPToolCallRequest,
    handler,
):
    """根据被调用工具添加认证请求头。"""
    token = get_token_for_tool(request.name)
    modified_request = request.override(
        headers={"Authorization": f"Bearer {token}"}
    )
    return await handler(modified_request)
组合拦截器 多个拦截器按“洋葱顺序”组合——列表中的第一个拦截器位于最外层:
组合多个拦截器
async def outer_interceptor(request, handler):
    print("outer: before")
    result = await handler(request)
    print("outer: after")
    return result

async def inner_interceptor(request, handler):
    print("inner: before")
    result = await handler(request)
    print("inner: after")
    return result

client = MultiServerMCPClient(
    {...},
    tool_interceptors=[outer_interceptor, inner_interceptor],
)

# 执行顺序:
# outer: before -> inner: before -> tool execution -> inner: after -> outer: after
错误处理 使用拦截器捕获工具执行错误并实现重试逻辑:
出错重试
import asyncio

async def retry_interceptor(
    request: MCPToolCallRequest,
    handler,
    max_retries: int = 3,
    delay: float = 1.0,
):
    """使用指数退避重试失败的工具调用。"""
    last_error = None
    for attempt in range(max_retries):
        try:
            return await handler(request)
        except Exception as e:
            last_error = e
            if attempt < max_retries - 1:
                wait_time = delay * (2 ** attempt)  # 指数退避
                print(f"工具 {request.name} 失败(第 {attempt + 1} 次),{wait_time}s 后重试...")
                await asyncio.sleep(wait_time)
    raise last_error

client = MultiServerMCPClient(
    {...},
    tool_interceptors=[retry_interceptor],
)
您也可以捕获特定错误类型并返回兜底值:
带兜底的错误处理
async def fallback_interceptor(
    request: MCPToolCallRequest,
    handler,
):
    """工具执行失败时返回兜底结果。"""
    try:
        return await handler(request)
    except TimeoutError:
        return f"工具 {request.name} 超时,请稍后重试。"
    except ConnectionError:
        return f"无法连接到 {request.name} 服务,已使用缓存数据。"

进度通知

为长时间运行的工具执行订阅进度更新:
进度回调
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_mcp_adapters.callbacks import Callbacks, CallbackContext

async def on_progress(
    progress: float,
    total: float | None,
    message: str | None,
    context: CallbackContext,
):
    """处理来自 MCP 服务器的进度更新。"""
    percent = (progress / total * 100) if total else progress
    tool_info = f" ({context.tool_name})" if context.tool_name else ""
    print(f"[{context.server_name}{tool_info}] Progress: {percent:.1f}% - {message}")

client = MultiServerMCPClient(
    {...},
    callbacks=Callbacks(on_progress=on_progress),
)
CallbackContext 提供:
  • server_name:MCP 服务器名称
  • tool_name:正在执行的工具名称(在工具调用期间可用)

日志

MCP 协议支持服务器发送 logging 通知。可通过 Callbacks 类订阅这些事件。
日志回调
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_mcp_adapters.callbacks import Callbacks, CallbackContext
from mcp.types import LoggingMessageNotificationParams

async def on_logging_message(
    params: LoggingMessageNotificationParams,
    context: CallbackContext,
):
    """处理来自 MCP 服务器的日志消息。"""
    print(f"[{context.server_name}] {params.level}: {params.data}")

client = MultiServerMCPClient(
    {...},
    callbacks=Callbacks(on_logging_message=on_logging_message),
)

Elicitation(交互补充)

Elicitation 允许 MCP 服务器在工具执行过程中向用户请求额外输入。与一次性提供所有输入不同,服务器可以按需进行交互式提问。

服务器端设置

定义一个工具,使用 ctx.elicit() 按 schema 请求用户输入:
带 elicitation 的 MCP 服务器
from pydantic import BaseModel
from mcp.server.fastmcp import Context, FastMCP

server = FastMCP("Profile")

class UserDetails(BaseModel):
    email: str
    age: int

@server.tool()
async def create_profile(name: str, ctx: Context) -> str:
    """创建用户档案,并通过 elicitation 请求详细信息。"""
    result = await ctx.elicit(
        message=f"请提供 {name} 的档案详细信息:",
        schema=UserDetails,
    )
    if result.action == "accept" and result.data:
        return f"已为 {name} 创建档案:email={result.data.email}, age={result.data.age}"
    if result.action == "decline":
        return f"用户拒绝提供信息,已为 {name} 创建最小档案。"
    return "已取消档案创建。"

if __name__ == "__main__":
    server.run(transport="http")

客户端设置

通过为 MultiServerMCPClient 提供回调函数来处理 elicitation 请求:
处理 elicitation 请求
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_mcp_adapters.callbacks import Callbacks, CallbackContext
from mcp.shared.context import RequestContext
from mcp.types import ElicitRequestParams, ElicitResult

async def on_elicitation(
    mcp_context: RequestContext,
    params: ElicitRequestParams,
    context: CallbackContext,
) -> ElicitResult:
    """处理来自 MCP 服务器的 elicitation 请求。"""
    # 在真实应用中,您通常会根据 params.message 与 params.requestedSchema
    # 向用户发起交互并收集输入
    return ElicitResult(
        action="accept",
        content={"email": "user@example.com", "age": 25},
    )

client = MultiServerMCPClient(
    {
        "profile": {
            "url": "http://localhost:8000/mcp",
            "transport": "http",
        }
    },
    callbacks=Callbacks(on_elicitation=on_elicitation),
)

响应动作

elicitation 回调可以返回以下三种动作之一:
动作说明
accept用户提供了有效输入。请在 content 字段中附带数据。
decline用户选择不提供所请求的信息。
cancel用户完全取消本次操作。
响应动作示例
# 接受并附带数据
ElicitResult(action="accept", content={"email": "user@example.com", "age": 25})

# 拒绝(用户不想提供信息)
ElicitResult(action="decline")

# 取消(中止操作)
ElicitResult(action="cancel")

更多资源