MCP 这东西,刚开始看很容易觉得:不就是让大模型调用外部工具吗?OpenAI 的 Function Call、Claude 的 Tool Use,不也能干这个?

表面看确实像。

但你多往下想一层就会发现,它们要解决的问题不是一个层级。Function Call 解决的是“模型怎么表达一次工具调用”,MCP 解决的是“智能体应用怎么安全、统一、可复用地连接外部世界”。

这也是理解 MCP 最关键的起点。

一、先把 MCP 的定位说清楚

MCP 到底是什么

MCP,全称是 Model Context Protocol,中文一般叫“模型上下文协议”。它最早由 Anthropic 开源出来,目标很明确:让大模型应用能用统一方式连接外部工具、数据源和上下文。

但是别被名字绕进去。MCP 不是一个具体框架,也不是某个 SDK,更不是某个模型独有的功能。它是一套开放协议,规定了 AI 应用和外部能力之间怎么通信、怎么发现能力、怎么调用工具、怎么返回结果。

用大白话说,MCP 更像 AI 时代的 USB 接口。

USB 不关心你插的是鼠标、键盘、U 盘还是摄像头,它只规定一套统一的连接方式。MCP 也是类似的思路:数据库、文件系统、GitHub、Slack、内部文档、业务接口,都可以包装成 MCP Server。只要客户端支持 MCP,就能按同一套协议接入。

以前你想让一个智能体读取数据库,通常要在这个智能体里写一套专门的 Function Call 逻辑。换一个智能体,可能又要重新适配一遍。今天 Cursor 写一套,明天 Claude Desktop 再写一套,后天自己的 Agent 框架还得继续写。

MCP 想干的事情就是:别每家都重复造轮子了。工具能力独立出来,大家按协议连接。

所以 MCP 的重点不是“让模型会调用工具”,而是让工具能力变成可以被不同智能体复用的标准服务。

下面这张图是来自 MCP 官网的架构图,可以直观地感受到 MCP 协议的”枢纽”作用:中间的 MCP 协议层作为统一标准,连接了左侧的智能体客户端(如 Claude、IDE)和右侧的工具(如数据库、文件系统等)。它将原本没有复用性的点对点连接,简化为了统一的接口调用,意味着只要支持 MCP 标准,左侧的任意客户端应用都能”即插即用”地连接右侧的任意工具,真正实现了智能体和工具的功能解耦。

为什么它必须是协议,而不是框架

理解 MCP 的第一原则是:真正的复用,一定发生在框架之外。

如果一个工具只能挂在某个 SDK 里,那它就天然被这个 SDK 绑定住了。你写了一个 Java SDK,Python 智能体怎么用?你接进某个 Agent 框架,换到另一个框架是不是又要重写?

工具要可移植、可组合、可复用,最干净的方式就是把它做成独立能力端点。智能体不需要知道工具是 Java 写的、Python 写的,还是跑在本地、容器里、远程服务器上。智能体只需要知道:这个 Server 暴露了哪些能力,每个能力怎么调用,结果会按什么格式返回。

这就是 MCP 为什么更像“HTTP 之于 Web”,而不是“MyBatis 之于数据库”。

框架解决的是某个生态里的开发效率,协议解决的是不同系统之间的协作边界。MCP 的价值就在这里:它把工具从智能体内部拆出来,变成外部可协商、可发现、可调用、可复用的能力服务。

二、MCP 的架构先看明白

MCP 的架构里有三个核心角色:Host、Client、Server。

  • Host 是宿主应用,也就是承载智能体能力的产品,比如 Cursor、Claude Desktop、Cline,或者你自己写的 Agent 应用。用户真正交互的是 Host。
  • Client 通常运行在 Host 里面,负责和某个 MCP Server 建立连接。一个 Host 可以连接多个 MCP Server,因此通常也会有多个 Client。可以把 Client 理解成 Host 里的“协议适配器”。
  • Server 则是独立的能力提供方。它把文件读取、代码搜索、数据库查询、GitHub 操作、内部系统调用等能力暴露出来,并按 MCP 协议响应 Client 的请求。

这个 Client-Server 结构看起来朴素,但它解决了以前工具和智能体强耦合的问题。工具代码不再放在智能体内部,而是独立运行。智能体只负责发现、选择和调度工具,不需要理解工具内部怎么实现。

这也是 MCP 能让工具标准化的前提。

三、MCP VS Function Call

在理解了 MCP 的整体架构之后,我们再回到一个核心问题:MCP 与传统的 Function Call 有什么区别?从表面上看,二者似乎都是为了让智能体调用工具,但实际上,它们在抽象层级、复用能力和工程复杂度上有着本质差异。

Function Call 流程痛点

在传统的 Function Call 模式中,整个流程本质上是一种“硬编码式集成“。每次的工具集成都是一次完整的开发,不可避免的就回重复造轮子、强耦合。所有环节都要由开发者自己实现。

并目这些逻辑全部都必须得在智能体内部“写死”。如果智能体数量增加,则每个智能体的接入代码都要重复书写。随着工具规模扩大,系统的耦合度越来越高,智能体的扩展成本也会急剧上升。

MCP 通过清晰的 Client-Server 分层架构解决了传统 Function Call 的“强耦合、难扩展、难管理”问题。工具不再需要嵌入到智能体内部,而是以独立的 MCP Sever 暴露能力;Client 负责通过 JSON-RPC 协议与 Sever 进行能力协商与通信;智能体则统一管理权限、上下文整合与大模型的调用。这样一来,工具接入不再需要在智能体中硬编码逻辑,功能边界更清晰,智能体也能通过工具的组合与复用轻松扩展。

graph TD
    subgraph "Your Computer"
        Host["Hoest with MCP Client<br/>(Claude, IDEs, Tools)"]
        
        ServerA["MCP Server A"]
        ServerB["MCP Server B"]
        ServerC["MCP Server C"]
        
        LocalA["Local Data Source A"]
        LocalB["Local Data Source B"]
    end

    subgraph "Internet"
        RemoteC["Remote Service C"]
    end

    %% 主机与服务器的连接
    Host -- "MCP Protocol" --> ServerA
    Host -- "MCP Protocol" --> ServerB
    Host -- "MCP Protocol" --> ServerC

    %% 服务器与数据源的连接
    ServerA <--> LocalA
    ServerB <--> LocalB
    ServerC -- "Web APIs" --> RemoteC

通俗来讲,Function Call 是“智能体直接带着自制的工具去工作”,而 MCP 则通过一套标准的协议与架构,把工具变成独立服务,由智能体统一调度,就像“在工具商店,挑选专业制造商制造的工具去工作”。智能体只需通过协议查询和调用,无需了解工具的内部实现即可直接使用,从而真正实现了工具的模块化、标准化和可插拔化。

四、MCP 的工作流程

下面用一张图,描述一下智能体通过 MCP 是如何来工作的。

MCP 工作流程

第一阶段:初始化(工具说明获取)智能体初始化的时候,会通过 MCP 协议向所有连接的 MCP Senver 使用 JSON-RPC 协议请求工具说明书。MCP Server 负责提供并确保这些说明书是标准化的 JSON 格式。

第二阶段:决策(大模型规划)智能体将用户的原始问题和获取到的所有标准化工具说明,一同发送给大模型,大模型根据这些信息进行规划,并返回一个清晰的工具调用指令。

第三阶段:调用(执行与结果回传)智能体接收到指令后,立即通过 MCP 协议请求对应的 MCP Sever 执行工具操作。MCP Server 完成实际的工具逻辑(如数据库查询),并将原始执行结果返回给智能体。

看到这里你应该就明白了:模型不直接碰你的文件系统、数据库或内部服务。模型只是做决策,真正动手的是 MCP Server。

也正因为如此,MCP Server 的权限边界、参数校验、审计日志和执行安全都非常重要。

五、大模型在工具调用里到底负责什么

这里有个边界一定要想清楚:不管是 Function Call,还是 MCP,大模型本身都不是那个真正执行动作的人。

大模型负责的是决策和规划。它看用户问题、上下文、工具说明,然后判断要不要调用工具、调用哪个工具、参数怎么填。

真正执行动作的是应用系统,或者更具体一点,是 Host 通过 Client 请求到的 MCP Server。

这个边界很重要。因为文件读取、数据库查询、退款、删除数据、发邮件这些动作都有真实副作用,不能把权限、安全、异常处理全部交给模型。模型可以建议执行动作,但不能成为权限系统本身。

工程上更稳的做法是:让模型在“你允许的工具集合里”做选择。当前页面、当前用户、当前业务状态能用代码确定的,就不要让模型猜。模型擅长理解意图和补全语义,确定性上下文应该交给工程系统。

六、MCP 是怎么做到把工具从智能体里拆出来的

要让工具不再绑定某个智能体,首先必须让工具的和智能体分离。

MCP 之所以能够做到这一点,正是因为它设计了 “client-server“ 的结构:

智能体作为客户端,向外部的 MCP Server 去请求能力;工具不再属于智能体,而属于独立的 Server。这种结构看似简单,却解决了此前所有工具耦合在智能体中的问题。

工具的代码不再放在智能体内部,也不再依赖于智能体的开发语言和运行环境,而是被包装为一个能够独立运行的能力端点。智能体只是与它进行通信,也不用关心工具是用 Java 写的、Python 写的,还是运行在什么平台上。这样工具就被从智能体内部拆到了系统外面,成为独立服务。

下面这个就是 MCP 官网提供的架构图。

graph TD
    subgraph "MCP Host (AI Application)"
        C1["MCP Client 1"]
        C2["MCP Client 2"]
        C3["MCP Client 3"]
        C4["MCP Client 4"]
    end

    %% 本地服务器连接
    C1 -- "Dedicated connection" --> SA["MCP Server A - Local<br/>(e.g. Filesystem)"]
    C2 -- "Dedicated connection" --> SB["MCP Server B - Local<br/>(e.g. Database)"]

    %% 远程服务器连接
    C3 -- "Dedicated connection" --> SC["MCP Server C - Remote<br/>(e.g. Sentry)"]
    C4 -- "Dedicated connection" --> SC

MCP HOST 其实就是我们的智能体应用,MCP Client 就是安装在 HOST 中的一段代码,用于与 MCP Server 交互获取工具信息的组件,而 MCP server 就是独立的工具服务,用于提供说明书和具体执行逻辑。

从图中也可以看出,client 和 server 是一对一的,而 host 中则可以接入多个 client 用于实现智能体的能力扩展。

七、MCP 如何把工具调用标准化

数据层和传输层分别解决什么

工具从智能体里拆出来之后,接下来要解决的是标准化。

如果每个 Server 都按自己的格式描述工具、自己的格式接收参数、自己的格式返回结果,Client 还是要为每个工具写适配逻辑,那就又回到了老路。

MCP 的标准化主要分两层:数据层和传输层。

  • 数据层规定大家交换什么消息。MCP 使用 JSON-RPC 2.0 作为核心消息格式,围绕初始化、能力发现、工具调用、资源读取、通知等动作定义了一组标准方法。
  • 传输层规定消息怎么传。定义了客户端和服务器之间进行数据交换的通信机制和通道,包括特定于传输的连接建立、消息帧和授权。MCP 支持本地 stdio,也支持远程 HTTP 方式。老版本里有 SSE,新方案里更推荐 Streamable HTTP。

你可以把它理解成两件事:JSON-RPC 解决“大家说什么语言”,传输层解决“用什么通道说话”。

JSON-RPC:MCP 的通用语言

所有工具都必须用统一的格式描述自己 —— 包括工具名、使用说明、参数结构、返回格式等。

这种描述不是随意写的,而是采用严格的 JSON 格式。智能体在连接 MCP Server 的第一步,就是向其索要所有工具的说明书。这一步在 MCP 中被称为“初始化”,对应一次标准的 JSON-RPC 请求。智能体无需关心工具是谁写的,也无需去理解其内部逻辑,只需要根据“说明书”就能让大模型进行决策判断,并最终发起调用。另外,工具的执行返回也遵循同样的标准格式,因此智能体无需适配不同工具的返回值,也不需要写任何特殊逻辑。

只要按标准写了工具说明书,任何工具就能被任何智能体直接使用,不需要额外的集成成本。

JSON-RPC 就是两个服务之间通过发送 JSON 数据来进行沟通的一种”通用语言”。

  • RPC(Remote Procedure Call):就是指远程过程调用。它的核心思想是:我在 A 服务上调用一个函数(比如 getWeather()),但实际上这个函数是在 B 服务上运行的,B 把结果运行完再传回给 A。
  • JSON:所有的数据传输都使用最通用的 JSON 格式,这意味着无论你的智能体是用什么编程语言写的,大家都能看懂对方发来的信息,而且这样的请求是非常简洁高效的。
  • 无状态(Stateless):每一次请求都是独立的,就像寄信一样,寄出去一封,收回来一封,协议本身不记忆之前的状态。

相比于复杂的 RESTful API(需要关心 HTTP 方法、URL 路径、Header),JSON-RPC 极其简单粗暴:只关心“需要调用的方法名”和“参数”。这与大模型调用工具的逻辑完美契合。

MCP 中的每一次请求,本质上都是在交换 JSON-RPC 2.0 标准的数据包。我们来看一下它的结构是什么样的。

请求体:

{
  "jsonrpc": "2.0",         // JSON-RPC 协议版本,MCP 固定使用 2.0
  "method": "tools/call",   // 调用方法:tools/call 表示调用外部工具/函数
  "params": {               // 请求参数体
    "name": "get_weather",  // 工具名称:要调用的具体工具名
    "arguments": {
      "city": "Beijing"     // 工具入参:传给工具的实际参数(键值对形式)
    }
  },
  "id": 1                   // 请求唯一标识,用于匹配请求和响应
}

响应体:

{
    "jsonrpc": "2.0",
    "result": {  // 结果:成功时的返回数据
        "content": [
            {
                "type": "text",
                "text": "北京今天晴,气温 25 度"
            }
        ]
    },
    "id": 1  // 对应请求的 ID
}

错误结构:

{
    "jsonrpc": "2.0",
    "error": {  // 错误对象
        "code": -32601,  // 标准错误码
        "message": "Method not found"  // 错误信息
    },
    "id": 1
}

MCP 如何利用 JSON-RPC 工作

在 MCP 架构中,JSON-RPC 并不是简单的数据传输工具,而是整个工具调用、状态管理和决策执行的核心标准语言。MCP 将智能体(MCP Host)、客户端(MCP Client)和工具服务(MCP Server)的交互完全规范化,使得每一次调用都遵循相同的流程。

初始化阶段:建立连接与能力协商

当 MCP Host 启动时,MCP Client 会向 MCP Server 发起 JSON-RPC 请求,进行初始化。

  • 能力协商:客户端和服务端会确认协议版本、功能支持等信息。
  • 准备就绪通知:初始化完成后,客户端会发送 notifications/initialized 告知服务器自己已准备好。
  • 意义:这一阶段保证双方在同一协议下通信,建立通信基础,为后续工具发现和调用做好准备。

示例请求:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "roots": {
        "listChanged": true
      },
      "sampling": {},
      "elicitation": {}
    },
    "clientInfo": {
      "name": "ExampleClient",
      "title": "Example Client Display Name",
      "version": "1.0.0"
    }
  }
}

初始化成功后,客户端必须发送 initialized 通知,表明其已准备好开始正常运行:

这是 MCP 协议的强制步骤,没有这一步,服务端会认为客户端未就绪,直接禁止后续所有操作(如工具调用、资源访问等)。

{
  "jsonrpc": "2.0",
  "method": "notifications/initialized"
}

工具发现阶段:获取工具说明书

初始化完成后,客户端需要知道服务器上“有哪些工具”可以使用。

  • 标准化描述:每个工具都以 JSON 格式描述方法名、参数类型、返回格式和使用说明。
  • 无需理解内部逻辑:智能体只需知道工具能做什么、需要哪些参数、返回什么结果,无需关心内部实现。
  • 动态发现:工具可能随时变化,每次获取的都是最新工具列表。

MCP Server 对外暴露三类核心能力:

  • Resources(资源):可供客户端直接读取的外部数据,如文件内容、API 返回结果等。
  • Tools(工具):具体执行的工具服务。(核心能力)
  • Prompts(提示):预定义的提示词模板。

示例请求:

{
  "jsonrpc": "2.0",       // JSON-RPC 协议版本,固定为 "2.0"
  "method": "tools/list", // MCP 工具发现阶段的请求方法,用于获取服务器支持的所有工具列表
  "params": {},           // 请求参数,此处为空对象,表示无额外参数
  "id": 100               // 请求ID,用于匹配服务器返回的响应
}

响应返回所有工具的说明信息。智能体在拿到这些说明后,就可以利用大模型构建调用策略和决策逻辑。

调用阶段:执行具体工具

当智能体通过大模型决策需要调用某个工具(如 get_weather)时,通过 MCP Client 构建标准 JSON-RPC 请求发送给 MCP Server。

{
  "jsonrpc": "2.0",         // JSON-RPC 协议版本,固定为 "2.0"
  "method": "tools/call",   // MCP 工具调用请求方法,用于执行指定工具
  "params": {
    "name": "get_weather",  // 要调用的工具名称
    "arguments": {          // 工具所需的参数(键值对形式)
      "city": "Beijing"     // 工具入参:查询天气的城市
    }
  },
  "id": 101                 // 请求ID,用于匹配服务器返回的响应
}

服务器收到请求后:

  • 解析工具名和参数:根据 name 找到对应工具执行。
  • 执行工具:独立运行工具逻辑,无需 Host 参与内部过程。
  • 返回结果:按照 JSON-RPC 的 resulterror 返回数据。 智能体接收到返回值后,可以直接处理输出、组合多个工具结果,或者继续做下一步决策。整个流程完全无需针对每个工具写不同的适配逻辑。

工具变更阶段:处理工具列表更新

在运行过程中,服务器上的工具可能会发生变更(新增、下线或修改)。MCP 支持客户端实时获知变更:

  • 变更通知:服务端通过 notifications/tools/list_changed 通知客户端工具列表已更新。
  • 重新获取列表:客户端收到通知后会重新请求 tools/list,更新内部工具注册表。
  • 保证一致性:智能体始终知道最新的可用工具,实现动态适应和高扩展性。

示例通知:

{
  "jsonrpc": "2.0",
  "method": "notifications/tools/list_changed",
  "params": {}
}

MCP 通过 JSON-RPC 的各阶段的调用,从而实现了工具的数据层的标准化、模块化和动态扩展能力。

MCP 传输层的标准化

JSON-RPC 搞定了 MCP 的数据层标准化。如果把 MCP 比作两个人的沟通,那么 JSON-RPC 就解决了“大家说什么语言(比如都说中文)”的问题。

那么接下来,我们必须解决“用什么工具通话”的问题。是面对面说话?打电话?还是发传真?这就是第二层标准:传输层。

MCP 官方前后共定义了三种主流的标准通信管道,分别对应不同的应用场景:Stdio、SSE(旧)和 Streamable HTTP(新)。

Stdio(标准输入输出)

也就是本地直连,这是 MCP 最原始、最高效的传输方式,也是 Claude、Cursor 等本地客户端智能体的首选集成方式。

Stdio 传输不涉及任何网络协议(TCP/HTTP),它的本质其实是进程间通信。当 MCP Host 启动一个 MCP Server 时,它实际上是在操作系统中 fork 了一个子进程。

  • 下行通道(Host -> Server):Host 将 JSON-RPC 请求序列化为字符串,写入子进程的 stdin(标准输入流)。
  • 上行通道(Server -> Host):Server 处理完后,将结果写入自己的 stdout(标准输出流),Host 监听这个流来获取响应。
  • 错误通道:日志信息通常写入 stderr,这样不会干扰正常的 JSON 数据流。

这个模式有两个明显优点。

第一,生命周期简单。Server 进程由 Host 管理,Host 关闭,Server 也跟着结束。

第二,没有网络开销。数据在本机进程间流转,延迟很低,很适合文件系统、本地项目、本地数据库这类场景。

所以你在本地开发工具里看到的大量 MCP Server,都会优先采用 stdio。

sequenceDiagram
    participant Client
    participant ServerProcess as Server Process

    Client->>ServerProcess: Launch subprocess
    loop [Message Exchange]
        Client->>ServerProcess: Write to stdin
        ServerProcess->>Client: Write to stdout
        ServerProcess-->>Client: Optional logs on stderr
    end
    Client->>ServerProcess: Close stdin, terminate subprocess

SSE(Server-Sent Events)

当工具部署在远程服务器上时,就不能再靠本地 stdin/stdout 了,需要走 HTTP。但在 HTTP/1.1 时代,服务器无法主动发消息给客户端。

所以早期 MCP 使用过 SSE,也就是 Server-Sent Events。SSE 的特点是服务器可以通过 HTTP 长连接持续向客户端推消息,但客户端不能通过这条连接反向发送消息。

于是 MCP 在 SSE 模式下通常要维护两条通道:Client 通过 GET /sse 建立一个长连接,用来接收 Server 推送;Client 如果要发送工具调用请求,则再通过另一个端点,比如 /messages,发起 HTTP POST。

  • 接收通道(Events Channel)
    • Client 发起一个 GET /sse 请求。
    • Server 保持连接不关闭(Keep-Alive),并设置 Content-Type: text/event-stream
    • 一旦有消息(如工具执行结果、日志),Server 就通过这个长连接推送给 Client。
  • 发送通道(Post Channel)
    • 当 Client 需要调用工具时,它无法通过 SSE 连接发送。
    • Client 必须向 Server 的另一个端点(如 /messages)发起一个新的 HTTP POST 请求。

这样的模式下的痛点有两个。

首先是状态维护比较麻烦,Client 必须同时维护一个长连接(听)和一个短连接(说)。

其次重连机制无法自动断点重连,需要双方自己实现定时心跳检测来维护长连接。

打个比方,就像用收音机听指挥(单向接收 Server 消息),但要汇报工作时,却必须另外跑去邮局寄信(独立发送)。虽然能通,但整个过程很笨重。

sequenceDiagram
    participant Client
    participant Server

    Client->>Server: Open SSE connection
    Server-->>Client: endpoint event
    loop [Message Exchange]
        Client->>Server: HTTP POST messages
        Server-->>Client: SSE message events
    end
    Client->>Server: Close SSE connection

Streamable HTTP

Streamable HTTP 的出现,就是为了解决 SSE 模式中读写分离带来的架构复杂度和传统 HTTP 的一次性交互无法满足 MCP 双向异步通知、以及无法断点重连等问题。

Streamable HTTP 的核心是统一和流式化。它将所有交互都汇集到一个标准的 HTTP POST 请求中,并利用 HTTP 的流式能力来承载 MCP 的 JSON-RPC 协议。

  • 单一连接:Client 只需要连接一个统一的端点(/mcp),无需维护两个分离的通道。
  • 请求发送:Client 将 JSON-RPC 报文放在 POST 请求的 Body 中发送给 Server。
  • 流式响应:Server 接收请求后,并不会立即关闭连接。它会利用 HTTP 协议的分块传输编码(Chunked Encoding),将响应(包括结果、日志、通知)分批次、实时地写入 POST 响应的 Body 中。

核心优势

  • 简化架构:只需要一个 HTTP 端点,不再读写分离即可实现双向异步通信。
  • 灵活性强:可以根据场景选择是否使用流式 SSE 响应,不必每次都保持长连接,支持无状态模式。
  • 资源效率更高:不用一直维持 SSE 长连接时,节省资源;需要流式时也能按需开启。
  • 更可靠的恢复机制:支持断点重连,可恢复会话(通过 Session ID + Last-Event-ID),通信更稳定。
  • 基础设施更友好:与标准 HTTP 基础设施(API 网关、负载均衡、云服务)兼容性更好。
  • 向后兼容:既可以支持新的 Streamable HTTP,也可以保留旧 SSE 通道,方便平滑升级。
sequenceDiagram
    participant Client
    participant Server

    %% 初始化阶段
    rect rgb(255, 248, 181)
        note over Client, Server: initialization
        Client->>Server: POST InitializeRequest
        Server-->>Client: InitializeResponse<br/>MCP-Session-Id: 1868a90c...
        Client->>Server: POST InitializedNotification<br/>MCP-Session-Id: 1868a90c...
        Server-->>Client: 202 Accepted
    end

    %% 客户端请求阶段
    rect rgb(255, 248, 181)
        note over Client, Server: client requests
        Client->>Server: POST ... request ...<br/>MCP-Session-Id: 1868a90c...
        alt [single HTTP response]
            Server-->>Client: ... response ...
        else [server opens SSE stream]
            loop [while connection remains open]
                Server-->>Client: ... SSE messages from server ...
            end
            Server-->>Client: SSE event: ... response ...
        end
    end

    %% 客户端通知/响应阶段
    rect rgb(255, 248, 181)
        note over Client, Server: client notifications/responses
        Client->>Server: POST ... notification/response ...<br/>MCP-Session-Id: 1868a90c...
        Server-->>Client: 202 Accepted
    end

    %% 服务器请求阶段
    rect rgb(255, 248, 181)
        note over Client, Server: server requests
        Client->>Server: GET<br/>MCP-Session-Id: 1868a90c...
        loop [while connection remains open]
            Server-->>Client: ... SSE messages from server ...
        end
    end

八、工程落地时真正要注意什么

MCP 能解决什么,不能解决什么

MCP 很重要,但它不是银弹。

它解决的是接入标准化问题:工具如何被发现,如何描述,如何调用,如何返回,如何被不同智能体复用。

但它不会自动解决所有工程问题。

一个 MCP Server 如果权限开太大,照样危险。一个工具描述写得很烂,模型照样可能误用。一个返回结果不可靠的内部系统,接上 MCP 之后也不会自动变可靠。协议把路铺好了,但不能保证路上的车都开得稳。

所以落地 MCP 时,仍然要回到工程基本功。

工具不要一股脑全暴露给模型。当前用户、当前页面、当前业务流程允许什么,就只给它什么。参数要做 schema 约束,也要在服务端二次校验。涉及副作用的工具,比如退款、删除、发消息,要有权限判断、二次确认、审计日志和幂等控制。

工具描述也要认真写。名字要清楚,描述要明确告诉模型什么时候该用、什么时候不该用。参数尽量扁平,能用枚举就不要让模型自由发挥。返回值要让模型看得懂,不要只返回内部错误码或晦涩字段。

这点和 Function Call 的工程经验是相通的:模型输出是输入源,不是可信边界。

为什么 MCP 现在会变得重要

原因很简单:Agent 应用越来越多了。

以前我们用大模型,更多是聊天。你问一句,它答一句。最多帮你写段代码、总结一篇文章。

但 Agent 不一样。Agent 要做事。

它要读文件、改代码、跑测试、查日志、搜文档、访问数据库、调用 SaaS、操作浏览器。只要它开始做事,就必然需要连接外部世界。

如果每个产品都自己接一遍数据库、GitHub、文件系统、Slack、内部接口,很快就会变成一堆重复、混乱、不可维护的适配层。

MCP 的出现,本质上是在给 Agent 时代补一层基础设施。

就像早期互联网需要 HTTP,服务之间需要 REST 或 gRPC,编辑器和语言服务之间需要 LSP。AI 应用要连接外部上下文和工具,也需要一个大家都能遵守的协议。

它真正改变的不是某一次工具调用,而是工具生态的组织方式。

一个 GitHub MCP Server 不应该只服务某一个 AI 应用。一个数据库 MCP Server 也不应该每接一个 Agent 框架就重写一遍。工具能力应该像基础设施一样,被多个支持协议的客户端复用。

最后

MCP 不是 Function Call 的换皮。

Function Call 的核心价值,是把自然语言意图变成结构化工具调用。它让模型能表达“我要调用哪个函数,参数是什么”。

MCP 的核心价值,是把外部能力包装成模型应用可以发现、可以理解、可以调用的标准服务。它通过 Host、Client、Server 的分层,把工具从智能体内部拆出来;通过 JSON-RPC 统一数据层;通过 stdio、SSE、Streamable HTTP 这些传输方式适配本地和远程场景。

说白了,Function Call 解决的是模型怎么点菜,MCP 解决的是厨房、菜单、传菜口和后厨系统怎么标准化。

等你从这个角度看 MCP,就不会再把它简单理解成“又一个工具调用方案”了。

它真正想做的,是给 Agent 连接真实世界这件事,定一套更通用的规矩。