深入学 LangChain 官方文档(十四)MCP 模型上下文协议首讲
本篇对应的官方文档
- Model Context Protocol (MCP):支撑
MultiServerMCPClient、transport、session、Tools、Resources、Prompts、返回内容与 Interceptor 的讲解。本篇讲解范围
本篇讲清 MCP 在 LangChain Agent 中的接入位置,并用客服 Agent 串起多服务连接、工具适配、会话生命周期、返回内容和权限治理。MCP OAuth 的完整实现、Registry、远程部署、协议版本协商和 Server 开发细节留给后续专题。
一个客服 Agent 需要查商品资料、读取订单、创建售后单。三个能力可能分别来自本地脚本、公司内网服务和第三方平台。如果每接一个系统都手写一套 SDK 包装、参数转换和返回值解析,工具数量越多,Agent 代码越像一块集成补丁板。
MCP 想解决的就是这层重复接入:服务端用共同协议公开能力,客户端按共同协议发现和调用。LangChain 再把发现到的 MCP tool 转换为 Agent 已经认识的 LangChain tool。Agent loop 并没有被替换,变化的是外部能力从哪里来、怎样被描述和怎样建立连接。

左侧每个外部系统都有独立 SDK、参数映射和错误处理;右侧由 MCP Server 按统一协议公开能力,LangChain 客户端负责发现并适配。协议减少的是接入差异,不会替业务系统决定权限和事务。
先把 MCP 协议边界和 Agent 执行边界分开,再看连接配置,后面的对象关系会清楚很多。
一、MCP 统一能力入口,不接管 Agent 决策
第 06 篇已经讲过 Tool Calling:模型根据 tool schema 生成调用请求,执行层真正运行工具,再用 ToolMessage 把结果交回模型。MCP 没有改变这条循环。
它新增的是一层标准化来源。过去,开发者在 Python 进程里直接定义 get_order();接入 MCP 后,订单服务可以作为独立 Server 暴露同名能力,MCP Client 读取它的名称、描述和输入 schema,再转换成 LangChain tool。
模型仍然只看到"有哪些工具、参数是什么"。是否调用、调用哪一个,仍由 Agent loop 决定;调用能否执行、当前用户有无权限,仍由应用和服务端决定。把 MCP 接上并不等于自动获得可信工具市场,更不等于所有工具都可以无条件开放给模型。
二、四个角色必须分清
在 LangChain 场景里,可以先固定四层对象。
用户应用承载 Agent、会话状态与业务流程;LangChain Agent 负责模型推理和工具循环;MCP Client 管理连接、发现能力并做对象适配;MCP Server 连接真实文件、数据库或 API,并执行自己公开的能力。

用户请求先进入 LangChain Agent;需要外部能力时,Agent 调用已经适配好的 tool,MCP Client 通过对应连接访问 Server,Server 再操作业务系统。返回结果沿原路进入 ToolMessage,而不是由 Server 直接控制模型。
这四层一旦混在一起,常见误区就会出现:把 Agent 的对话 State 当成 MCP Session,把 Server 的进程存活当成用户登录状态,或者认为 Client 加了认证 header 就已经完成业务授权。
一个更稳妥的判断是:Agent 保存"任务进行到哪里",MCP Session 保存"客户端和某个 Server 的协议会话",业务系统保存"订单和用户权限等权威事实"。三类状态可以关联,但不能互相替代。
三、MCP Server 不只公开 Tools
MCP 的三类核心能力是 Tools、Resources 和 Prompts。它们都来自 Server,却不承担同一种职责。
Tools 是可执行动作,例如查询订单、创建售后单。LangChain 可以把它们转换成 Agent tools,由模型在运行中选择调用。
Resources 是可读取数据,例如一份政策文件或一条数据库记录。client.get_resources() 返回 Blob,应用可以读取文本或二进制内容,再决定是否放进上下文、索引或缓存。Resource 不会仅因来自 MCP 就自动变成模型可执行的工具。
Prompts 是 Server 提供的可复用消息模板。client.get_prompt() 返回 messages,应用仍要决定何时取用、是否附加变量,以及它是否适合当前 Agent 的 system prompt 或某个工作流节点。

Tools 进入执行面,Resources 进入数据读取面,Prompts 进入消息模板面。三者共享协议连接,但消费方式不同;全部塞进 tools 会把"读数据"和"执行动作"的权限边界抹平。
因此,接入一个 MCP Server 前先问它公开了什么能力。客服 Agent 可能把 lookup_order 作为 tool,把售后政策作为 resource,把工单摘要模板作为 prompt。只有第一个需要进入模型的工具选择集合。
四、stdio 与 HTTP 决定连接位置
LangChain 当前文档展示了 stdio 和 HTTP 两种主要连接方式。
stdio 由客户端启动本地子进程,通过标准输入输出通信,适合本机脚本、开发工具和受控环境。连接配置需要 command 和 args,路径和可执行文件都属于部署合同。Server 进程拥有当前操作系统用户能够访问的资源,因此本地不等于天然安全。
HTTP 面向独立运行的远程或内网 Server,配置由 url 指向 MCP endpoint。认证信息可以通过 headers 传递,也可以实现 httpx.Auth。这里的 token 应来自运行时安全配置,不能硬编码进博客示例、仓库或模型上下文。

stdio 在应用机器上启动子进程,部署简单但继承本机权限;HTTP 连接独立服务,适合跨进程和跨机器调用,同时必须处理网络认证、超时与服务可用性。选择 transport 先看部署边界,不看名字是否更"高级"。
项目可以同时连接多个 Server。MultiServerMCPClient 用配置中的名称区分连接,后续 get_tools() 汇总它们公开的工具。
五、从 MCP tools 到 LangChain Agent
下面的客户端连接一个本地商品 Server 和一个远程订单 Server。示例只说明对象关系,product_server.py、URL 和 token 都需要由真实环境提供。
python
import asyncio
import os
from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_openai import ChatOpenAI
# 作用:连接两个 MCP Server,加载工具并执行一次客服查询。
async def run_customer_service_agent() -> None:
client = MultiServerMCPClient(
{
"product": {
"transport": "stdio",
"command": "python",
"args": ["C:/absolute/path/product_server.py"],
},
"order": {
"transport": "http",
"url": "https://mcp.example.com/order",
"headers": {
"Authorization": f"Bearer {os.environ['ORDER_MCP_TOKEN']}"
},
},
}
)
tools = await client.get_tools()
model = ChatOpenAI(
model="qwen3.7-plus",
api_key=os.environ["MODEL_API_KEY"],
base_url=os.environ["MODEL_BASE_URL"],
)
agent = create_agent(model=model, tools=tools)
result = await agent.ainvoke(
{
"messages": [
{
"role": "user",
"content": "查询订单 A-2048,并判断耳机是否满足售后条件",
}
]
}
)
print(result["messages"][-1].content)
if __name__ == "__main__":
asyncio.run(run_customer_service_agent())
client.get_tools() 是适配边界。它向 Server 获取工具定义,再生成 LangChain 能识别的工具对象。create_agent 接收到的仍是普通 tools 集合,因此后面的模型选择、messages、middleware 和 streaming 能继续沿用前文机制。

MCP Server 公开名称、描述与 input schema,get_tools() 将其适配为 LangChain tool,create_agent 再把工具集合交给模型。适配层连接两套对象合同,但不改写 Agent loop。
模型生成 tool call 后,LangChain 会调用适配好的工具,请求再由 MCP Client 交给对应 Server。执行结果沿这条链路返回,转换成 ToolMessage 后进入 Agent State;Server 不会越过 Client 直接把答案写进聊天记录。

一次 MCP tool call 仍处在 Agent 的"模型请求工具---执行工具---结果配对---模型继续推理"循环中。tool_call_id 负责把请求与 ToolMessage 对齐,协议连接只负责把执行跨到 Server。
这里应复用第 06 篇的安全判断:模型生成了合法参数,不代表操作已经获得授权。创建售后单、退款或改库存等副作用工具,仍需 schema 校验、身份检查、幂等键和必要审批。
六、默认无状态不等于不能保留会话
MultiServerMCPClient 默认按无状态方式使用:每次工具调用创建新的 ClientSession,完成执行后清理。对只依赖显式参数的查询工具,这种方式简单,也减少了隐式会话残留。
如果 Server 需要在多个调用之间保留上下文,应显式打开持久 Session,并在这个上下文里加载工具。
python
from langchain.agents import create_agent
from langchain_mcp_adapters.tools import load_mcp_tools
# 作用:在同一个 MCP ClientSession 中加载并连续使用工具。
async def run_with_persistent_session(client, model) -> None:
async with client.session("order") as session:
tools = await load_mcp_tools(session)
agent = create_agent(model=model, tools=tools)
await agent.ainvoke(
{"messages": [{"role": "user", "content": "继续处理订单 A-2048"}]}
)
这段代码把 Session 的打开、工具加载、多次调用和关闭放进同一个生命周期。与默认路径并排看,差异不在 create_agent,而在 MCP Client 是否显式保留同一个协议会话。

默认路径为每次 tool call 建立并清理 Session;显式 client.session() 才在代码块生命周期内复用会话。stdio 子进程持续运行也不能证明每次调用共享同一协议 Session。
持久 Session 是协议状态,不是业务数据库。即使 Server 记住了上一次游标,订单状态仍应从权威系统读取;即使 Agent 有同一个 thread_id,也不代表 MCP Server 自动识别当前用户。需要关联时,应由应用明确传递稳定标识,并定义超时、重连和失效策略。
七、返回值可能不只是文本
MCP tool 可以同时返回面向阅读的文本和机器可处理的 structured content。LangChain adapter 会把 structured content 放进 MCPToolArtifact,调用方通过 ToolMessage.artifact 读取。这样订单查询既能给模型一段摘要,也能给应用一份结构化订单对象。
多模态返回则会转换成标准 content_blocks。截图工具可能返回文本说明和图片;消费者应按 block 类型读取 URL 或 base64,而不是把整个结果强制拼成字符串。

普通文本进入消息内容,structured content 保存在 ToolMessage.artifact,多模态部分转换为标准 content_blocks。展示层、模型上下文和业务逻辑可以消费不同视图,避免重复解析自然语言。
工具失败也要分层处理。当前 LangChain 文档说明,MCP tool 返回 CallToolResult(isError=True) 时,默认会作为 status="error" 的工具消息交给模型,让 Agent 有机会修正参数或选择替代路径;若设置 handle_tool_errors=False,则改为抛出异常。
但 transport、session 和内容转换失败始终属于客户端或基础设施异常。网络断开不能伪装成"订单不存在",内容解析失败也不能让模型随意猜测结构。应用需要分别记录工具业务错误与连接错误,才能给出正确的重试和降级策略。
八、Interceptor 是运行时治理边界
MCP Server 运行在独立进程中,默认看不到 LangGraph runtime 的 context、state 或 store。Tool Interceptor 在调用进入 Server 前提供了一道应用侧控制面。
它可以从 request.runtime.context 读取当前用户标识,向工具参数注入租户信息;可以根据 State 判断用户是否完成认证;也可以做限流、重试、日志或短路返回。需要修改请求时,应使用 request.override() 生成新请求,而不是原地改变共享对象。

Agent 产生调用后,Interceptor 先读取 runtime context 与 State,完成身份注入、权限判断、限流或短路,再决定是否访问 MCP Server。Server 仍需执行自己的授权校验,客户端拦截不能成为唯一防线。
最重要的边界是不要把凭据交给模型。API token 应由连接配置、认证对象或 Interceptor 从安全存储读取;模型只需要决定业务参数,不需要看到完整认证 header。Server 端还要用当前身份重新校验资源范围,不能盲目信任模型传来的 user_id。
Interceptor 也不是 Middleware 的替代品。Middleware 管理 Agent loop 的模型、工具和状态生命周期;MCP tool interceptor 聚焦 MCP 工具执行边界。两者可以协作,但应该清楚哪条策略在哪一层生效,避免同一操作重复重试或重复审计。
九、生产接入先检查四个问题
第一个问题是能力类型。需要模型选择并执行的是 Tool;需要应用读取的是 Resource;需要复用消息模板的是 Prompt。分类错误会直接扩大模型权限。
第二个问题是部署与连接。本地能力是否适合 stdio,远程服务是否使用 HTTP,认证从哪里注入,连接超时和失败如何处理。不要把"连接成功"当成"业务可用"。
第三个问题是生命周期。工具是否只依赖本次参数,还是需要持久 Session;Agent thread、MCP Session 与业务登录态如何关联;重连后哪些状态可以恢复,哪些必须重新读取。
第四个问题是风险。谁负责授权、参数校验、幂等、审批、审计和敏感数据脱敏。MCP Server 公开的每个 tool 都应拥有最小权限,尤其不能把文件删除、支付或配置修改能力作为无条件工具暴露。

接入顺序从能力分类开始,再选择 transport 和认证方式,随后确定 Session 生命周期,最后补齐授权、幂等、审批与观测。任一层没有明确责任人,都不应因为"工具已经被发现"就直接交给生产 Agent。
完成这些判断后,MCP 才真正降低集成成本:Agent 代码不再了解每个服务的 SDK 细节,Server 可以独立演进,应用仍保留明确的控制面和失败边界。
十、MCP 应该用在什么地方
当多个 Agent 或应用需要复用同一组外部能力,服务需要独立部署和演进,或者团队希望用共同协议管理工具发现,MCP 很合适。它把连接与能力描述从单个 Agent 项目中抽出来,也让 LangChain tools 可以来自不同 Server。
如果只有一个稳定的本地函数,没有跨进程复用、动态发现或独立部署需求,直接定义 LangChain tool 往往更简单。为了"用了 MCP"而增加 Server、transport 和 session,只会把一个函数调用变成分布式问题。
选择时可以用一句话收束:MCP 统一外部能力的接入合同,LangChain 负责 Agent 运行合同,业务系统负责权限与事实合同。 三份合同边界清晰,协议才会减少耦合;边界混在一起,统一入口反而会放大风险。
总结:标准化接入,不等于自动可信
LangChain 通过 langchain-mcp-adapters 连接一个或多个 MCP Server。MultiServerMCPClient 管理 stdio 与 HTTP connection,get_tools() 把 MCP tools 转换为 LangChain tools,再交给 create_agent 进入已有的 Tool Calling 循环。
Tools、Resources、Prompts 分别承担动作、数据和模板职责;默认无状态调用与显式持久 Session 解决不同生命周期问题;文本、structured content 和多模态内容也有不同落点。Interceptor 能把 runtime context、权限、限流和日志带到工具执行边界,却不能替代 Server 端授权。
真正可用的 MCP 接入,要同时回答能力是什么、连接在哪里、状态保留多久、风险由谁控制。协议把接口变得一致,工程系统仍要对每一次真实动作负责。
下一篇将进入《Human-in-the-loop 与 Guardrails:高风险动作如何审批和拦截》。MCP 让外部能力可以被统一接入,但"能调用"并不等于"应当自动执行";接下来会把审批、拦截、暂停与恢复串起来,看清支付、删除、配置修改等高风险动作怎样在真正执行前经过人工和策略闸门。