现在有两套开放标准,能让智能体直接画出一块界面,而不是用文字去描述它;人们比较它们时几乎总是盯着格式:A2UI 传 JSON,MCP Apps 传 HTML。这恰恰是两者之间最无关紧要的差别。它们真正的分歧在于组件目录归谁所有——因而也就决定了,当用户照着一块由智能体拼出来的界面上的错误数字去操作时,这个缺陷算在谁头上。按这条轴去选,格式问题自己就有了答案。
两份规范,同一个冬天
两者的落地相隔不到十周。MCP Apps 始于 SEP-1865,由 Anthropic 与 OpenAI 的 MCP 核心维护者连同 mcp-ui 维护者于 2025 年 11 月 21 日提出,并在 2026 年 1 月 26 日作为 Model Context Protocol 的第一个官方扩展正式发布。Google 在这个窗口的中段推出了 A2UI——2025 年 12 月下旬——此后推进得很快:v0.8 是早期公开预览,v0.9 于 2026 年 7 月落地,仓库目前以 v0.9.1 作为生产版本,后面还跟着一个 v1.0 候选发布版。两者都是 Apache-2.0 或与之等价的许可,都是开放的,谁也不是那种你可以坐等它自生自灭的草案。
| A2UI | MCP Apps | |
|---|---|---|
| 线上传输的是什么 | 一个扁平的组件列表,组件之间用 ID 相互引用,格式是 JSON,可以流式传输,因而界面能渐进渲染。 | 一份预先声明好的 HTML/JS 包,以 ui:// 资源寻址,由工具的 _meta.ui.resourceUri 指名。 |
| 由谁渲染 | 你的客户端,把抽象组件类型映射到自己的控件上——React、Angular、Lit、Flutter,SwiftUI 与 Compose 在路线图上。 | 宿主,在一个沙箱化的 iframe 里渲染,并通过 postMessage 上的 JSON-RPC 与宿主回话。 |
| 界面是谁写的 | 智能体,在运行时,用你已经交付的组件拼出来。 | 某位开发者,事先写好,装在一个你安装的服务器里。 |
| 已经跑在哪里 | 参考渲染器,加上 Google 自家的产品面——Opal、Gemini Enterprise、Flutter GenUI。 | 首日即有 Claude 网页版与桌面版、Goose 和 VS Code Insiders,ChatGPT 在同一周内跟上。 |
第三行值得读两遍。下游一切要紧的事——测试、无障碍、事故响应、你要向审计人员交代什么——都从它推导而来,而第一行什么也推导不出。
格式之争是无聊的那一场
通行的说法是:JSON 安全,因为它不是代码;HTML 危险,因为它是。这个直觉有三十年历史,放在这里基本上是错的。MCP Apps 把 HTML 跑在一个权限受限的 iframe 里,模板是预先声明的,宿主可以在任何东西渲染之前先检查它,每一次回调宿主都是可记录的 JSON-RPC,而且宿主可以要求由界面发起的工具调用必须经用户明确批准才能执行。这是智能体生态里规格最完备的沙箱之一。
A2UI 的 JSON 不可执行,这消掉了一类问题,也留下了一个更微妙的。智能体交付的不是代码,它组合的是你的代码。一个模型吐出 {"component":"Button","label":"Confirm transfer","action":"tools/call"},它并没有注入任何东西。它请求的是一个你亲手做的按钮,接的是一个你亲手暴露的工具,配着一句它自己写的文案——而那句文案,正是没有任何沙箱会去检查的部分。
所以安全问题不是"这段载荷会不会被执行",而是"用户最终可能看到哪些界面,以及有没有人评审过这个集合"。这是另一个问题,两份规范给出的答案也各不相同。
可穷举性才是决定一切的那条性质
问一个审计人员会问的问题:把这个系统能呈现给用户的界面全部列出来。在 MCP Apps 下,答案是有限的,而且你可以直接交出去——模板是预先声明的,装在一个有版本号的服务器里,集合只在有人部署时才变。在 A2UI 下,答案是你能穷举词汇——Card、Button、TextField,你目录里的那些组件——但穷举不了句子。智能体在运行时组合,而组合空间是组合爆炸式的。
这两个答案都不算错,它们只是对应不同的产品。一块客户每周二都要依赖的仪表盘,应该在"界面"这一粒度上可穷举,因为得有人为它的回归负责。而一个必须按今天这个问题的形状去渲染的客服智能体不可能可穷举,因为它的全部意义就在于没人提前穷举过今天这个问题。真正的失败,是因为演示效果更好,就把运行时组合的那套用在了周二的仪表盘上。
有个很省事的检验方法。如果你能把界面清单写在一块白板上,那你就不需要为它们上生成式界面——把它们做出来、以模板交付,让智能体在其中做选择就好。生成式界面挣回自己成本的地方,恰恰是那份清单一块白板装不下的时候。
评审被搬到了哪里
采用 MCP Apps,是把评审往外搬。用户触碰的界面此刻由一个不是你写的服务器撰写,按别人的发布节奏做版本,以一个打包件的形式交付。你的控制点是真实存在的,但很粗:装哪些服务器、模板自你上次看过之后有没有变、沙箱允许什么。这与任何第三方依赖是同一种姿态,也就该用同一套办法去治理——一份清册、一个钉死的版本,以及一个会注意到 diff 的人。这同时也是在"经得起时间的产品行为"上叙事更好的那一侧,因为写成代码的界面是有测试的。
采用 A2UI,是把评审往内搬,搬进你自己的设计系统——而这个方向恰恰是团队最容易低估的。你的组件即将以没人写过用户故事的顺序被组合起来。一个设计师只在表单里摆放过的 TextField 会单独出现。一个数字会渲染在一张 Card 里,而那张卡片的标签是模型挑的。每个组件都变成了一个面向不可信调用方的公开 API,于是它需要公开 API 所需要的一切:在整个 prop 空间上行为完备、边界处做校验、对不认识的 prop 有明确定义的渲染方式。多数设计系统达不到这个标准,因为在此之前,唯一的调用方是一个照着文档读的开发者。
老实的总结是:MCP Apps 把界面外包出去、把边界留在手里;A2UI 把界面留在手里、把组合外包出去。两者都站得住脚。哪一个都不是"安全的那一个"。
真正会坏掉的东西
| 故障 | 在 A2UI 下 | 在 MCP Apps 下 |
|---|---|---|
| 界面是错的 | 你的组件,你的事故。模型把它们组合起来,而它们是你交付的。 | 服务器作者的缺陷,却是挂着你的名字、经由你的产品到达用户。 |
| 无障碍 | 逐个组件继承自你的目录,组合层面则完全缺席——阅读顺序与焦点流转从未被评审过。 | 第三方包做成什么样就是什么样。通常未经审计,也在你的测试套件之外。 |
| 本地化 | 组件外壳是你的,能翻译;模型写的每一句文案都不能。 | 随包一起交付,作者支持哪些语言就是哪些。 |
| 一次无声的变更 | 换个 prompt 或换个模型,组合出的东西就变了,而你这边没有任何部署。 | 服务器一次更新就改了模板,而你这边没有任何部署。 |
| 用户提交了什么 | 安全性完全取决于按钮背后的那个工具——界面本身什么都不校验。 | 同上,外加宿主若有强制要求,会多一道宿主批准。 |
第四行是要真正记住的一行,因为它在两侧完全相同,而且是全新的。在传统产品里,界面在有人部署时才会变。在这两者里,界面可能仅仅因为模型换了版本、或某个服务器推了更新就变了——用户看到的那一面,如今成了一个你的发布流程管不住的移动靶。这是一项监控要求,不是设计要求。
Google 的答案是"都要",而这并非和稀泥
Google 自家的开发者博客把两者并置,而不是排座次:真正的产品级集成需要经得起时间的行为时用 MCP Apps——仪表盘、编辑器、审批界面、账户工具;该由智能体在运行时从一个安全目录里挑选呈现方式时用 A2UI。值得一提的是,两边阵营都没有在为今天生产环境中最普遍的第三种选择辩护,那就是:智能体写 Markdown,用户再把结构化的答案敲回聊天框。
这种组合之所以自洽,是因为两份规范处在同一个技术栈的不同层:生态里大致的共识是,MCP 承载工具,A2A 承载智能体之间的往来,AG-UI 承载智能体到前端的流,而 A2UI 描述"画什么"。A2UI 是一种内容格式;MCP Apps 是一套投递与沙箱机制,里面焊死了一种内容格式。没有什么能阻止同一个产品用声明式组合去覆盖长尾、用安装好的模板去守住那十块要紧的界面,而大多数认真的产品最后都会落到这里。
这种收敛并不能替你省掉逐个界面做决定。每一块界面依然只有一个"谁为它负责"的答案,而你选的那套标准,就是你给出的答案。
这个季度该做什么
如果你正把一个智能体产品往这两者之一上落:
- 先按"能不能穷举"给你的界面分类,再按技术选型分类。能穷举的那些要的是模板——你自己的,或者一个你已经钉死版本的 MCP App。剩下的才是运行时组合真正的候选。
- 如果你采用 A2UI,先把目录做硬。每个组件在其 prop 空间上行为完备、对未知值与缺失值有明确的渲染、对客户端不认识的组件类型有明确的行为。一片空白区域是 A2UI 生产环境的典型故障,而它就来自跳过了这一步。
- 如果你采用 MCP Apps,把每个服务器当成一个附带界面的依赖来对待。钉死版本、更新时对模板做 diff、事先决定由界面发起的工具调用是否需要确认,并记住:你对工具问过的那些供应链问题,如今同样适用于像素。
- 让纯文本这条路一直是通的。两套标准都能降级为散文,而一个不画界面就答不出问题的智能体,等于白白背上了对渲染器的依赖。
- 给界面本身埋点。每一轮都记录组合了哪些组件、或渲染了哪个模板。当有人报告"界面是错的",这份日志是唯一能告诉你他们当时到底在看什么的东西。
常见问题
A2UI 是不是因为不传代码所以比 MCP Apps 更安全?
不是。MCP Apps 把预先声明的 HTML 跑在权限受限的 iframe 里,回宿主的 JSON-RPC 可审计——这道边界比多数自研前端代码得到的还紧。A2UI 的风险不在注入,而在于智能体把你可信的组件组合成了没人评审过的界面,配着模型自己写的文案。
我必须二选一吗?
不必,Google 自己的建议就是组合使用:产品级的长期界面用安装好的模板,长尾用声明式组合。这个决定是逐界面做的,不是逐公司做的。
A2UI 上线后最先坏的是什么?
组件出现在它从未被设计过的组合里——一个没有表单包着的输入框、一张值缺失的卡片、一个客户端不认识因而被悄悄丢掉的组件类型。修法是在放手让模型去组合之前,先让每个组件在其 prop 空间上行为完备。
AG-UI 在这里是什么位置?
AG-UI 是智能体与你的前端之间的传输层;A2UI 是描述"画什么"的内容格式。两者是组合关系而非竞争关系,CopilotKit 的 AG-UI 已经能承载 A2UI 载荷。
如果我的界面很稳定,生成式界面还值得吗?
通常不值。如果你能把界面列出来,那就把它们做出来,让智能体在其中挑——你保住了测试、保住了无障碍评审,也保住了"说清用户看到了什么"的能力。生成式界面只在清单本身是开放式的时候才划算。
一块由智能体拼出的界面,审计留痕该怎么留?
记录载荷,而不是截图。A2UI 记的是组件列表与数据模型;MCP Apps 记的是模板标识、它的版本,以及喂给它的那次工具结果。两者都很小,而且当时没记下来,事后都补不回来。
延伸阅读
本站相关:
- 生成式界面与智能体渲染的界面——上文全部内容在建造一侧的实操手册。
- 安全地渲染智能体输出——当输出变成一块界面时,依然成立的边界规则。
- 审批与确认的交互设计——一道确认要做到什么程度才算数。
- 智能体互操作——这些规范在协议栈中的位置。
- MCP 工具投毒——为什么装一个服务器是一次供应链决策。
- 为失败而设计——两套标准都需要的那条降级路径。
信息来源:
- Model Context Protocol — MCP Apps: Bringing UI Capabilities to MCP Clients
- Model Context Protocol — MCP Apps: Extending servers with interactive user interfaces (SEP-1865)
- google/A2UI — specification and reference renderers
- Google Developers Blog — A2UI + MCP Apps: combining declarative and custom agentic UIs
- InfoQ — Google Releases A2UI v0.9: Portable, Framework-Agnostic Generative UI