MCP Apps 实战:给 Agent 工具加交互界面,也要守住能力协商、最小权限与文本降级
承接: 协议升级深度实践:MCP无状态化迁移清单(网关、任务与鉴权) · 协议升级后的验收深度实践: MCP迁移实战:用官方 Conformance Suite 给 Client/Server 加回归门禁
调研日期:2026-08-03
本文目标:将 MCP Apps 作为一个可协商、可降级、可审查的交互层接入 MCP Server,而不是让聊天里的 iframe 绕开工具权限、网络限制或人工确认。
MCP 工具返回文本与结构化数据已经能覆盖大量 Agent 场景,但它不擅长审批表单、实时状态、图表和复杂选择。MCP Apps 为这类场景定义了标准路径:Server 声明 ui:// 资源,Host 在沙箱 iframe 中渲染 View,并经由 Host 把 UI 的交互继续纳入 MCP 的通信与审计路径。MCP Apps 的 2026-01-26 规范已标为稳定;而 2026 年 MCP 的扩展化方向又使它成为值得优先评估的能力。官方规范 与 项目文档 都强调:这是一项可选扩展,Host 支持度不同,不能假设“接了 MCP 就一定能渲染 UI”。
因此,最可靠的实现目标不是“让每个工具都有一个漂亮界面”,而是:即使 UI 不存在、加载失败或被 Host 拒绝,工具仍以安全且有意义的文本契约工作;而当 UI 可用时,它也只拥有完成该交互所需的最小能力。
适用前提:你维护一个可在隔离环境启动的 MCP Server,并能同时测试至少一个目标 Host 和不支持 MCP Apps 的纯文本路径。本文不假设任意生产 Host 都支持该扩展;上线前应按目标客户端、SDK 与规范版本逐一核验。
一、先看清三方边界:Server、Host 与 View 各做什么
MCP Apps 的关键不在于“把网页塞进聊天框”,而在于保持下面三条通信边界:
Agent / 模型
│ 选择工具
▼
Host(聊天客户端、能力协商、iframe 沙箱、审计代理)
│ MCP tools/call、resources/read
▼
MCP Server(工具、ui:// 资源、业务授权)
Host ⇄ postMessage / JSON-RPC ⇄ View(iframe 中的交互界面)
|
组件 |
主要责任 |
不应承担的责任 |
|
MCP Server |
声明工具与 UI 资源、执行业务授权、返回文本与结构化数据 |
假定浏览器存在,或把授权判断下放给前端 |
|
Host |
协商扩展能力、获取资源、用沙箱 iframe 渲染并代理通信 |
把任何 UI 按钮默认视为已获用户授权 |
|
View |
展示数据、收集受限输入、通过 Host 请求允许的工具 |
直接连接其他 Server、持有高权限凭据或取代 Server 鉴权 |
规范将 UI 资源与普通资源区分为 ui:// URI,并要求初始 HTML 资源使用 text/html;profile=mcp-app。View 与 Host 的双向通信使用 JSON-RPC 基础协议;这意味着“刷新”“分页”“提交表单”等界面交互不必暴露成模型上下文里的新工具,但也不意味着它们脱离审计与权限边界。
二、先做能力协商与文本降级,再谈 UI 体验
MCP Apps 是渐进增强(progressive enhancement),不是前置依赖。Server 应先判断 Host 是否声明了相应 UI 能力:支持时注册带 UI 元数据的工具;不支持时仍提供同一业务结果的文本版本。无论哪条路径,工具都要返回有意义的 content,不能只把关键数据藏在 iframe 里。
|
Host 状态 |
Server 行为 |
用户可见结果 |
失败信号 |
|
已协商 MCP Apps |
关联 |
可交互的仪表盘/表单,同时保留可读摘要 |
UI 存在但文本结果为空,无法审计或降级 |
|
不支持或未协商 |
不注册 UI 元数据,走普通工具结果 |
可读文本、JSON 或链接说明 |
工具因缺少 iframe 直接失败 |
|
UI 资源读取失败 |
回退到文本结果并记录受控诊断 |
用户仍能完成只读判断或请求人工处理 |
业务结论只能从前端看见 |
|
UI 需发起动作 |
仅暴露经过定义的 app-only 工具,经 Host 转发 |
明确的动作、理由与确认路径 |
View 直接调用未声明或跨 Server 的能力 |
这条降级策略还能避免一个常见兼容性陷阱:官方项目明确提示 Host 支持会变化。把“目标 Host 已测试的 UI 版本”和“纯文本回退仍通过”都放入发布验收,才不会因一个客户端升级而让核心工具失效。
三、用两类工具划出模型和界面的最小契约
规范允许在工具的 _meta.ui 中关联 UI 资源,并通过 visibility 区分模型和 App 能否调用。下面是协议级契约示意,用于说明边界;实际注册 API 应以已固定版本的 @modelcontextprotocol/ext-apps SDK 为准。
{
"name": "show_change_risk",
"description": "返回一次变更风险评估的文本摘要和交互式详情",
"inputSchema": { "type": "object", "properties": { "changeId": { "type": "string" } } },
"_meta": {
"ui": {
"resourceUri": "ui://risk-console/change-risk",
"visibility": ["model", "app"]
}
}
}
上面的业务工具可被模型选择,也能让 UI 获得相同 Server 连接内的结果。对刷新、页码切换或临时表单状态这类不该污染模型上下文的动作,使用 app-only 工具:
{
"name": "refresh_change_risk",
"description": "仅供风险面板刷新其已展示的数据",
"inputSchema": { "type": "object" },
"_meta": {
"ui": {
"resourceUri": "ui://risk-console/change-risk",
"visibility": ["app"]
}
}
}
visibility: ["app"] 不会让模型看见或调用这个工具,且 app-only 工具只能由同一 Server 连接中的 App 使用。它适合低风险、界面专属的读取和刷新;它不是隐私标签,也不是把危险操作藏起来的方式。只要动作会改变外部系统、权限或数据,就仍需要 Server 侧授权、明确的输入校验,以及符合团队政策的用户确认或人工审批。
新实现应使用嵌套的 _meta.ui.resourceUri 结构,不要从已弃用的扁平 ui/resourceUri 别名开始。模板和数据也要拆开:模板是可审查、可缓存的静态资源,工具结果才承载每次调用的动态数据。
四、把 iframe 当作隔离边界,而不是信任边界
MCP Apps 的规范要求 Host 以沙箱 iframe 渲染 HTML,并允许资源通过元数据声明 CSP。最小网络策略应从“没有外连”开始:规范中省略或留空 connectDomains 的含义是没有外部连接;只有确实需要的 API 或 WebSocket 域名才逐项加入。
{
"uri": "ui://risk-console/change-risk",
"mimeType": "text/html;profile=mcp-app",
"_meta": {
"ui": {
"csp": {
"connectDomains": ["https://risk-api.example.internal"],
"resourceDomains": ["https://static.example.internal"]
},
"prefersBorder": true
}
}
}
示例中的域名只是占位形态,生产配置必须替换为团队实际受控的精确来源;不要使用 *、临时 CDN 通配或未经审核的第三方埋点。还应明确以下四条原则:
- 资源先审查,再渲染。 UI 模板应进入代码审查、版本固定和安全扫描,Host 可以在工具运行前预取或审查资源并不代表团队不必审查其供应链。
- 前端不持久化高权限凭据。 View 需要的数据应由 Server 依据当前请求和身份提供,不能把服务 Token、OAuth refresh token 或客户数据打进 HTML。
- 按钮不是审批。 “确认部署”“提交修改”按钮只能发起一个受控工具调用;真正允许或拒绝应发生在 Server/Host 的授权与审批链上。
- 内容与指令分离。 从工具结果呈现的外部文本、图表标签或链接都可能是不可信内容,不能反向改变 UI 能调用的工具白名单。
五、用一个只读原型验证端到端,再增加写操作
官方 ext-apps 仓库提供了 basic-host 和多个示例。要快速检查本地渲染链路,可在隔离环境运行其参考实现:
git clone https://github.com/modelcontextprotocol/ext-apps.git
cd ext-apps
npm install
npm start
然后先接一个只读工具,例如变更风险摘要、构建状态或资产清单。首个版本不需要“提交”按钮;它只需证明资源发现、Host 渲染、文本回退和错误处理都按预期工作。下面这张验收矩阵比“界面是否好看”更重要:
|
场景 |
期望结果 |
失败信号 |
|
支持 UI 的目标 Host |
|
UI 成为唯一数据载体 |
|
不支持 UI 的 Host |
不带 UI 元数据的工具照常返回可读结果 |
因无 iframe 而报错或空结果 |
|
CSP 未列出的外部请求 |
被阻止并产生受控诊断 |
模板可随意连接任意域名 |
|
app-only 刷新工具 |
不进入模型的工具列表,只能在同一 App 会话使用 |
模型能调用隐藏工具,或跨 Server 调用成功 |
|
恶意/异常工具数据 |
文本和 UI 都把它当数据,不扩展权限 |
页面内容可诱导自动执行高风险动作 |
|
UI 资源版本升级 |
通过固定 SDK/Host 组合验证并可回滚 |
只更新前端模板,未重测协议与降级路径 |
确定只读原型稳定后,再把一个可逆的动作做成 app-only 工具,并让 Server 对每次调用验证身份、对象范围、参数和审计字段。涉及提交、删除、转账、修改生产配置等不可逆操作时,UI 只能改善信息呈现,不能替代现有的人审、权限检查与变更记录。
六、把 MCP Apps 纳入同一套协议与供应链门禁
上一节的 UI 规范并不替代核心 MCP 的升级与 conformance。建议将发布检查拆成三层:
|
层次 |
最小检查 |
不能证明什么 |
|
MCP 协议层 |
当前 SDK/传输组合通过项目的协议测试与回归门禁 |
UI 模板的 CSP、交互和业务授权正确 |
|
App 资源层 |
|
业务动作是否按组织流程获批 |
|
业务动作层 |
参数校验、最小权限、审计关联、人工确认与回滚演练 |
其他 Host 是否同样支持该 UI |
每次升级 @modelcontextprotocol/ext-apps、核心 SDK、Host 或 UI 打包链时,都应重新运行这三层检查。不要只因某个演示 Host 成功显示面板,就宣布生产客户端、网关和认证路径已经兼容。
七、六个常见误区
1)把 MCP Apps 当成所有 Host 的默认能力
它是可选扩展,Host 兼容度需要实际验证。文本回退不是“旧客户端兼容补丁”,而是协议设计的一部分。
2)只返回 iframe,不返回文本结果
这样会让无 UI Host、审计系统和辅助工具失去核心信息,也会让故障时无法判断工具是否已成功。
3)将 app-only 误解为保密或高权限通道
它只控制工具对模型/App 的可见性;Server 仍要逐次鉴权、校验参数并记录动作。
4)放宽 CSP 来解决加载失败
宽泛的 CDN 或未固定的远程脚本会把调试便利变成供应链与数据外传风险。应先找出真正需要的精确来源。
5)让 UI 按钮直接代表用户同意
界面点击只是一个输入事件。高影响操作仍需要服务端确认当前身份、目标、范围和审批状态。
6)将 UI 测试与协议升级测试割裂
传输、能力协商和资源发现的变化都可能破坏界面。MCP Apps 应和核心 MCP 回归一起被验证。
结语
MCP Apps 最有价值的地方,是把图表、表单和复杂状态从“每个聊天客户端各做一套”变成可协商的协议扩展;但只有把 UI 当成渐进增强的受限视图,它才不会变成新的权限旁路。
从一个只读面板开始:先让支持 UI 的 Host 能渲染,确认不支持时仍返回完整文本,再测试 CSP、app-only 工具与审计关联。等这条最小闭环被证明稳定后,才逐一增加可逆交互与需要人工确认的业务动作。这样,丰富界面提升的是 Agent 的可用性,而不是它的越权能力。
来源与延伸阅读
- MCP Apps 官方仓库与 SDK:
@modelcontextprotocol/ext-apps、参考 Host、示例、安装与兼容性提示。 - SEP-1865:MCP Apps 交互式 UI 规范:
ui://资源、工具元数据、可见性、iframe 沙箱、CSP 与能力协商。 - MCP Apps Overview:渐进增强、Server/Host/View 三方架构和 app-only 工具的实践说明。
- MCP 2026-07-28 修订说明:协议无状态化与扩展机制的背景;实际部署仍应以当前正式规范和 SDK 版本为准。
- 协议升级深度实践:MCP无状态化迁移清单(网关、任务与鉴权) :先厘清传输、状态、鉴权与网关迁移。
- 协议升级后的验收深度实践: MCP迁移实战:用官方 Conformance Suite 给 Client/Server 加回归门禁:将协议一致性测试固定进 CI,避免只凭一次连接成功判断兼容。
更多推荐



所有评论(0)