跳到正文
AI 知识地图 0.18 · 2026-07-30
关于与纠错文字目录 / Search
理解原理

MCP 架构:让 Host 在隔离边界内连接外部能力

从 Host–Client–Server、JSON-RPC、能力协商与三类原语,理解 MCP 标准化了什么,以及授权与执行安全仍由谁负责。

核心命题 MCP 把 AI 应用与工具/数据源之间的连接从 M×N 个专用适配器变成共享协议;它规定通信和发现方式,却不替 Host 替用户做信任、授权与副作用决策
读完你应该能:画出 Host、Client、Server 与传输边界;手工追踪初始化和工具调用;区分 Tools、Resources、Prompts 的控制权;设计最小权限、超时与审计策略。
  1. Host选择并信任一个Server
  2. 为Server创建隔离Client
  3. 协商版本与双向能力
  4. 发现原语并建立本地目录
  5. 模型提出或应用选择操作
  6. Host授权后发送最小请求
  7. 验证结果并记录副作用
  8. 取消、重连或关闭会话

1协议解决的是复用,不是模型是否会调用工具定位

在模型已经具备函数调用(function calling)能力之后,为什么还需要 MCP?要回答这个问题,得先分清两类机制各自解决什么问题。工具调用描述的是模型怎样产生结构化意图:模型面对一个请求,输出一份“我想调用某个函数、参数是什么”的结构化表示,但它并不知道也不关心这个函数背后连着什么系统。MCP 描述的则是应用怎样发现、读取和调用来自不同提供方的能力——也就是说,当模型已经表达出意图之后,实际去找到那个工具、读取它的输入输出定义、把调用发过去、把结果带回来,这些连接层面的工作才是 MCP 关心的范围。

把这两个层面分开,MCP 的价值就清楚了。假设有 M 个 Host(承载 AI 应用的宿主程序)需要接入 N 个外部系统(工具提供方)。如果没有公共协议,每个 Host 都要为每个系统单独写一套适配器,连接数量近似于 M×N,而且每一对组合的发现逻辑、错误处理、取消机制都要重新实现。引入公共协议之后,每个 Host 只需要实现一个 Client,每个提供方只需要实现一个 Server,主要工作量就从 M×N 降到了接近 M+N。这才是 MCP 解决的核心问题:不是“让模型学会调用工具”,而是消除重复集成,把连接契约标准化。

但要避免一个过度解读:这不等于“写一次就永远兼容”。协议统一的是消息交换的边界——发现、协商、错误、取消、审计各自有什么消息、由谁发出、在什么阶段生效;而 schema(某个具体工具的参数结构)、语义(参数的业务含义)、权限(谁被允许做什么)以及协议版本本身仍然会随着业务演进而变化。MCP 不会替应用消除这些变化,也不会替模型决定“是否应该调用某个工具”,更不会替用户决定“是否授权这次调用”。它只是让这些变化有共同的契约边界可以依赖。

因此,MCP 可以理解为 Host 与外部能力之间的公共连接协议:它的输入是形形色色的 AI 应用和工具提供方,输出是统一的发现、调用、错误与生命周期消息。它把专用适配器的数量从近似 M×N 降到 M+N,解决的是重复集成问题;它统一的是交换契约本身,而不是保证 schema 永久兼容,也不负责模型是否应调用或用户是否授权的决策。把握住这层边界,才能理解后面各章节中协议所覆盖的每一种消息、每一个角色,其职责范围究竟在哪里。

2三个角色与一对一会话架构

MCP 把一次连接拆成 Host、Client、Server 三个角色,而不是笼统地叫“应用”。这个拆分不是命名上的讲究,而是为了划清各自知道什么、不该默认得到什么。

Host 是用户面对的那个 AI 应用本身。它拥有完整会话,负责模型选择、用户同意和安全策略,并且知道自己要达成的用户目标、需要建立哪些连接、遵循哪些策略。Client 由 Host 为每个 Server 分别创建:一个 Host 连接三个 Server,就有三个 Client。每个 Client 只维护它与那一个 Server 的专用会话,负责路由协议消息,也只掌握单个 Server 的能力与会话状态。Server 则是暴露能力的提供方,聚焦于一组工具、资源或提示,可以运行在本机进程里,也可以跑在远端服务上,它只接收完成一次请求所需的最少参数。

这三层的职责边界,核心是“知道什么”与“不应该默认得到什么”。Host 需要用户目标、连接和策略,但它不应默认拿到某个 Server 的内部凭证——内部密钥是 Server 自己的。Client 需要单个 Server 的能力与会话状态,但不应默认得到其他 Server 的私有上下文;一个 Client 的会话里不该出现另一个 Server 的内部数据。Server 需要完成请求所需的参数,但不应默认拿到完整对话,也不应默认拿到任意的本机数据。

所以整个过程可以这样理解:输入是用户会话、单个连接和服务能力,输出是 Host、Client、Server 三层责任划分——Host 持有用户目标与策略,Client 只维护一个 Server 的会话,Server 只接收完成请求所需的最少参数。这种划分让工作可以隔离跨源上下文:来自不同 Server 的数据不会在 Client 层互相串扰,因为每个 Client 只守着自己那一条连接。

最后要澄清一点:一对一会话描述的是通信边界,而不是信任声明。Client 与某个 Server 维持一对一会话,只说明它们之间有一条专属的消息通道,并不意味着这个 Server 已经可信,也不意味着 Server 有权读取完整对话。隔离的是上下文和消息路由,信任仍然需要由 Host 的策略和用户同意来单独决定。

角色应知道什么不应默认得到什么
Host用户目标、连接和策略Server内部凭证
Client单个Server的能力与会话其他Server的私有上下文
Server完成请求所需参数完整对话与任意本机数据

3数据层与传输层必须分开理解分层

看到 stdio 和远程 HTTP 两种连接方式时,容易以为这是两套不同的 MCP。实际上 MCP 需要分成两个独立层面来理解:数据层定义的是协议本身“说什么”,传输层定义的是“这些话怎么送过去”。

数据层规定 JSON-RPC 的请求、响应、通知、生命周期以及各类原语的语义。也就是说,工具发现、调用、错误返回、取消这些操作各自对应什么方法、携带什么字段、处于哪个生命周期阶段,都由数据层决定。传输层则负责进程或网络连接、消息分帧与认证:它解决的是消息以什么方式在两端之间可靠传递。stdio 常用于 Host 启动本地 Server 的场景——Host 拉起一个子进程,通过标准输入输出与它通信;Streamable HTTP 更适合远端服务,通过 HTTP 连接收发消息。

分层的关键结论是:上层方法相同,不代表威胁相同。无论走 stdio 还是走 HTTP,数据层的方法语义是一样的,但两套传输暴露的风险完全不同。本地进程继承主机上的文件和环境风险——一个本地 Server 天然就能读宿主机能读的文件、继承宿主机的环境变量;远程端则新增了网络身份、令牌、租户隔离和重放等风险,攻击面从“本机程序”扩展到了“网络上的不可信实体”。

正因为如此,授权属于传输和应用安全的一部分,而不是数据层协议自动提供的。成功建立连接只证明双方能够通信,绝不证明某一次工具调用符合用户意图。一个 HTTP 连接握手成功、令牌校验通过,只能说明“对端是谁、连接是通的”,至于“它现在请求的这个工具调用该不该被允许”,仍需由应用层根据用户同意和策略另行判断。

把这两层分开,输入是同一条协议消息和具体的连接方式,输出是数据层语义与传输层风险两套认知:数据层规定 JSON-RPC 方法、生命周期和原语,传输层负责 stdio 或 HTTP 的连接、分帧和认证。上层方法相同不代表风险相同,本地进程与远端服务都必须另做身份、权限和内容信任判断——不能因为“协议是同一个”就假设安全边界也是同一个。

4完整示例:逐步追踪一次初始化与工具调用案例推演

把前面几节的角色、分层概念落到一条真实的消息流上,就能看到一次连接从零到拿到结果之间经历了哪些可检查的状态。以天气 Server 为例:Client 从零连接它,最终拿到天气结果,中间每一步都是一个可以停下来验证的状态点。

第一步,Client 发送 initialize 请求,声明自己支持的协议版本、Client 能力和实现信息。第二步,Server 返回协商后的版本、它提供的 tools、resources 等能力以及它自己的实现信息;如果双方版本不兼容,Client 应当断开连接,而不是硬着头皮继续。第三步,Client 发送 notifications/initialized 通知,确认初始化完成,双方进入正常操作期。第四步,Client 调用 tools/list,拿到 get_weather(city) 这个工具的输入 schema——也就是告诉 Client:要调用它,需要传一个 city 参数,参数长什么样。

到这里连接就绪了,真正干活的是后面几步。模型提出 {city:"杭州"} 这样的结构化意图;Host 先做策略和同意检查,确认这个调用符合用户授权后,Client 才发送 tools/call。Server 返回内容,或者返回协议错误;关键在于 Host 拿到的是带来源标记的结果——它把这份结果当作不可信数据交给模型去理解,而不是自动当作指令去执行。Server 返回的文本不会直接驱动后续动作,它只是“某个来源声称的内容”,最终解释权在模型和 Host 的策略之下。

把整个过程的时间成本看成一个求和:MCP 总延迟约等于连接协商、能力发现、策略决策、工具执行与模型消费这几段时间之和。这就是为什么后面会有各种优化。缓存工具列表可以省掉能力发现的时间,但它有前提:一旦收到列表变更通知,或者重新建立连接,就必须按新能力重建缓存,不能继续沿用旧列表。请求超时之后要取消并停止等待,避免一个晚到的“幽灵结果”在动作已经推进之后又触发一次重复操作——旧缓存和盲目重试都不安全。

所以这个初始化案例的输入是 Client/Server 的版本、能力和天气工具的 schema,输出是协商后的会话与一份带来源标记的天气结果。双方先 initialize、再确认 initialized、发现工具、由 Host 审批后调用;总延迟由连接协商、发现、策略决策、工具执行和模型消费相加构成。缓存可以缩短发现时间,但版本变化、超时或晚到结果都会让旧缓存和盲目重试变得不安全。

TT连接协商+T发现+T策略+T工具+T模型

5原创图:Host 是跨 Server 的唯一协调与信任汇合点可视化

为什么 Server A 不该直接读取 Server B,也不该直接读取完整会话?一张结构图可以把这个约束说清楚。图中,Host 位于中心,内部包含模型与策略,它下面挂着两个相互隔离的 Client:一个连接本地 Server,另一个连接远程 Server。两个 Client 之间没有通道,本地 Server 和远程 Server 之间也没有任何直连。

图 1 表达的核心关系是:协议连接可以复用,信任却不能传递。Host 可以复用同一套连接机制去接多个 Server,但绝不意味着 Server A 因此可以借道 Host 去接触 Server B 的数据。Host 必须保持连接隔离,并控制上下文在哪里汇合。每个 Server 各自只得到完成请求所需的最少信息,返回的结果由 Host 在本地汇合、由模型统一理解,而不是让 Server 之间彼此交换。

这张架构图的输入是多个 Server 以及它们各自所需的上下文,输出是由 Host 协调的一组隔离 Client 会话。Host 只向每个 Server 传最少信息,并在本地汇合结果,从而避免 Server 彼此直连而获得跨源数据。Server A 拿不到 Server B 的上下文,也不该拿到完整对话,因为完整对话属于 Host 的会话层,不属于任何一个 Server 的请求参数。

最后一点必须讲明:图中的隔离表示的是设计责任,而不是协议强制。协议本身不会阻止泄漏——如果 Host 主动把完整会话复制给某个 Server,或者让多个 Server 共用同一套凭据,那么泄漏照样发生,协议不会站出来拦截。隔离能够成立,靠的是 Host 确实执行了“最小信息传递 + 本地汇合”这条设计约定,而不是靠传输层或数据层替它把关。

Host:用户同意 · 模型 · 策略 · 审计Client A与 Server A 1:1Client B与 Server B 1:1Host 统一做跨源编排只传最少必要上下文,不共享完整会话隔离,不直连Server A · 本地文件stdio · 进程与文件权限Tools / ResourcesServer B · 远程工单HTTP · 身份与租户边界Tools / Prompts
图 1 协议连接可以复用,信任却不能传递;Host 必须保持连接隔离并控制上下文汇合。

6三类 Server 原语的控制主体不同原语

Tools、Resources、Prompts 看起来都是“Server 提供给模型的东西”,但它们各自的控制主体和风险并不相同。把它们混成一类,是很多实现出错的原因。

Tools 的典型控制者是模型提出、Host 批准:模型表达调用某个动作的意图,Host 做策略检查后放行。它执行的操作是发现并调用动作,主要风险在于副作用、参数与权限——工具会真实地改状态、消耗资源,参数是否合法、调用者是否有权限,都需要在放行前检查。

Resources 的控制者是应用,而不是模型。应用选择要读取哪些上下文数据,操作是列出并读取这些资源。它的主要风险是越权读取、注入与过期:资源内容是上下文数据,可能被注入恶意指令,也可能已经过期但应用仍按新数据使用。

Prompts 的控制者是用户。用户选择获取可复用的提示模板,操作是拉取这些模板。它的主要风险是隐藏指令与版本漂移:模板里可能藏着设计者不透明的指令,模板版本也可能在更新后与用户预期不一致。

把三者的区分整理出来就是:模型提出 Tool,应用选择 Resource,用户选择 Prompt,而 Host 负责最终策略检查。这个划分解决的核心混淆,是把所有 Server 内容都当成同一种模型输入来对待——实际上它们的控制者不同,校验责任也不同。

还有一个方向反转的情况:Server 也可能反过来使用 Client 暴露的能力,例如 sampling、roots 或 elicitation。当消息方向反过来、Server 请求 Client 侧的能力时,信任方向也随之反转——原本由 Host 审查 Server 输出,现在变成要审查 Server 提出的请求。但无论方向如何,Host 都不能因为消息“来自 MCP”就跳过用户同意、输入校验或输出隔离。任何原语——不管是工具、资源、提示还是反向能力——仍然可能越权、过期或包含提示注入,这些风险不会因为走了协议就自动消失。

原语典型控制者操作主要风险
Tools模型提出、Host批准发现并调用动作副作用、参数与权限
Resources应用选择列出/读取上下文数据越权读取、注入与过期
Prompts用户选择获取可复用模板隐藏指令与版本漂移

7能力协商不是授权清单安全

Server 在能力协商时声明自己支持 tools,是否就等于允许调用它的所有工具?不是。能力协商和授权是两件输入、输出都不同的判断。

能力协商的输入是双方支持的协议特性,输出是本次会话中可以使用的方法集合——它回答的是“协议层面,我们能互相说什么”。授权的输入是主体、资源、动作和作用域,输出是某次具体请求是否被允许——它回答的是“这个用户对那个资源做这个动作,行不行”。能力只说明协议特性可用,不说明某个具体主体可以访问哪个资源。一个 Server 声明支持 tools,只能解释为“协议支持工具这个能力”,不能解释为“任意用户可以调用所有工具”。

因此 Host 侧的流程应该是:先校验 Server 的身份和配置,确认对端是谁、配置是否符合预期;然后对工具逐项做允许列表、参数约束、用户/项目作用域和风险分级,而不是“支持即放行”。Server 侧同样不能把授权外包给 Client:它不能信任 Client 传来的用户 ID,仍要执行真正的资源授权——因为 Client 声称的身份可能被伪造,权威判断必须落在掌握资源的那一端。

传输层的安全措施也要分层落地。本地 Server 应使用最小进程权限和干净的环境,让它只拥有完成工作所需的那一点点权限,不继承宿主机的完整能力;远程 Server 应使用短期、受众绑定的令牌,令牌过期快、且只能用于指定受众,降低被窃取后的复用价值。

最后,Server 返回的内容一律按不可信数据处理。尤其是资源文本或工具描述,绝不能覆盖系统策略——一个工具描述里写“请忽略之前的规则”,不会因为它是通过 MCP 返回的就获得更高权威。能力协商告诉 Host“可以调用”,授权告诉 Host“这次调用允许”,而内容可信度需要另做判断,三者不能互相替代。

8失败恢复要覆盖超时、取消、重连与幂等可靠性

工具已经执行但响应在传输中丢了,这时重试会发生什么?答案取决于动作是读还是写。

读操作通常可以安全重试:查一次天气没收到结果,再查一次,副作用为零,两次结果即使略有差异也不破坏状态。写操作则不然——如果“扣款”已经执行成功,只是响应丢了,盲目重试就会扣两次。所以写操作必须携带幂等键,或者先查询状态再决定是否重试。幂等键让 Server 能识别“这是同一次逻辑请求的第二次到达”,从而只执行一次效果;先查询状态则是主动确认上次到底成没成。

超时和取消也需要明确语义。每个请求都应设置可配置的超时;即使收到进度通知,也仍要保留一个最大时限,不能因为“有进展”就无限等下去。取消之后,Server 应尽力终止正在进行的动作,但 Host 不能把取消当成“动作一定没发生”。取消只表示 Host 不再等待,不能证明远端动作没有完成——动作可能刚好在取消信号到达前执行完毕,而响应还堵在路上。面对“动作完成、响应晚到”这种不确定状态,Host 必须显式处理,缺少状态查询能力时只能报告“结果不确定”。

重连之后要重新协商版本和能力,不能沿用旧会话的假设。旧会话里缓存的能力列表、协商出的版本,在断线重连后都可能已经变了,必须重新初始化、重新确认,而不是直接接着上次的状态继续发消息。

最后,定位故障需要足够的日志。日志至少要关联这几项:Host 请求、Client 会话、Server 身份、工具、参数摘要、用户批准、结果和副作用 ID。有了这些关联,出问题时才能判断错误究竟发生在模型、Host、传输还是 Server 哪一环,而不是只知道“某一步失败了”。

把这些串起来:恢复机制的输入是请求 ID、超时、动作类型、幂等键和已知副作用,输出是重试、查询状态、取消或停止的决定。读操作通常可重试,写操作先用幂等键或查询结果;重连后重新协商。取消只表示 Host 不再等待,不能证明远端动作没有完成,缺少状态查询时必须报告不确定。

9何时用 MCP,何时直接 API 或内部函数选型

把每个函数都包装成 MCP Server 就会更标准吗?并不会。MCP 的价值高度依赖场景,选型要按具体需求来,而不是按“是否流行”或“是否更标准”来决定。

MCP 的契约价值在三种情况下最大:需要跨多个 Host 复用、需要动态发现能力、或者面向第三方生态。如果你希望同一组工具能被不同 AI 应用发现并调用,或者让外部开发者接入你的能力生态,那么统一的发现、协商、错误和生命周期消息就是实打实的收益。反过来,单进程内部的稳定函数、极低延迟的热路径,或者已经有成熟服务 SDK 可以直接用,那么直接调用往往更简单——为一个进程内函数多套一层 Server 和连接,只会平白增加延迟和攻击面。

还要注意,MCP 并不替代业务 API。一个 MCP Server 往往只是把已有的业务 API 映射成面向 AI 应用的能力:真正执行动作的仍然是背后的 API,MCP 提供的是统一的发现与调用入口。选型时比较的维度是连接复用、版本治理、认证、可观测性、延迟和攻击面,每一项都要落到具体数字和风险上,而不是以“大家都在用”为由跳过权衡。

一个稳妥的推进方式是从最小面开始:先做只读的最小 Server,只暴露查询能力,验证复用和治理收益之后,再逐步开放写工具。写操作带来副作用和幂等责任,应该等证据充分了再开放,而不是一开始就全量暴露。

所以选型的输入是复用范围、发现需求、延迟、认证、治理成本和攻击面,输出是 MCP、直接 API 或内部函数三者之一的方案:跨 Host 生态复用最适合 MCP,单进程稳定热路径常适合直接调用。采用 MCP 只表示连接契约更统一,绝不表示业务 API、SDK 或安全治理可以省略——协议标准化了“怎么连接”,但“做什么业务、怎么授权、怎么治理”仍然要你自己完成。

10先澄清:协议、SDK、Server 与 Agent 不是同一层概念消歧

当有人说“装了一个 MCP”时,指的可能是完全不同的东西,这就是混淆的起点。MCP 这四个字母之下其实叠着至少四个层面,必须分开。

MCP 本身是一份消息与生命周期规范:它规定 initialize、initialized、tools/list、tools/call 这些消息长什么样、按什么顺序走。SDK 是实现这份规范的代码库:它把规范里的消息封装成可以直接调用的函数和类,方便开发者在具体语言里用。Server 是暴露能力的运行程序:它用 SDK(或手写实现)把一组工具、资源、提示挂出来,等待 Client 连接。Agent 则是决定是否以及怎样使用能力的应用逻辑:它理解用户目标、判断要不要调用某个工具、怎么解读返回结果。

这四个层面的区别不是抽象分类,而是直接影响排错。一个 Server 可以完全不含模型——它只是提供能力,不做任何推理决策。一个 Agent 也可以完全不用 MCP,直接调用内部函数——它不需要外部能力时,根本不会经过协议。如果把这几层混在一起,就会把 SDK 的 bug、协议版本不兼容、工具语义错误、模型决策错误统统称为“MCP 失败”,结果连责任在哪一层都定位不到。SDK 写错了是代码库的问题,版本对不上是协商问题,工具返回了错误数据是语义问题,模型选错了工具是决策问题——它们不是同一个故障,也不能用同一种方式修。

所以最易混淆的一点要钉死:MCP 标准化的是“怎样连接与交换”,它不保证工具真实、不保证安全、不保证获准、也不保证适合当前任务。协议让你能发现和调用一个工具,但那个工具是否真的做了它声称的事、是否被允许调用、是否适合此刻的任务,这些判断都在协议之外,由 SDK 的实现、Server 的授权和 Agent 的决策分别负责。把规范、代码库、运行程序和应用逻辑分开,才能真正说清楚一次调用里每一环在干什么。

11把因果链连起来综合

把前面各节的机制按运行顺序串起来,就得到一条从“问题”一路通往“可验证实践”的因果链。每一步既是上一环的结果,也是下一环的前提。

第一环是 Host 选择并信任一个 Server。信任不是协议给的,而是 Host 基于身份校验和配置检查做出的决定;这一步定下了整条链的起点——用谁、信多少。

第二环是为这个 Server 创建隔离的 Client。一个 Server 一个 Client,会话彼此隔离,Server 之间没有直连,Host 在这里把跨源上下文的汇合点牢牢控制在自己手里。

第三环是协商版本与双向能力。双方通过 initialize 交换协议版本和各自支持的能力,版本不兼容就断开;这里的能力协商只回答“协议上能做什么”,不回答“允许做什么”。

第四环是发现原语并建立本地目录。Client 通过 tools/list 等拿到工具、资源、提示的清单和输入 schema,Host 据此建立自己的目录,供后续的模型或应用选择。

第五环是模型提出或应用选择操作。到这一步,之前建立的能力目录才被真正使用:模型表达工具意图,或应用选择要读取的资源,或用户选择要用的提示——控制主体不同,这一步的语义也就不同。

第六环是 Host 授权后发送最小请求。Host 先做策略和同意检查,再只发送完成请求所需的最少参数;能力协商在这里被授权判断补全,Server 也仍要自己做资源授权,不能信任 Client 传来的身份。

第七环是验证结果并记录副作用。返回的内容按不可信数据处理,Host 记录结果和副作用 ID,为后续的审计与失败恢复留出依据。

第八环是取消、重连或关闭会话。超时则取消并停止等待,断线则重连并重新协商,结束则关闭会话;写操作靠幂等键或状态查询兜底,晚到的不确定结果被显式报告而非默认忽略。

这条链把“Host 持有策略、Client 维护单会话、Server 提供能力”的角色分工,落成了可逐步检验的八步流程。每一环的边界都很清楚:能力、授权与内容可信度是三个不同的判断,任何一环都不能被上一环的结果替代。

资料来源与改编说明
访问日期:2026-07-22