Skip to content

21.4 MCP(模型上下文协议)

上一节我们看到,从 Voyager 到 Hilbert,现代智能体的核心范式已经从"训练一个端到端模型"转向"编排多个专长不同的组件"。然而,一个尖锐的工程问题随之浮现:当智能体需要连接的外部工具越来越多时,每对"模型-工具"之间都需要编写独立的适配代码,集成成本呈指数级增长。 这正是 MCP(Model Context Protocol,模型上下文协议)试图解决的问题。

MCP 由 Anthropic 于 2024 年 11 月发布,其核心目标可以用一句话概括:为大语言模型与外部工具之间的交互建立一套统一的开放标准。 如果说 USB 统一了计算机与外设的物理接口,那么 MCP 就是试图统一 AI 应用与数据源、工具之间的逻辑接口。本节将从 MCP 的设计动机出发,逐层拆解其核心架构,对比它与传统 Function Calling 及 Google A2A 协议的异同,并讨论其实际应用场景与当前局限。


21.4.1 为什么需要 MCP:从 N×M 到 N+M

在 MCP 出现之前,大语言模型调用外部工具的主流方式是 Function Calling(函数调用)。我们在 21.1 节中已经介绍过 ReAct 框架的工作原理——模型在推理过程中生成结构化的函数调用请求,运行时将请求分发给对应的工具,再将结果注入上下文。OpenAI 在 2023 年为 GPT 系列模型引入的 Function Calling 机制正是这一思路的工业实现。

Function Calling 在技术上是可行的,但在工程实践中暴露出一个严重的可扩展性问题:每个 AI 应用(或模型提供商)与每个工具之间的集成都是独立的。假设市场上有 N 个 AI 应用、M 个工具服务,那么理论上需要编写 N×M 套适配代码。

没有 MCP 时的 N×M 集成问题

图 21-22:没有 MCP 时的集成困境。每个 AI 应用(上方)与每个工具(下方)之间都需要单独编写 Function Call 适配层,形成密集的多对多连接。当 N 和 M 增大时,集成成本迅速失控。

这种 N×M 的集成模式带来了三个具体痛点:

第一,重复工作。 假设 Slack 提供了一个"发送消息"的 API。Claude Desktop 需要为 Slack 写一套适配代码,Cursor 也需要写一套,ChatGPT 也需要写一套——三个应用做了本质上相同的事情。

第二,碎片化。 每个模型提供商对 Function Calling 的格式定义不同。OpenAI 用 functions 字段,Anthropic 用 tools 字段,Google 又有自己的格式。工具开发者必须为每个平台分别适配。

第三,升级耦合。 当工具 API 发生变更时,所有接入该工具的 AI 应用都需要同步修改——这在分布式开发中是运维噩梦。

MCP 的解决方案在概念上非常简单:在 AI 应用与工具之间引入一个标准化的协议层。 AI 应用只需要实现一次 MCP 客户端,工具提供商只需要实现一次 MCP 服务器,两者通过统一的协议通信。这将 N×M 的集成复杂度降低为 N+M

有 MCP 时的 N+M 集成模型

图 21-23:引入 MCP 协议后的集成模型。所有 AI 应用(上方)通过统一的 MCP 协议层(中间)连接所有工具(下方),集成复杂度从 N×M 降低到 N+M。

这种"中间协议层"的思路在计算机科学中并不新鲜。一个恰当的类比是 ONNX(Open Neural Network Exchange)。在深度学习框架百花齐放的年代,PyTorch、TensorFlow、MXNet 等框架训练出的模型无法直接在其他推理引擎上运行。ONNX 定义了一种中间表示(IR),任何框架导出的模型只要符合 ONNX 格式,就可以在任何支持 ONNX 的推理引擎上部署。MCP 之于 AI Agent 工具调用,正如 ONNX 之于模型部署——它不改变 AI 的本质能力,但极大降低了工程集成的摩擦。


21.4.2 MCP 的核心架构:Host / Client / Server

MCP 采用经典的 Client-Server(CS)架构,并在其上叠加了一个 Host 的概念。理解这三层模型是掌握 MCP 的关键。

MCP 的三层架构

图 21-24:MCP 的核心架构。Host(如 Claude Desktop)承载多个 MCP Client,每个 Client 与一个 MCP Server 建立一对一的连接。Server 通过本地资源或远程服务提供工具能力。

我们通过一个具体场景来理解这三层组件的协作。假设用户在 Claude Desktop 中提问:"我桌面上有哪些文件?"

Host(宿主应用) 是用户直接交互的 AI 应用程序——在这个例子中是 Claude Desktop。Host 负责接收用户输入、管理与大模型的对话,并在内部承载一个或多个 MCP Client。当大模型决定需要访问外部资源时,Host 负责协调 Client 的调用。

Client(客户端) 是 Host 内部的协议适配组件,每个 Client 维持与一个特定 MCP Server 的一对一双向连接。在我们的例子中,当大模型判断需要访问文件系统时,Host 中负责文件系统的那个 Client 就会被激活,向对应的 Server 发送请求。

Server(服务器) 是真正提供工具能力的组件。它封装了具体的工具逻辑、数据资源和上下文信息,通过 MCP 协议对外暴露标准化的接口。在这个例子中,文件系统 MCP Server 会执行实际的目录扫描操作,返回文件列表。

整个流程如下:

用户提问 → Host(Claude Desktop) 接收问题
    → 发送给大模型推理
    → 大模型判断需要文件系统信息
    → Host 激活文件系统 Client
    → Client 通过 MCP 协议连接 Server
    → Server 执行目录扫描,返回文件列表
    → Client 将结果传回 Host
    → Host 将结果注入上下文,大模型生成最终回答
    → 用户看到回答

这种架构设计的精妙之处在于职责分离:应用开发者只需关心 Host 和 Client 的实现(或直接使用现成的 SDK),工具开发者只需关心 Server 的实现。两者通过标准化协议解耦,互不影响。

三种标准化能力

每个 MCP Server 可以向 Client 暴露三种标准化的能力:

能力名称说明示例
工具(Tools)可执行的函数模型可以调用的具体操作,附带结构化的参数定义search_files(path, pattern)send_email(to, subject, body)
资源(Resources)结构化数据流文件、日志、数据库记录等数据内容,以统一格式提供给模型当前目录的文件列表、数据库查询结果
提示模板(Prompts)预定义的指令模板为特定工作流优化的 Prompt 模板,帮助模型更好地使用工具"请根据以下 SQL 查询结果回答用户问题:{result}"

这三种能力的统一定义是 MCP 区别于简单 Function Calling 的重要特征。Function Calling 通常只定义了工具(即可调用的函数),而 MCP 将工具、数据和 Prompt 三者打包为一个完整的服务单元,使得工具提供者可以同时控制"能做什么"(Tools)、"能看到什么"(Resources)和"应该怎么用"(Prompts)。

传输层:Stdio 与 HTTP SSE

MCP 定义了两种传输方式:

  • Stdio(标准输入/输出):Client 和 Server 在同一台机器上运行,通过标准输入输出管道通信。这种方式适合本地工具(如文件系统、本地数据库),延迟极低且天然安全——数据不经过网络。
  • HTTP SSE(Server-Sent Events):Client 和 Server 通过 HTTP 通信,适合远程服务。Server 通过 SSE 向 Client 推送事件,支持跨网络部署。

两种传输方式都基于 JSON-RPC 2.0 协议进行消息编码,保证了消息格式的一致性。

下面是一个最简化的 MCP Server 实现示例,它暴露了一个"获取当前时间"的工具:

python
# 一个最简 MCP Server 示例(使用 Python SDK)
from mcp.server import Server
from mcp.types import Tool, TextContent
import datetime

# 创建 MCP Server 实例
server = Server("time-server")

# 定义工具列表
@server.list_tools()
async def list_tools():
    return [
        Tool(
            name="get_current_time",
            description="获取当前的日期和时间",
            inputSchema={
                "type": "object",
                "properties": {
                    "timezone": {
                        "type": "string",
                        "description": "时区,如 'Asia/Shanghai'"
                    }
                },
                "required": []
            }
        )
    ]

# 实现工具逻辑
@server.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "get_current_time":
        tz = arguments.get("timezone", "UTC")
        now = datetime.datetime.now().isoformat()
        return [TextContent(type="text", text=f"当前时间:{now} ({tz})")]
    raise ValueError(f"未知工具:{name}")

这段代码清晰地展示了 MCP Server 的核心职责:声明自己拥有哪些工具list_tools)和执行具体的工具调用call_tool)。Client 通过 tools/list 请求获取工具清单,通过 tools/call 请求触发具体执行。

大模型如何通过 MCP 选择工具

理解了 Server 端的实现后,一个自然的问题是:大模型是如何"知道"该调用哪个工具的?答案是——完全通过 Prompt。当 Host 启动并连接所有配置的 MCP Server 后,每个 Client 会调用对应 Server 的 tools/list 接口,获取该 Server 提供的所有工具定义。这些工具定义(名称、描述、参数 Schema)会被拼接成文本,注入到发送给大模型的 System Prompt 中。

用伪代码描述这一过程:

python
# Host 端的工具发现与注入流程(简化伪代码)
def build_system_prompt(mcp_clients):
    tool_descriptions = []
    for client in mcp_clients:
        # 每个 Client 向对应的 Server 请求工具列表
        tools = client.request("tools/list")
        for tool in tools:
            tool_descriptions.append(
                f"工具名称: {tool.name}\n"
                f"功能描述: {tool.description}\n"
                f"参数: {json.dumps(tool.inputSchema)}\n"
            )

    # 将所有工具描述注入 System Prompt
    system_prompt = (
        "你是一个有用的助手。你可以使用以下工具:\n\n"
        + "\n---\n".join(tool_descriptions)
        + "\n\n当你需要使用工具时,请输出 JSON 格式的调用请求。"
    )
    return system_prompt

这意味着:模型对工具的理解完全依赖于工具描述文本的质量。 一个名称含义模糊、描述不清的工具,即使功能再强大,也可能被模型忽略或误用。这也是为什么 MCP 规范特别强调工具的 description 字段和参数的 description 字段必须清晰、准确——它们本质上是写给大模型"阅读"的文档。


21.4.3 MCP 与 Function Calling 的对比

MCP 和 Function Calling 解决的是同一个根本问题——让大模型调用外部工具。但两者在架构层次和设计理念上有本质区别。

MCP 中 Model-Client-Server 的交互流程

图 21-25:MCP 的完整交互流程。左侧的 Model 通过 MCP Client 的上下文窗口与右侧的 MCP Server 交互。Client 先通过 tools/list 获取可用工具定义,将其注入上下文;模型决策后,Client 通过 tools/call 执行工具并将结果反馈给模型。

维度Function CallingMCP
定义层次模型 API 级别的功能独立的开放协议标准
集成模式每个模型自定义格式统一的 JSON-RPC 协议
服务发现调用方硬编码工具定义Server 动态声明能力,Client 运行时发现
通信方式单次请求-响应双向持久连接(支持 Server 主动推送)
能力范围仅定义可调用函数工具 + 资源 + Prompt 模板
状态管理无状态支持会话级状态保持
生态复用工具代码与模型绑定同一个 Server 可被任意 Host 复用

两者最关键的区别在于复用性。Function Calling 的工具定义嵌入在 AI 应用的代码中——如果你为 OpenAI API 写了一个查询天气的函数,切换到 Claude API 时需要重新适配。而 MCP Server 是一个独立运行的服务进程,任何支持 MCP 协议的 Host 都可以直接连接使用,无需修改一行 Server 代码。

另一个重要区别是服务发现。在 Function Calling 模式下,开发者必须在调用模型 API 时硬编码所有可用工具的 Schema。而 MCP 支持运行时服务发现——Client 连接 Server 后,通过 tools/list 请求动态获取该 Server 提供的所有工具定义。这意味着新增工具时,只需更新 Server 端代码,所有连接该 Server 的 Host 自动获得新能力,无需任何客户端改动。

当然,MCP 并非 Function Calling 的替代品,而是对其的标准化封装。 MCP Server 内部的工具执行逻辑本质上仍然是"接收参数 -> 执行函数 -> 返回结果",与 Function Calling 的语义完全一致。MCP 的贡献在于将这一过程从"每个模型自定义的 API 特性"提升为"跨模型、跨应用的开放协议"。


21.4.4 MCP 与 Google A2A 协议的异同

2025 年 4 月,Google 发布了 A2A(Agent-to-Agent)协议,这是另一个旨在标准化 AI 生态的开放协议。MCP 与 A2A 的定位有本质区别,理解两者的关系对于把握 AI Agent 生态的全景至关重要。

核心区别:人-机 vs. 机-机

一句话概括:MCP 解决的是"智能体如何使用工具"的问题,A2A 解决的是"智能体之间如何协作"的问题。

MCP 的交互模型是智能体 → 工具。一个大模型驱动的智能体通过 MCP 协议连接外部工具(文件系统、数据库、API 等),工具本身不具备智能,它只是被动地执行指令并返回结果。

A2A 的交互模型是智能体 → 智能体。两个各自具备独立推理能力的智能体通过 A2A 协议进行对等通信。例如,一个"旅行规划智能体"可以通过 A2A 协议与一个"航班预订智能体"协作——后者不是一个简单的 API,而是一个能够自主理解需求、搜索航班、比较价格、处理异常的完整智能体。

维度MCPA2A
交互对象智能体 ↔ 工具/数据源智能体 ↔ 智能体
对方的智能程度工具无智能,被动执行对方是完整的智能体,具备自主推理能力
通信模式请求-响应(类似 RPC)任务委派与协商(类似人与人的协作)
能力发现tools/list 列出工具 SchemaAgent Card(JSON 描述文件)公布能力
典型场景查询数据库、读写文件、调用 API跨组织的多智能体协作、复杂任务分解
发起方Anthropic(2024.11)Google(2025.04)

互补而非竞争

MCP 和 A2A 并不是竞争关系,而是互补的两层协议。一个完整的 AI Agent 系统可能同时使用两者:通过 MCP 连接底层工具(数据库、文件系统、搜索引擎),通过 A2A 与其他智能体协作完成跨领域的复杂任务。

用一个类比来理解:MCP 相当于"人使用工具"——你用锤子钉钉子,锤子不会思考,它只服从你的指令。A2A 相当于"人与人协作"——你请一位水电工来修管道,水电工有自己的专业判断和工作方式,你们之间是协商关系。一个工程项目中,你既需要使用工具,也需要与其他专业人士协作。

谁更适合 MCP,谁更适合 A2A?

一个值得关注的趋势是:传统软件基础设施(操作系统、数据库、开发工具)倾向于通过 MCP 接入 AI 生态,而移动互联网时代的超级应用更可能通过 A2A 对外提供服务。

原因不难理解。以 PostgreSQL 为例,它本质上是一个"被动执行 SQL 的工具",不具备业务决策能力,天然适合作为 MCP Server——接受查询指令,返回数据结果。但设想一下美团——如果你希望 AI 助手帮你"订一家离公司近、人均 80 元以下的日料",美团不太可能愿意通过一个简单的 search_restaurants(location, price, cuisine) 工具接口暴露其搜索排序策略和价格数据。它更可能部署一个自己的"餐饮推荐智能体",通过 A2A 协议以对等身份与你的 AI 助手协商——这个智能体内部有自己的推荐算法、库存检查和异常处理逻辑,对外只暴露最终结果。

这一分化可以总结为:数据和工具走 MCP,智能和服务走 A2A。


21.4.5 实际应用场景与生态现状

截至 2025 年,MCP 的生态已经初具规模。官方维护的 MCP Server 仓库中已包含数百个现成的工具服务器,覆盖了开发者日常工作中的主要场景。

MCP 生态系统一览

图 21-26:MCP 的实际生态。以 Cherry Studio 客户端为例,左侧菜单中可以看到 MCP 已成为一级功能入口,右侧展示了可配置的多种模型服务。各类桌面 AI 应用正在将 MCP 作为标准的工具集成方案。

主要应用场景

  1. 开发工具集成:这是 MCP 最成熟的应用场景。通过 GitHub MCP Server,智能体可以直接搜索代码、创建 Issue、提交 Pull Request;通过文件系统 MCP Server,智能体可以读写本地文件;通过数据库 MCP Server,智能体可以执行 SQL 查询。像 Claude Code、Cursor 等编码智能体已广泛依赖 MCP 来扩展自身能力。

  2. 数据查询与分析:PostgreSQL、SQLite、Elasticsearch 等数据库系统都已提供官方或社区维护的 MCP Server。智能体可以通过自然语言提问,由模型生成 SQL 查询,通过 MCP Server 执行并获取结果。

  3. 网络信息获取:Web 搜索、网页抓取、API 调用等场景。智能体通过搜索引擎 MCP Server 获取实时信息,通过网页抓取 MCP Server 提取特定页面的结构化内容。

  4. 本地资源管理:文件系统的读写、桌面自动化操作、本地应用的控制。这类场景中 MCP 使用 Stdio 传输方式,数据全程不离开本地机器,天然保证了安全性和隐私。

MCP SDK 生态

MCP 的快速普及得益于丰富的多语言 SDK 支持。官方和社区维护的 SDK 覆盖了主流编程语言:

语言SDK 名称说明
TypeScript@modelcontextprotocol/sdk官方参考实现,功能最完整
Pythonmcp官方 Python SDK,支持 async
Javamcp-java-sdk社区维护,适合企业级应用
Kotlinmcp-kotlin-sdk适合 Android 和 JVM 生态
C#mcp-csharp-sdk适合 .NET 生态

对于大多数开发者而言,接入 MCP 生态的最低成本路径是直接使用现有的 MCP Server——官方仓库中已有数百个现成的 Server 覆盖了常见场景,开发者只需在配置文件中添加几行 JSON 即可启用。只有当需要连接内部系统或自定义数据源时,才需要编写新的 MCP Server。

一个完整的 MCP 配置示例

以 Claude Desktop 为例,用户通过一个 JSON 配置文件声明需要连接的 MCP Server:

json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/documents"],
      "transport": "stdio"
    },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_TOKEN": "ghp_xxxxxxxxxxxx"
      },
      "transport": "stdio"
    },
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/mydb"],
      "transport": "stdio"
    }
  }
}

配置完成后,Claude Desktop 会为每个 Server 启动一个独立的进程,创建对应的 MCP Client,通过 Stdio 管道建立连接。模型在推理时可以动态发现并调用这些 Server 提供的所有工具——无需在代码中硬编码任何工具定义。


21.4.6 MCP 的局限性与未来展望

尽管 MCP 在工程层面带来了显著的便利,但它并非没有局限。理解这些局限对于正确定位 MCP 在 AI 技术栈中的角色至关重要。

局限一:严重依赖模型的基础能力。 MCP 的工具选择机制本质上是基于 Prompt 的——Server 提供的工具描述(名称、docstring、参数说明)以文本形式注入模型的上下文窗口,模型通过理解这些文本来决定调用哪个工具。这意味着:工具描述的质量直接影响调用效果;模型的指令遵循能力和结构化输出能力决定了调用的准确性。基础模型能力不足时,再好的 MCP Server 也无法被正确使用。

局限二:工具数量受限于上下文窗口。 每个工具的描述文本通常包含名称、说明、参数 Schema 等信息,占用数百到数千个 token。当连接的 MCP Server 提供大量工具时,所有工具描述会占满模型的上下文窗口,挤压用户输入和推理的空间。这是一个基本的信息瓶颈——模型无法同时"记住"成千上万个工具。

局限三:每次调用只能选择有限的工具。 即使上下文窗口足够大,模型在单次推理中能够可靠选择和编排的工具数量仍然有限。当可用工具超过一定阈值时,模型的选择准确率会显著下降。

未来方向

针对这些局限,一个重要的探索方向是 MCP 即代码 API(MCP-as-Code-API),我们将在 21.8 节详细讨论。其核心思想是:让智能体通过编写代码来调用 MCP 工具,而不是直接在上下文中列出所有工具定义。 具体来说,系统会根据连接的 MCP Server 生成一棵文件树,每个工具对应一个代码模块。智能体在执行任务时,先通过浏览文件系统发现需要的工具,按需加载其定义,然后编写调用代码。

这种方案的效果是惊人的:Anthropic 的工程实践表明,采用 MCP-as-Code-API 后,上下文中的工具描述 token 从约 150,000 锐减到约 2,000——节省了 98.7% 的上下文空间。同时,由于中间数据在代码执行环境中处理而不进入模型上下文,还带来了数据隐私保护和复杂逻辑编排等附加收益。

另一个值得关注的趋势是 MCP 能力融入模型训练。目前 MCP 的工具选择完全依赖 Prompt 工程,但未来各模型厂商可能会将 MCP 工具描述的理解和调用能力作为后训练(Post-Training)的一部分,使模型原生具备更好的 MCP 工具使用能力。事实上,Claude 模型被认为在 MCP 工具调用上表现优于其他模型,一个合理的推测是 Anthropic 已经对其进行了相关的指令微调。


本节小结。 MCP 通过定义标准化的 Host-Client-Server 三层架构,将 AI 应用与工具之间的 N×M 集成问题简化为 N+M,显著降低了 AI Agent 生态的集成摩擦。它与 Function Calling 是"标准化封装"而非"替代"的关系,与 Google A2A 协议则是"工具调用"与"智能体协作"两个互补层次。MCP 的核心贡献在于加速了 AI Agent 与工具之间的连接通路,但它不改变模型的基础能力——真正决定 AI Agent 上限的,仍然是底层大模型的推理能力和工具使用能力。在 21.8 节中,我们将进一步探讨如何通过代码执行范式突破 MCP 的上下文瓶颈。