MCP 深度解析:AI 时代的工具协议革命
MCP 深度解析:AI 时代的工具协议革命
一、背景:大模型的「最后一公里」困境
1.1 能力边界的结构性矛盾
大语言模型(LLM)的核心能力是语义理解和推理生成,但它天然是一个封闭的静态系统:训练数据有截止日期,无法感知实时状态,无法主动操作外部系统。这个矛盾在早期以”幻觉问题”的面貌出现,但随着 LLM 被推向更复杂的业务场景,问题的本质逐渐清晰——
LLM 缺少的不是智能,而是与现实世界的连接通道。
最直观的数据:一个企业级 AI 助手,可能需要对接内部知识库、代码仓库、工单系统、监控平台、日历服务……每一个系统都是独立的数据孤岛。模型本身再强,没有数据和操作能力,也只能在真空中推理。
1.2 工具调用的野蛮生长期
2023 年前后,各家 LLM 厂商几乎同步推出了”工具调用”能力——OpenAI 的 Function Calling、Anthropic 的 Tool Use、Google 的 Function Declarations。思路相似:给模型一份工具描述的 JSON,模型决定何时调用哪个工具,返回结构化参数,由调用方执行并将结果回填。
这个机制解决了”模型能不能调用工具”的问题,但随即暴露出更深层的工程问题:
问题一:N×M 集成爆炸
假设你有 N 个 AI 应用(内部 Copilot、客服机器人、数据分析助手……),M 个工具系统(CRM、代码库、知识库、监控系统……),理论上需要维护 N×M 个适配层。不同 LLM 的工具描述格式不同,相同工具在不同模型里需要重写一遍 schema。这是纯粹的工程税。
问题二:上下文注入无标准
除了工具调用,另一个高频需求是”把数据喂给模型”——把文档内容、数据库查询结果、用户历史记录注入到上下文窗口。但怎么注入?截多长?谁来管理生命周期?每个项目都有一套私有解法,完全没有复用性。
问题三:会话状态与工具状态失配
LLM 的 session 是有状态的(对话历史),但工具系统通常是无状态的 API。当一个复杂任务需要跨多步工具调用时,谁负责维护中间状态?调用方需要手工拼接上下文,极易出错,且难以调试。
1.3 抽象层的缺失
回顾软件工程史,每当出现 N×M 集成爆炸时,解法通常是引入一个标准抽象层:
-
• TCP/IP 解决了异构网络互联 -
• POSIX 解决了操作系统 API 碎片化 -
• USB 解决了外设连接的硬件混乱
2024 年底,Anthropic 开源了 Model Context Protocol(MCP),试图扮演这个角色——成为 AI 应用与工具世界之间的标准连接协议。
二、MCP 是什么:协议层的统一解
2.1 核心定位
MCP 的官方定位是:一个开放协议,用于标准化 LLM 应用与外部数据源及工具之间的连接方式。
但这句话太抽象。更准确的说法是:
MCP 定义了一套语言,用于描述”一个系统能为 AI 提供哪些能力”,以及 AI 与这个系统交互的完整生命周期。它不依赖任何特定的 LLM,也不依赖任何特定的编程语言或框架。
2.2 三类能力原语
MCP 将 AI 可以使用的外部能力抽象为三类原语:
Resources(资源)
类比文件系统中的”文件”。Resources 是只读的、可被 LLM 消费的数据单元,每个 Resource 有一个 URI 标识。典型例子:一个文档的内容、一段代码、一条数据库记录。LLM 通过读取 Resource 来获取上下文,而不是通过工具调用。
关键设计:Resources 可以是静态内容(text/blob),也可以是动态内容(通过 URI 参数化)。Server 可以通知 Client 某个 Resource 发生了变化(Resource Changed Notification),这使得”订阅式上下文更新”成为可能。
Tools(工具)
类比函数调用。Tools 是有副作用的操作,LLM 通过决策何时调用来执行真实世界的动作。每个 Tool 有名称、描述(自然语言,供模型理解意图)、输入 Schema(JSON Schema,供模型生成合法参数)、以及执行结果。
Tools 是 MCP 中最类似传统”function calling”的部分,但有重要区别:工具描述与执行逻辑完全在 Server 侧,Client 不需要知道工具的实现细节。
Prompts(提示)
这个原语常被忽视,却非常有价值。Prompts 是预定义的、可复用的提示模板,由 Server 提供,可以包含参数化插槽。例如,一个代码审查系统可以提供”code_review”提示模板,Client 只需传入代码片段,Server 返回一个结构良好的 prompt,直接送入 LLM。
Prompts 解决的是”提示工程”的复用和标准化问题——将领域专家的提示经验封装到 Server 侧,避免每个调用方重复发明轮子。
2.3 传输层设计
MCP 在传输层支持两种模式:
stdio 模式(本地):Host 进程启动 Server 子进程,通过标准输入输出通信。适合本地工具(文件系统操作、本地命令执行)。延迟极低,无网络开销,安全边界清晰。
SSE over HTTP 模式(远程):Server 是独立的 HTTP 服务,通过 Server-Sent Events 实现服务端推送,通过 HTTP POST 实现客户端发送。适合云端服务、多 Client 共享场景。
协议本身是传输无关的——相同的 JSON-RPC 2.0 消息格式,在两种传输层上完全一致。这个设计决策使得本地开发和生产部署可以无缝切换。
三、技术架构深解
3.1 三层角色模型
MCP 定义了三种角色,理解它们的职责边界是理解整个协议的关键:
┌─────────────────────────────────────────┐
│ Host(宿主) │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Client A │ │ Client B │ │
│ └──────┬──────┘ └──────┬──────┘ │
└─────────┼────────────────┼─────────────┘
│ │
┌─────▼──────┐ ┌──────▼─────┐
│ Server 1 │ │ Server 2 │
│ (文件系统) │ │ (代码库) │
└────────────┘ └────────────┘
Host(宿主应用):用户直接交互的应用层,如 Claude Desktop、Cursor IDE、自研 AI Copilot。Host 负责管理整体用户体验,创建和管理多个 Client 实例,协调 LLM 与 MCP 能力的交互,以及执行安全策略和用户授权。Host 是整个系统中唯一真正”知道用户意图”的角色。
Client(协议客户端):Host 内部的协议实现层,每个 Client 对应一个 Server 连接。Client 负责维护与 Server 的会话状态,执行能力协商,转发工具调用请求并接收响应。一个 Host 可以同时维护多个 Client,每个 Client 与一个 Server 保持独立的有状态连接。
Server(能力提供者):独立运行的进程或服务,封装具体的工具和数据能力。Server 可以无状态(每次请求独立处理),也可以有状态(维护会话上下文)。关键约束:Server 不应直接访问对话历史,也不应知道自己在哪个应用里被使用。 这个隔离是 MCP 安全模型的基础。
3.2 会话生命周期
MCP 的连接生命周期分为四个阶段,每个阶段都有精确的协议定义:
阶段一:Initialize(初始化)
Client 发送 initialize 请求,携带自身的协议版本和能力声明。Server 响应自身支持的协议版本和能力集合。这个握手不仅是版本协商,更是能力边界的划定——双方明确告知对方”我支持哪些特性”。
// Client → Server
{
"method":"initialize",
"params":{
"protocolVersion":"2024-11-05",
"capabilities":{
"roots":{"listChanged":true},
"sampling":{}
},
"clientInfo":{"name":"ExampleClient","version":"1.0.0"}
}
}
// Server → Client
{
"result":{
"protocolVersion":"2024-11-05",
"capabilities":{
"tools":{"listChanged":true},
"resources":{"subscribe":true,"listChanged":true},
"prompts":{"listChanged":true}
},
"serverInfo":{"name":"FileSystemServer","version":"2.1.0"}
}
}
阶段二:Capability Discovery(能力发现)
初始化完成后,Client 通过 tools/list、resources/list、prompts/list 枚举 Server 提供的全部能力。这些描述信息最终会被注入到 LLM 的系统提示中,成为模型”感知”外部能力的方式。
阶段三:Operation(操作阶段)
这是协议的核心运行态。双向通信在这里展开:
-
• Client → Server:执行工具调用( tools/call)、读取资源(resources/read)、获取提示(prompts/get) -
• Server → Client:推送通知(能力列表变更、资源内容变更)、发起 Sampling 请求(见 3.3 节)
阶段四:Shutdown(关闭)
优雅关闭协议,确保进行中的操作有机会完成。
3.3 Sampling:被低估的反向通道
MCP 中最被忽视、但设计上最有深度的特性是 Sampling——Server 可以主动请求 Client(进而请求 LLM)执行推理。
这打破了传统的”模型调用工具”的单向模式,实现了工具调用 LLM 的反向通道:
传统模式:LLM → 决策 → 工具执行 → 结果回填 → LLM
MCP Sampling:LLM → 调用 Server → Server 内部需要 LLM 推理
→ Server 请求 Client Sampling
→ Client 调用 LLM(携带 Server 构建的 prompt)
→ LLM 响应返回 Server
→ Server 继续处理 → 最终结果返回 LLM
这有什么实际价值?考虑一个复杂场景:一个代码库 MCP Server,在处理”找到所有与 bug 相关的函数”这个工具调用时,需要对每个函数的代码语义进行判断——这本身就需要 LLM 的语义理解能力。通过 Sampling,Server 可以在执行过程中递归调用 LLM,而不需要 Host 应用做任何额外的协调逻辑。
但是Sampling 的安全边界设计同样值得关注:Server 不能自由地调用任何 LLM,而是通过 Client 发起请求,Host 有完整的控制权——包括是否允许 Sampling、使用哪个模型、是否需要用户确认。这保留了 Human-in-the-Loop 的核心约束。
3.4 安全模型与权限边界
MCP 的安全设计遵循几个核心原则:
最小权限原则:每个 Server 只暴露它需要暴露的能力。文件系统 Server 可以限制可访问的根目录(通过 roots 机制),数据库 Server 可以只暴露只读工具。
用户授权节点:敏感操作(写文件、发邮件、执行代码)应在 Host 层获得用户明确授权,而不是 Server 自行决定。MCP 规范将这个设计原则称为”Human-in-the-Loop”。
Server 隔离:不同 Server 之间不能直接通信,所有协调逻辑都在 Host/Client 层。这防止了一个被攻击的 Server 影响其他 Server。
Prompt Injection 防护:这是 MCP 生态目前最活跃的安全研究方向。当 Server 返回的内容中包含试图操控 LLM 的文本时(例如,一个恶意文档中嵌入了”忽略之前的指令”),Host 需要有明确的防护机制。MCP 规范要求 Server 返回的内容应明确标记来源,但实际的防护仍然主要依赖 Host 实现。
3.5 LLM 与 MCP 的交互机制:全链路拆解
这是理解 MCP 运转本质最关键的一节,也是最容易被文档略过的部分。很多人知道”LLM 可以调用 MCP 工具”,但不清楚这个过程在工程层面究竟发生了什么。
核心事实:LLM 本身不直接感知 MCP 协议。
LLM 只处理 token 序列。它看不到 JSON-RPC 消息,不知道 MCP Server 的存在,也不会主动发起任何网络请求。LLM 与 MCP 之间的交互,完全由 Host 层作为中介来编排。理解这一点,是理解后续所有机制的前提。
3.5.1 工具描述的注入机制
交互的起点是 Host 在向 LLM 发送请求之前,将 MCP Server 的工具能力翻译成 LLM 能理解的格式——通常是原生的 function calling schema(各家格式略有差异,但核心是 JSON Schema 描述的工具列表)。
以 Anthropic Claude 为例,Host 从 MCP Server 获取工具列表后,将其转换为如下格式,附加到 API 请求中:
{
"model":"claude-3-5-sonnet",
"tools":[
{
"name":"search_documents",
"description":"在内部知识库中搜索相关文档。当用户询问公司内部规范、流程或历史记录时使用此工具。",
"input_schema":{
"type":"object",
"properties":{
"query":{
"type":"string",
"description":"搜索关键词或自然语言问题"
},
"top_k":{
"type":"integer",
"description":"返回的最相关文档数量,默认 5",
"default":5
}
},
"required":["query"]
}
},
{
"name":"create_ticket",
"description":"在工单系统中创建新工单。需要用户明确表示要提交问题时才调用。",
"input_schema":{
"type":"object",
"properties":{
"title":{"type":"string","description":"工单标题"},
"description":{"type":"string","description":"问题描述"},
"priority":{
"type":"string",
"enum":["low","medium","high"],
"description":"优先级"
}
},
"required":["title","description"]
}
}
],
"messages":[
{"role":"user","content":"帮我找一下数据库慢查询的排查流程文档"}
]
}
注意 description 字段的写法——这是给 LLM 读的意图描述,直接影响模型是否会在合适的时机选择正确的工具。这是 MCP Server 开发者最需要精心设计的部分,远比参数 Schema 更重要。
3.5.2 LLM 的工具调用决策
LLM 接收到包含工具列表的请求后,在推理过程中会决定:
-
1. 是否需要调用工具:基于用户意图和工具描述,判断当前问题能否直接回答,还是需要外部信息 -
2. 调用哪个工具:根据工具描述的语义匹配程度做出选择 -
3. 填入什么参数:根据参数的 JSON Schema 和 description 生成合法的参数值
当 LLM 决定调用工具时,它不是”执行调用”,而是输出一个结构化的调用意图,以特殊的 stop reason 终止本次生成:
{
"stop_reason":"tool_use",
"content":[
{
"type":"text",
"text":"我来帮你查找相关文档。"
},
{
"type":"tool_use",
"id":"toolu_01XYZ",
"name":"search_documents",
"input":{
"query":"数据库慢查询排查流程",
"top_k":3
}
}
]
}
这是 LLM 与外部工具交互的本质:LLM 产出意图,Host 负责执行。 LLM 的工作在这里暂停,控制权交回 Host。
3.5.3 Host 的工具执行与结果回填
Host 收到 LLM 的工具调用意图后,经过三个步骤完成执行闭环:
Step 1:参数校验与权限检查
Host 在将调用转发给 MCP Server 之前,通常会做一轮校验:
-
• 参数是否符合 JSON Schema(防御模型生成非法参数) -
• 是否需要用户确认(对于写操作、敏感操作) -
• 调用频率是否超限
这个校验层是 Human-in-the-Loop 的核心实施点。
Step 2:向 MCP Server 发起工具调用
Host 通过 MCP Client 向对应的 Server 发送 tools/call 请求:
{
"method":"tools/call",
"params":{
"name":"search_documents",
"arguments":{
"query":"数据库慢查询排查流程",
"top_k":3
}
}
}
Server 执行真实的业务逻辑(查询 ES、调用内部 API 等),返回结构化结果:
{
"result":{
"content":[
{
"type":"text",
"text":"找到 3 篇相关文档:\n\n1. 《MySQL 慢查询分析指南》\n - 路径:/wiki/db/slow-query-guide\n - 摘要:介绍 slow_query_log 配置、EXPLAIN 分析方法...\n\n2. 《索引优化最佳实践》\n ...\n\n3. 《DBA OnCall 手册 - 性能问题处理流程》\n ..."
}
],
"isError":false
}
}
Step 3:将结果回填到 LLM 上下文
Host 将工具执行结果以 tool_result 的角色追加到对话历史,然后再次调用 LLM,让模型基于工具返回的真实数据继续生成回答:
{
"messages":[
{"role":"user","content":"帮我找一下数据库慢查询的排查流程文档"},
{
"role":"assistant",
"content":[
{"type":"text","text":"我来帮你查找相关文档。"},
{"type":"tool_use","id":"toolu_01XYZ","name":"search_documents",
"input":{"query":"数据库慢查询排查流程","top_k":3}}
]
},
{
"role":"user",
"content":[
{
"type":"tool_result",
"tool_use_id":"toolu_01XYZ",
"content":"找到 3 篇相关文档:\n\n1. 《MySQL 慢查询分析指南》..."
}
]
}
]
}
LLM 接收到包含工具结果的完整上下文后,生成最终回答。此时模型处理的已经是真实的、最新的业务数据,而非训练时的静态知识。
3.5.4 多工具串联调用
实际场景中,一个用户意图往往需要多次工具调用才能满足。整个链路可能经历多个”LLM 推理 → 工具调用 → 结果回填”的迭代:
用户: "分析一下最近三天的数据库告警,找出根因"
第 1 轮:
LLM → 调用 get_alerts(time_range="3d")
Host → MCP Server 返回告警列表 (15 条)
结果回填 LLM
第 2 轮:
LLM → 发现告警集中在某表,调用 get_metrics(table="orders", metric="slow_query_count")
Host → MCP Server 返回时序指标数据
结果回填 LLM
第 3 轮:
LLM → 需要确认最近变更,调用 search_commits(keyword="orders table", days=3)
Host → MCP Server 返回相关提交记录
结果回填 LLM
最终:
LLM 基于三次工具返回的真实数据,生成根因分析报告
每一轮迭代,LLM 都在动态决策下一步需要什么信息。这种推理驱动的工具编排是 MCP + LLM 组合最核心的能力——不是预定义工作流,而是模型根据中间结果动态调整调用策略。
3.5.5 Resources 的注入方式:与 Tools 的本质差异
Resources 与 Tools 的交互方式截然不同,容易混淆,值得单独澄清。
Tools 由 LLM 在推理过程中主动决策调用,是动态的、按需触发的。
Resources 通常由 Host 在对话开始前(或根据上下文判断时)主动注入到 LLM 的上下文窗口,LLM 并不知道这段内容来自 MCP Resource,它只是”上下文里有这段文字”。
典型 Resources 注入流程:
1. Host 根据用户消息判断需要哪些背景信息
(如:用户问到了某个服务 → 注入该服务的架构文档 Resource)
2. Host 通过 Client 调用 resources/read 获取 Resource 内容
3. Host 将 Resource 内容以 system message 或 context block 形式
注入到发送给 LLM 的请求中
4. LLM 在回答时天然利用这些背景信息,无需显式调用任何工具
这个机制的关键设计权衡:Resources 降低了调用延迟和 token 开销(不需要额外一轮工具调用来获取数据),但 Host 必须预判需要哪些 Resources,引入了主动预加载的逻辑复杂度。
实践中,对于大型文档库,通常先用 Tool 做语义检索(找到相关文档的 URI),再用 Resource 读取具体内容——Tool 和 Resource 的组合使用,才是最完整的 MCP 使用姿势。
3.5.6 交互模型的完整全景
综合以上各环节,LLM 与 MCP 的完整交互模型如下:
用户(User)、Host层(Host)、MCP Server;三者为独立交互主体,采用顺序交互模式,消息流向遵循时间顺序,具体交互过程如下:
-
1. 用户 → Host层:同步发送【用户消息】,触发整个交互流程,为初始交互触发点。 -
2. Host层 → MCP Server:Host层接收用户消息后,发起第一个交互消息【resources/read】,用于向MCP Server请求预加载资源。 -
3. MCP Server → Host层:同步响应Host层的资源请求,返回【Resource 内容】,完成资源预加载(对应原流程图①)。 -
4. Host层内部操作:基于接收的用户消息、预加载的Resource内容,注入工具列表,完成LLM请求的构建(对应原流程图②),此过程为Host层内部处理,不涉及与其他参与者的消息交互。 -
5. Host层内部操作:调用LLM,LLM输出【tool_use 意图】,明确需要执行的工具调用操作(对应原流程图③),此过程为Host层与LLM的内部交互,不涉及外部参与者。 -
6. Host层 → MCP Server:根据LLM输出的tool_use意图,发起第二个交互消息【tools/call】,请求MCP Server执行对应工具(对应原流程图④)。 -
7. MCP Server → Host层:同步响应工具调用请求,返回【工具执行结果】,完成工具调用的执行与结果反馈(对应原流程图④)。 -
8. Host层内部操作:将MCP Server返回的工具执行结果回填至上下文,再次调用LLM,LLM基于所有上下文(用户消息、Resource内容、工具执行结果)生成【最终回答】(对应原流程图⑤),此过程为Host层内部处理。 -
9. Host层 → 用户:同步发送【最终回答】,将交互结果反馈给用户,整个MCP交互流程结束。
理解这个全景图有一个重要推论:MCP 的性能瓶颈不在协议本身,而在这个编排循环的深度。 每一轮工具调用都意味着一次 LLM 推理 + 一次 Server 执行 + 一次上下文更新。对于需要 10+ 轮工具调用的复杂任务,端到端延迟可能达到数十秒。这是当前 MCP 架构在实时性要求高的场景下需要重点优化的工程问题。
3.5.7 以 LangChain 框架为例:MCP 接入的完整实现
前几节讲的是原理层面的交互机制。这里用 LangChain 框架做一次完整的工程落地示例,把抽象概念映射到真实代码,帮助你建立”能跑起来”的直觉。
LangChain 是目前使用最广泛的 LLM 应用开发框架,它本身有一套 Tool 抽象(BaseTool),但与 MCP 协议并没有直接绑定。当你想把 MCP Server 的能力接入 LangChain Agent 时,需要一层适配代码——这层适配正好清晰地展示了”Host 如何将 MCP 工具翻译给 LLM”的全过程。
整体架构
LangChain Agent(扮演 Host 角色)
│
├── ChatOpenAI / ChatAnthropic(LLM)
│
├── MCP Client(通过 langchain-mcp-adapters 或手写适配层)
│ │
│ ├── MCP Server A(stdio,本地文件系统)
│ └── MCP Server B(SSE/HTTP,远程知识库)
│
└── AgentExecutor(编排循环:推理 → 工具调用 → 回填)
Step 1:启动 MCP Client,获取工具列表
使用 mcp Python SDK 连接 Server,获取工具描述并转换为 LangChain 的 Tool 对象:
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from langchain_core.tools import StructuredTool
import asyncio, json
asyncdefload_mcp_tools(server_script: str) -> list[StructuredTool]:
"""
连接 MCP Server,将其暴露的 Tools 转换为 LangChain StructuredTool 列表
"""
server_params = StdioServerParameters(
command="python",
args=[server_script],
)
asyncwith stdio_client(server_params) as (read, write):
asyncwith ClientSession(read, write) as session:
# 协议握手:Initialize + Capability Negotiation
await session.initialize()
# 获取工具列表
tools_result = await session.list_tools()
langchain_tools = []
for mcp_tool in tools_result.tools:
# 将 MCP Tool 定义转换为 LangChain StructuredTool
tool = _mcp_tool_to_langchain(session, mcp_tool)
langchain_tools.append(tool)
return langchain_tools
def_mcp_tool_to_langchain(
session: ClientSession, mcp_tool
) -> StructuredTool:
"""
适配层核心:将单个 MCP Tool 描述映射为 LangChain StructuredTool
关键点:description 直接使用 MCP 工具的 description,这段文字将被注入给 LLM
"""
# 从 MCP Tool 的 inputSchema 动态构建 Pydantic 模型(用于参数校验)
from pydantic import create_model
from typing importAny
schema = mcp_tool.inputSchema
fields = {}
for prop_name, prop_def in schema.get("properties", {}).items():
field_type = _json_type_to_python(prop_def.get("type", "string"))
description = prop_def.get("description", "")
fields[prop_name] = (field_type, ... if prop_name in schema.get("required", []) elseNone)
ArgsModel = create_model(f"{mcp_tool.name}_args", **fields)
# 构建实际调用函数:这是 LangChain 执行工具时真正触发的逻辑
# 内部通过 MCP Client 发起 tools/call 请求
asyncdef_run(**kwargs: Any) -> str:
result = await session.call_tool(mcp_tool.name, arguments=kwargs)
# 将 MCP 返回的 content 列表拼接为字符串返回给 LLM
return"\n".join(
item.text for item in result.content ifhasattr(item, "text")
)
return StructuredTool(
name=mcp_tool.name,
description=mcp_tool.description, # 直接透传给 LLM 的意图描述
args_schema=ArgsModel,
coroutine=_run,
)
def_json_type_to_python(json_type: str):
return {"string": str, "integer": int, "number": float, "boolean": bool}.get(
json_type, str
)
Step 2:构建 LangChain Agent,绑定 MCP 工具
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
asyncdefbuild_mcp_agent(server_script: str) -> AgentExecutor:
# 1. 加载 MCP 工具(已转换为 LangChain Tool)
tools = await load_mcp_tools(server_script)
# 2. 初始化 LLM
# LangChain 会在调用时自动将 tools 列表转换为对应 LLM 的 function calling 格式
# OpenAI 格式 / Anthropic 格式 / Gemini 格式 由 LangChain 内部适配,无需手动处理
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# 3. 构建 Prompt 模板
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个企业内部助手,可以使用以下工具查询内部知识库和工单系统。\n"
"优先使用工具获取准确信息,而不是依赖自身知识。"),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"), # 存储中间推理过程和工具调用记录
])
# 4. 创建 Agent
# create_tool_calling_agent 封装了"推理 → 工具调用 → 结果回填"的编排循环
agent = create_tool_calling_agent(llm, tools, prompt)
# 5. 包装为 AgentExecutor(负责实际运行循环,直到 LLM 不再发出工具调用意图)
return AgentExecutor(
agent=agent,
tools=tools,
verbose=True, # 开启后可以观察每一轮推理和工具调用的详细日志
max_iterations=10, # 防止无限循环,限制最大工具调用轮次
return_intermediate_steps=True, # 返回中间步骤,便于调试和审计
)
Step 3:运行 Agent,观察完整交互链路
asyncdefmain():
agent_executor = await build_mcp_agent("./knowledge_base_server.py")
result = await agent_executor.ainvoke({
"input": "帮我找一下数据库慢查询的排查流程,然后根据内容给我一个简洁的操作步骤清单"
})
print("=== 最终回答 ===")
print(result["output"])
print("\n=== 中间步骤(工具调用链路) ===")
for i, (action, observation) inenumerate(result["intermediate_steps"]):
print(f"\n第 {i+1} 轮工具调用:")
print(f" 工具名: {action.tool}")
print(f" 输入参数: {json.dumps(action.tool_input, ensure_ascii=False, indent=2)}")
print(f" 返回结果(前 200 字): {str(observation)[:200]}...")
asyncio.run(main())
开启 verbose=True 后,你会在控制台看到如下输出,清晰展示了 §3.5.2 至 §3.5.4 中描述的完整推理-调用循环:
> Entering new AgentExecutor chain...
[推理轮次 1]
Invoking: `search_documents` with `{'query': '数据库慢查询排查流程', 'top_k': 3}`
[MCP tools/call 请求发出,Server 执行检索,返回结果]
文档搜索结果:找到 3 篇相关文档...
[推理轮次 2 - LLM 判断需要读取具体文档内容]
Invoking: `get_document_content` with `{'doc_id': 'wiki/db/slow-query-guide'}`
[MCP tools/call 请求发出,Server 返回文档全文]
文档内容:...
[推理轮次 3 - LLM 已有足够信息,直接生成最终回答]
以下是数据库慢查询排查的操作步骤清单:
1. 开启 slow_query_log,设置阈值为 1s ...
...
> Finished chain.
关键工程细节:LangChain 如何透明地处理工具格式差异
这里有一个值得关注的细节:上面代码中,仅仅切换 ChatOpenAI 为 ChatAnthropic,LangChain 就会自动将相同的 StructuredTool 列表转换为 Anthropic 的 tool schema 格式(input_schema 而非 parameters)。这正是”协议与模型解耦”在框架层面的体现——MCP Server 的工具定义不变,LLM 侧的格式差异由 LangChain 的 Chat Model 层统一抹平。
# 只需切换这一行,工具调用逻辑完全不变
# llm = ChatOpenAI(model="gpt-4o")
llm = ChatAnthropic(model="claude-3-5-sonnet-20241022")
多 MCP Server 的并行加载
实际项目中往往需要同时连接多个 MCP Server,工具来自不同的业务系统。在 LangChain 中,只需将多个 Server 的工具列表合并即可,AgentExecutor 会统一管理:
asyncdefbuild_multi_server_agent():
# 并行连接多个 MCP Server,提升初始化速度
tools_lists = await asyncio.gather(
load_mcp_tools("./knowledge_base_server.py"), # 知识库 Server
load_mcp_tools("./ticketing_server.py"), # 工单系统 Server
load_mcp_tools("./monitoring_server.py"), # 监控系统 Server
)
# 合并所有工具,统一注入给 LLM
all_tools = [tool for tools in tools_lists for tool in tools]
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# ... 后续构建 Agent 同上
这里揭示了 MCP 生态最核心的工程价值:每个 Server 独立开发、独立部署,而在 Host 层(LangChain AgentExecutor)被无缝整合。新增一个业务系统的 AI 能力,只需:① 实现一个新 MCP Server ② 在 asyncio.gather 中加一行——这就是 N+M 优于 N×M 在工程层面最直观的体现。
四、行业落地:通用解决方案视角
MCP 的落地价值,不在于”又一个 AI 功能”,而在于重构了 AI 应用的集成架构。以下从四个通用场景展开分析。
4.1 企业知识管理:从数据孤岛到统一上下文层
场景描述:企业内部存在多套知识系统——内部 Wiki、技术文档平台、工单系统、代码仓库、邮件档案。每个系统都有自己的 API 和数据格式。AI 助手需要跨系统理解和检索信息。
传统做法的问题:
-
• 每个 AI 应用各自对接各个系统,代码重复、维护成本高 -
• 检索结果的格式不统一,需要大量后处理才能注入上下文 -
• 系统权限管理分散,难以统一控制 AI 的数据访问范围
MCP 方案:
将每个知识系统封装为独立的 MCP Server。Wiki Server 提供文档搜索和内容读取的 Resources;工单 Server 提供工单查询 Tools;代码库 Server 提供文件浏览 Resources 和代码搜索 Tools。
所有 AI 应用(不论是 Chat 助手、IDE Copilot 还是自动化 Agent)都通过标准 MCP Client 接入,权限控制统一在 Server 侧实现。当新增一个知识系统时,只需实现一个新的 MCP Server,所有 AI 应用立即获得这个能力,而不需要逐一修改。
核心价值:将 AI 与知识系统的集成从 N×M 降低到 N+M,且权限边界清晰、可审计。
4.2 研发效能:AI 深度嵌入工程工具链
场景描述:研发团队希望 AI 不只是”代码补全”,而是能够真正参与工程流程——理解 CI 状态、分析测试失败、触发部署、查询监控数据。
MCP 方案的关键优势:
这个场景最能体现 MCP 的双向能力原语价值。以一个”智能 OnCall 助手”为例:
-
1. 监控系统 作为 MCP Server,提供告警查询 Tools( get_alerts、get_metrics)和监控数据 Resources(时序数据、日志流) -
2. 代码仓库 作为 MCP Server,提供最近提交记录、变更文件 Resources -
3. 部署系统 作为 MCP Server,提供部署历史查询 Tools 和(在授权下的)回滚操作 Tools
当告警触发时,AI 助手可以:
-
• 读取告警详情(Resource) -
• 查询关联的监控指标(Tool) -
• 搜索最近的代码变更(Resource) -
• 基于分析结果提出回滚建议,并在用户确认后执行(Tool + Human-in-the-Loop)
整个链路中,每个 MCP Server 独立维护,安全策略各自管理。核心 AI 逻辑与具体工程系统的耦合降到最低。
4.3 业务流程自动化:安全边界下的系统操作
场景描述:企业核心业务系统(CRM、ERP、财务系统)有大量重复性操作,希望 AI 辅助甚至自动完成。但这些系统的数据敏感,直接暴露数据库给 AI 是不可接受的。
MCP 的安全优势:
MCP Server 充当了业务系统的安全适配层:
-
• Server 暴露的工具是业务语义层面的操作( create_order、update_customer_status),而非底层 SQL -
• 每个工具都有精确的 JSON Schema 约束,不可能执行超出 Schema 描述的操作 -
• 敏感字段可以在 Server 层过滤,不暴露给 LLM 的上下文 -
• 操作日志在 Server 层统一记录,可审计、可回溯
相比”给 AI 一个数据库连接”的粗暴方案,MCP Server 提供了接口即权限边界的架构。每个可暴露的操作都是有意识的设计决策,而不是默认开放所有能力。
4.4 数据分析:从 BI 工具到 AI 原生数据接口
场景描述:数据分析师和业务方希望通过自然语言查询数据,而不是学习复杂的 BI 工具操作。
MCP 方案:
BI 平台作为 MCP Server 暴露两类能力:
-
• Resources:数据集元信息(表结构、字段说明、业务含义) -
• Tools:结构化查询(限制在预定义的查询模板范围内)、图表生成
LLM 通过读取 Resources 理解数据结构,通过 Tools 执行查询。关键设计是将”任意 SQL”转化为”参数化查询模板”——LLM 填入参数而非生成完整 SQL,大幅降低 SQL 注入风险。
对于多租户 SaaS 场景,MCP Server 可以在初始化阶段基于用户身份动态返回不同的工具集和资源列表——同一个 Server,管理员看到的 Tools 和普通用户看到的 Tools 不同。这是传统 REST API 难以优雅处理的场景。
五、MCP vs Agent Skill:一场有价值的技术对话
这是本文最想认真讨论的部分。两者经常被放在一起比较,但大多数讨论停留在表层。让我们从架构本质出发做一次深入分析。
5.1 各自解决什么问题
MCP 的核心问题域:异构系统集成与协议标准化
MCP 从一开始就是在回答一个互操作性问题:如何让任意 LLM 应用和任意工具系统之间能够”说上话”?它的本质是一个外部集成协议,关注点在边界——Host 和外部世界的边界如何定义、如何传递信息、如何协商能力。
MCP 解决的是横向集成问题:一个工具实现,所有 AI 应用受益。
Agent Skill 的核心问题域:能力模块化与快速组合
Agent Skill(以 OpenClaw Skills 为代表的实现形态)解决的是一个内部组织问题:如何将 Agent 的业务逻辑拆分成可独立开发、可快速组合、可按需加载的能力单元?它关注的是 Agent 内部的架构:SKILL.md 作为能力描述契约,脚本和工具作为执行载体,平台 Registry 作为发现机制。
Agent Skill 解决的是纵向组织问题:将复杂的 Agent 能力分而治之,保持内聚性。
5.2 架构层次的本质差异
从软件架构视角看,两者处于不同的抽象层次:
┌─────────────────────────────────────┐
│ 用户 / 业务逻辑层 │
├─────────────────────────────────────┤
│ Agent / LLM 应用层 │ ← Agent Skill 在这一层发挥作用
├─────────────────────────────────────┤
│ 能力集成与工具协议层 │ ← MCP 在这一层发挥作用
├─────────────────────────────────────┤
│ 外部系统 / 数据源层 │
└─────────────────────────────────────┘
Agent Skill 是应用层的能力组织模式。它定义的是”这个 Agent 能做什么”以及”Agent 的各个能力模块如何协作”。SKILL.md 是给 LLM 读的契约,本质是 prompt engineering 的结构化表达。Skill 的执行可以是调用 shell 命令、调用本地 API、甚至调用 MCP Server。
MCP 是协议层的连接标准。它定义的是”Agent 如何与外部世界通信”的底层语言。它不关心 Agent 内部怎么组织,只关心 Agent 与 Server 之间的消息格式和交互契约。
这个层次差异意味着:两者不是竞争关系,而是天然的分层协作关系。
5.3 核心优劣势对比
|
|
|
|
| 标准化程度 |
|
|
| 生态网络效应 |
|
|
| 开发复杂度 |
|
|
| 业务逻辑内聚性 |
|
|
| 上下文感知 |
|
|
| 动态能力 |
|
|
| 调试与可观测性 |
|
|
| 安全隔离 |
|
|
| 适合场景 |
|
|
5.4 融合路径:Skill 调用 MCP
最有价值的工程实践不是”选 MCP 还是选 Skill”,而是在 Skill 内部调用 MCP Server——用 Skill 提供业务语义层的组织,用 MCP 提供外部系统的集成能力。
具体形态:
-
1. 轻量场景:Skill 直接封装业务逻辑,使用 exec 或内置工具完成操作。适合私有、快速迭代的场景。 -
2. 集成场景:Skill 的实现中调用 MCP Client,连接到已有的 MCP Server(代码库、知识库等)。Skill 提供业务语义,MCP 提供数据和工具能力。 -
3. 服务化场景:将成熟的 Skill 逻辑封装为 MCP Server,对外提供标准接口,供其他 Agent 应用复用。这是 Skill 演进为 MCP Server 的自然路径。
这三种形态代表了能力封装从”内部工具”到”标准服务”的成熟度演进,而不是非此即彼的选择。
5.5 一个值得思考的深层问题
MCP 的 Prompts 原语和 Agent Skill 的 SKILL.md 在某种程度上解决了相似的问题:如何将领域专家的操作知识结构化地传递给 LLM?
MCP Prompts 的方式是:将提示模板封装在 Server 侧,通过协议动态获取,模板可以包含动态数据。
Agent Skill 的方式是:SKILL.md 是静态的能力说明,在加载时注入到 Agent 的系统提示中。
前者更动态、更灵活,但引入了额外的协议开销;后者更简单、更直接,但灵活性受限。这个设计权衡反映了两种哲学:MCP 倾向于将更多智能下沉到 Server 侧,Skill 倾向于保持 Agent 的中心性。
六、相关技术生态横向对比
6.1 vs OpenAI Function Calling / GPT Actions
Function Calling 是私有的、模型侧的特性,工具描述与调用约定由 OpenAI 定义,其他模型需要各自实现。GPT Actions 进一步将工具接入 ChatGPT 平台,但仍是封闭生态。
MCP 的根本区别在于协议开放性:任何 LLM、任何应用可以实现 MCP,不依赖特定厂商。当你的工具实现为 MCP Server,它可以被 Claude、Cursor、任何支持 MCP 的应用使用,而不需要为每个 LLM 各写一套。
6.2 vs LangChain / LlamaIndex Tools
LangChain 的 Tool 是一个框架层概念,解决的是”如何在 Python 代码中组织工具调用逻辑”。它是代码抽象,不是通信协议——工具的实现和调用发生在同一个进程中,不涉及跨进程或跨服务通信。
MCP 是进程间协议,Server 和 Client 是独立的进程(甚至独立的机器)。这使得 MCP Server 可以用任意语言实现,可以独立部署、独立扩缩容,可以在不同 Agent 框架之间共享。代价是引入了进程间通信的复杂度和延迟。
6.3 vs OpenAPI / REST
这个对比最能揭示 MCP 的本质创新。OpenAPI 描述的是”这个接口接受什么参数、返回什么数据”,面向人类开发者和代码生成工具设计。MCP 描述的是”这个工具做什么事情、在什么情况下应该被调用”,面向 LLM 的语义理解设计。
关键差异:
-
• 意图描述:MCP 工具的 description字段是给 LLM 读的自然语言,不是给人读的文档注释 -
• 参数语义:JSON Schema 中的 description同样面向 LLM,引导模型正确填充参数 -
• 双向通信:MCP 支持 Server 主动推送(通知)和 Sampling(反向 LLM 调用),REST 是纯请求-响应模式 -
• 会话状态:MCP 有明确的会话生命周期,REST 是无状态的
换句话说,OpenAPI 是数据接口的描述语言,MCP 是 AI 能力的描述语言。
6.4 与 A2A(Agent-to-Agent Protocol)的关系
2025 年,Google 发布了 Agent-to-Agent Protocol(A2A),试图标准化 Agent 之间的通信协议。这引发了业界关于 MCP 和 A2A 关系的讨论。
两者解决的是互补的不同问题:
-
• MCP:Agent 与工具/数据源的连接(Agent-Tool 层) -
• A2A:Agent 与 Agent 之间的委托和协作(Agent-Agent 层)
在多 Agent 系统架构中,两者可以同时存在:一个 Orchestrator Agent 通过 A2A 将子任务委托给 Specialist Agent,每个 Specialist Agent 通过 MCP 访问自己需要的工具和数据。这是一个自洽的分层架构。
七、展望:协议演进与未来挑战
7.1 生态建设的关键缺口
MCP 目前最大的短板不是协议设计,而是生态基础设施。
可发现性问题:目前没有官方的 MCP Server Registry。用户找到一个可用 Server 的方式主要是 GitHub 搜索和社区分享。一个标准化的、可信任的 Registry(类似 npm registry 或 Docker Hub)是生态爆发的前提。
信任与验证问题:如何确认一个 MCP Server 是可信的?恶意 Server 可以通过工具描述中的隐藏指令(Tool Poisoning)操控 LLM 行为。这需要 Registry 层面的签名验证和社区审核机制。
开发者体验:创建一个生产级 MCP Server 的工程成本仍然较高——需要处理协议握手、错误处理、日志、监控、授权。更好的 SDK、脚手架工具和最佳实践文档是近期最有价值的投入方向。
7.2 有状态工作流的支持
当前 MCP 的会话模型适合短时任务——发起一次工具调用,获取结果,继续对话。但企业级 Agent 越来越多需要长时工作流:任务可能持续几小时甚至几天,需要在中途暂停等待人工审批,需要断点续传能力。
这要求 MCP 在协议层面增强对有状态工作流的支持:
-
• 任务 ID 机制:为长时任务分配持久化的 ID,支持异步查询状态 -
• Checkpoint 协议:标准化任务状态的持久化和恢复接口 -
• Human-in-the-Loop 节点:将等待人工审批建模为一等公民的协议原语,而不是由 Host 应用自行处理
7.3 Multi-Agent 场景的协议栈融合
最值得关注的长期趋势是 MCP + A2A 的协议栈融合。当 Multi-Agent 系统成为主流架构,Agent 的角色会同时扮演多个身份:对 Orchestrator 而言是工具(MCP Server),对子 Agent 而言是调度者(A2A Client),对用户而言是 Host。
这个场景要求 Agent 框架能够无缝融合两种协议,并且统一处理权限、审计、上下文传递等横切关注点。未来的 Agent Runtime 可能会内置同时支持 MCP 和 A2A 的能力,让 Agent 开发者只关注业务逻辑,而不需要手工处理协议细节。
7.4 Agent Skill 的演进路径
对于 Agent Skill 生态,可以预见几个演进方向:
标准化增强:Skill 的 SKILL.md 约定会逐步形式化,从自然语言描述演进为包含结构化元数据的混合格式——类似 OpenAPI Spec,但面向 LLM 语义而非机器解析。
跨平台可移植性:随着 MCP 生态成熟,Skill 可以选择将自身对外暴露为 MCP Server,从而在不同 Agent 平台间复用。这是 Skill 从平台内工具演进为生态级能力的关键路径。
能力组合的协议化:当前 Skill 之间的组合调用(一个 Skill 内部调用另一个 Skill)是松散的。未来可能出现 Skill-to-Skill 的标准调用协议,将 Skill 编排从约定变为规范。
7.5 一个更大的图景:AI 原生软件架构
从更宏观的视角看,MCP 代表的是软件架构范式的一次转变尝试:从为人类工程师设计的接口(REST API、SDK)迈向为 AI Agent 设计的接口(语义描述、意图驱动、双向通信)。
这个转变还处于非常早期的阶段。MCP 解决了工具集成层的协议问题,但 AI 原生软件架构还需要在认证授权、数据隐私、可解释性、可审计性等多个维度上建立新的标准。
MCP 是第一块砖,不是终点。
结语
MCP 的出现,标志着 AI 工具集成从”每个团队造自己的轮子”走向”基于共识的协议互操作”。它没有解决所有问题,但它定义了一个值得投资的方向。
对于工程师和架构师而言,现在是一个好时机:
-
• 将现有的工具和数据接口封装为 MCP Server,为未来的多应用复用做准备 -
• 在 Agent 开发中同时拥抱 Skill 的敏捷性和 MCP 的互操作性,用分层架构而非非此即彼的选择 -
• 关注 Registry、安全、有状态工作流这三个近期最可能产生工程价值的演进方向
夜雨聆风