在 MCP 过去的大部分发展时间里,它的交互模式都很简单:智能体调用一项工具,工具完成某个操作,服务器返回文本或结构化数据,最后再由模型把结果解释给用户。

对于很多场景来说,这种方式已经足够。如果我问:"现在有多少个正在运行的部署?"我并不需要为此打开一个完整的前端应用。智能体只要调用 get_deployments(),得到以下结果:
{
"total": 12,
"healthy": 10,
"degraded": 2
}
然后回答一句:
当前共有12个部署,其中10个运行正常,2个处于降级状态。
任务完成。
然而,当用户不只是想"阅读结果",而是希望与结果进行交互时,事情就变得复杂了。如果我想查看一个仪表盘呢?如果我需要批准某项操作呢?如果我需要一张能够筛选的表格、一个配置表单、一幅图表、一套部署控制面板、一段多步骤流程,或者一个必须由人工确认的审批环节呢?
长期以来,MCP 并没有提供一种能够跨宿主应用统一解决这些问题的标准方式。现在,它终于有了。这套扩展叫作 MCP Apps。
过去的 MCP 服务器,更像机器接口
最初的 MCP 思维模型,是围绕智能体设计的。服务器公开一组工具,例如:
search_projects()
create_ticket()
restart_service()
get_customer()
模型发现这些工具,选择其中一项并发起调用,服务器返回数据,最终用户看到的则是模型对这些数据的重新表述。
这种架构非常强大。开发者不再需要为每一种 智能体 单独编写集成,而是可以通过统一协议公开业务能力。不过,它始终存在一个重要限制:这些能力主要是为机器设计的,人类用户与真实结果之间仍然隔着一层模型。
当任务只需要一句话回答时,这种距离并没有问题。可一旦交互本身具有明显的视觉属性,差距便会迅速暴露出来。
比如用户要求:
"把所有生产环境服务展示出来。"
返回一大块包含状态、CPU 和内存数据的 JSON,在技术上完全正确。然而,相比 JSON,一个带有进度条、状态标记,以及"日志""重启""扩容"按钮的小型仪表盘,显然更有用。更关键的是,那些按钮应该真的能够工作。MCP Apps 要填补的,正是这个缺口。
MCP Apps 到底是什么
MCP Apps 是 Model Context Protocol 的一项扩展,允许 MCP 服务器在公开工具的同时,交付与之配套的交互式用户界面。
这里所说的界面,不是截图,也不是用 Markdown 勉强模拟出来的按钮,而是真正运行在 MCP 宿主中的 HTML 和 JavaScript 应用。
按照官方文档描述:
- 工具可以声明
ui:// 资源;
- 宿主在沙箱化的 iframe 中渲染这些资源;
- 宿主把工具数据传入界面;
- 界面也能通过宿主反向调用工具。
它可以被简化为:
MCP App = MCP Tool + UI Resource + Host/View协议
工具仍然完成原来的工作:
- 调用 API;
- 查询数据库;
- 执行业务逻辑;
- 返回结构化数据。
区别只是,工具现在还能额外告诉宿主:
"我有一个专门用于展示这份结果的界面。"
这套 UI 本身同样以 MCP 资源的形式提供,例如:
ui://services/dashboard
资源中可以包含一套完整的前端应用:
- HTML;
- CSS;
- JavaScript;
- React 或 Vue;
- 图表;
- 表单;
- 按钮;
- 其他所需组件。
于是,MCP 第一次以跨宿主的标准方式,把两类能力组合在了一起:面向机器的操作接口,以及面向人类的交互界面。
MCP Apps 是从哪里来的
MCP Apps 非常新,这段历史背景很重要。
如果你在 2025 年一直积极开发 MCP 服务器,却从没见过这项功能,并不是因为错过了什么隐藏文档。当时,它还没有成为正式标准。
2025 年 11 月 21 日
MCP 维护者提出了 SEP-1865,也就是 MCP Apps 扩展提案。该方案由 MCP 维护者、MCP-UI 创建者,以及来自 OpenAI 和 Anthropic 的维护人员共同推动。
它并不是凭空出现的。在此之前,MCP-UI 与 OpenAI Apps SDK 已经分别尝试解决相似问题。真正棘手的是互操作性:同一个服务器开发者,可能需要为某个宿主编写一套 UI 集成,又为另一个宿主重新实现一遍。
MCP Apps 希望把这件事标准化:一个服务器同时公开工具、数据和 UI,任何兼容宿主都能渲染。
2026 年 1 月 26 日
两个多月后,MCP 维护者正式宣布 MCP Apps 上线,并将其称为第一个官方 MCP 扩展,同时明确表示已经可以用于生产环境。
此时,最初的提案已经发展为:
- 更成熟的规范;
- 更完整的 SDK;
- 已经落地的宿主实现。
1 月公告中提到,ChatGPT、Claude、Goose 与 Visual Studio Code 已经推出支持。
2026 年 7 月 28 日
MCP 规范候选版本进一步完善了扩展机制。扩展开始拥有:
- 独立标识符;
- 客户端与服务器能力协商;
- 专用代码仓库;
- 独立版本控制;
- 正式的 Extensions Track。
MCP Apps 也被明确列入官方扩展。
这一版本还进一步确认了一项重要安全属性:由 UI 触发的操作,仍然必须经过宿主,并继续使用相同的 JSON-RPC 基础设施。
无论触发路径是:
智能体 → 工具
还是:
人类 → 按钮 → 工具
最终都沿着同一条控制链路执行,UI 代码不能绕过 MCP 宿主直接操作服务器。
2026 年 9 月 11 日
AWS 公布了一个实际案例,展示如何在 Amazon Bedrock AgentCore 上部署 MCP App。
需要注意的是,这不代表 MCP Apps 变成了 AWS 专属功能。它依然是一项与宿主无关的开放标准。AWS 展示的,只是一种生产部署模式:
AI宿主
↓
AgentCore Gateway
↓
AgentCore Runtime
↓
同时公开工具与UI资源的MCP服务器
↓
Lambda与DynamoDB中的业务逻辑
示例在 ChatGPT 中演示了完整体验,同时指出,同一个服务器也能在 Claude 和其他支持 MCP Apps 的宿主中运行。详见 AWS 示例。
哪些客户端真正支持 MCP Apps
这里必须说得准确一点:支持 MCP,不等于支持 MCP Apps。
一个客户端可以支持普通工具和资源,却完全没有实现 Apps 扩展。2026 年 1 月的公告明确提到,ChatGPT、Claude、Goose 和 VS Code 已经提供支持。
当前文档也表示,App 可以内嵌显示在 Claude、ChatGPT 和其他兼容客户端中,同时明确提醒:不同宿主的支持情况并不完全一致。
因此,正确的设计前提应该是:
服务器支持MCP Apps
+
宿主支持MCP Apps
=
交互式UI
不应该假设所有 MCP 客户端都会自动理解 Apps。一个设计良好的服务器,在宿主无法渲染 App 时,也应该能够优雅降级,继续返回普通文本或结构化内容。
真正关键的问题只有一个
第一次研究这套模式时,我最关心的问题并不是 iframe,也不是 SDK。真正重要的是:如果 UI 资源只是一份静态 HTML,那么不断变化的工具数据究竟怎样进入界面?
CPU 占用现在可能是 21%,十秒后可能就变成 87%。我们显然不能在每次工具执行时,都重新生成一整套前端应用。事实上,也不需要这样做。
秘诀在于:UI 和数据是相互分离的。
可以这样理解:
工具 = 数据
资源 = 展示方式
宿主 = 连接两者的胶水
工具返回动态数据。资源返回一套能够渲染这种数据类型的应用。宿主则在运行时,把数据与界面连接起来。
MCP Apps 实际如何运行
假设服务器提供一项工具:
get_servers()
它会返回服务器列表,每项记录包含:
- ID;
- 名称;
- 状态;
- CPU 占用;
- 内存使用量。
同时,这项工具在定义中指向一个 UI 资源:
ui://servers/dashboard
整个生命周期如下。
首先,模型调用 get_servers()。服务器执行必要的业务逻辑,可能查询数据库、AWS、Kubernetes 或内部服务,随后返回结构化数据。
宿主读取工具元数据,发现它指向 ui://servers/dashboard,于是知道这份结果可以用交互式界面展示。
接着,宿主针对该 URI 发起 MCP 资源读取请求:
resources/read
服务器返回真正的前端应用。它可能是:
- 打包后的 React 应用;
- Vue 应用;
- 原生 JavaScript 页面。
一个非常关键的细节是:当前服务器数据不会被直接写死在 HTML 中。
UI 只需要知道数据契约,例如:
servers[].id
servers[].status
servers[].cpu
servers[].memory
随后,宿主在沙箱化 iframe 中渲染这套应用。这道边界非常重要,因为宿主正在执行由 MCP 服务器提供的 UI 代码,绝不能让它无限制访问父页面 DOM、用户会话或身份凭据。
最后,宿主把真实工具结果交给正在运行的视图。按照官方架构,宿主和沙箱视图之间通过 postMessage,以 JSON-RPC 形式进行通信。

前端收到的数据,与任何普通 Web 应用收到的数据没有本质区别。只有一台服务器,它就渲染一张卡片;有五十台服务器,它就渲染五十张卡片。应用本身没有改变,变化的只是数据。
把它与传统 Web 架构比较,就更容易理解。
传统模式:
React
↓
GET /api/servers
↓
返回JSON
↓
渲染界面
MCP Apps 模式:
智能体
↓
tools/call
↓
工具返回JSON
↓
宿主把JSON传给iframe中的React应用
↓
渲染界面
最大的不同是,UI 未必需要亲自发起第一次请求。操作已经由智能体触发,宿主也已经拿到了结果,只需要把数据交给应用。
MCP Apps 不只是更漂亮的工具结果
真正有意思的地方,从这里才开始。
UI 不仅能接收数据,还可以通过宿主向服务器"说话",也就是直接调用其他 MCP 工具。
还是刚才的服务器仪表盘,现在为每台服务器增加三个按钮:
[日志] [重启] [扩容]
用户点击"重启"后,应用希望执行:
restart_server(server_id="srv-1")
不过,iframe 不会直接连接 MCP 服务器。完整路径是:
MCP App
↓
请求调用工具
↓
宿主
↓
tools/call
↓
MCP服务器
↓
restart_server()
按钮背后的 JavaScript 只需要知道工具名称和输入契约:
async function restartServer(serverId) {
return app.callServerTool({
name: "restart_server",
arguments: {
server_id: serverId
}
});
}
现在,智能体和面向人类的应用可以共同使用同一层业务能力。这远比"在聊天机器人里嵌入一张图表"更值得关注。
一项能力,两种接口
没有 MCP Apps 时,系统架构很容易把同一项业务能力公开两次。
前端使用:
POST /api/deployments/restart
智能体使用:
restart_deployment()
同一项重启操作,却拥有两套集成接口。开发者需要分别维护授权、参数验证、监控、错误处理和业务逻辑,很容易出现两边行为逐渐不一致的问题。
有了 MCP Apps 之后,无论是智能体主动调用,还是人类点击按钮,都能执行同一个 restart_deployment 操作。它们共享:
- 身份验证;
- 权限控制;
- 参数验证;
- 可观测性;
- 业务规则;
- 审计记录。
所以,MCP Apps 带来的变化远不只是"MCP 现在支持小组件了"。更深层的影响是:机器和人类终于可以通过不同界面,复用同一套业务能力。
生产案例:在智能体流程中加入人工审批
来看一个更贴近生产环境的例子。用户要求智能体为某项功能创建一条用户故事。智能体先调用:
get_feature_context()
获得背景信息,再由 LLM 生成用户故事和验收标准。如果你正在为智能体平台挑选高性价比的模型 API,RouteFast.ai 提供了低倍率的 Claude、GPT 和 Gemini 接入,可以作为模型调用成本优化的一种选择。
在普通对话流程中,任务到这里基本就结束了。用户会看到一段文字,然后回复:
"看起来不错。"
或者:
"把第二条验收标准改一下。"
后端必须根据自然语言猜测,这究竟是在批准、提出修改,还是仅仅发表意见。这种流程很脆弱。
使用 MCP Apps 后,智能体可以调用:
present_user_story_for_approval(...)
App 会把用户故事展示出来,并提供两个清晰按钮:
[拒绝] [批准]
点击"批准"时执行:
approve_user_story(
story_id="US-483",
version=1
)
点击"拒绝"时执行:
reject_user_story(
story_id="US-483",
version=1,
reason="..."
)
此时,整个流程不再依赖模型猜测,而是变成了一台明确的状态机。

这并不是视觉装饰。界面已经成为智能体工作流中的正式组成部分。而且,这种审批能够被完整审计,远比让用户输入"YES"更加可靠。
这里还有两个值得注意的生产细节。
不要让 App 抓取聊天内容
App 不应该尝试读取上方聊天消息,再从页面中提取用户故事正文。这样会让工作流依赖宿主的具体渲染结构,极其脆弱。
更干净的做法,是把故事作为工具参数明确传入:
present_user_story_for_approval(
story_id,
version,
story
)
视图从一开始就拥有所需状态,聊天界面的展示逻辑则与真实工作流状态保持分离。
提交 ID 和版本,不要提交整个对象
假设用户故事已经更新为版本 2,而对话中仍然保留着版本 1 的旧审批卡片。如果按钮重新提交整段旧文本,系统可能错误批准一个已经过期的状态。
更可靠的方式是只提交:
story_id + version
后端发现版本落后后,可以拒绝审批,并要求用户检查最新草稿。需要始终记住:对话只是 UI,后端才是事实来源。
不是每个按钮都要变成模型工具
MCP Apps 还有一个很实用的设计:工具可见性。
仪表盘可能需要许多只与 UI 有关的细小操作,例如:
- 翻页;
- 刷新表格;
- 修改排序;
- 调整图表时间范围;
- 保存筛选条件。
这些操作没有必要全部进入 LLM 的工具上下文。App 可以拥有自己专用的 UI 操作,而模型继续面对更小、更清晰的能力集合。
随着 MCP 服务器不断扩大,工具上下文体积和工具选择准确率都会成为真实问题。将 UI 微操作从模型工具中分离,可以避免模型被迫理解那些原本就不该由它处理的行为。
而且,这并不只是一项设计惯例。规范提供了明确机制。工具的 _meta.ui.visibility 字段可以设置为:
["model", "app"]
这是默认值,代表模型与 App 都能看到。也可以缩小为:
["app"]
当工具被标记为仅 App 可见时,宿主不能把它放进提供给模型的工具列表。从智能体角度来看,这项工具根本不存在,只能由正在运行的 App 视图调用。
于是,同一个 MCP 服务器可以把工具真正拆分成不同访问层级:
- 只允许智能体调用;
- 只允许 App 调用;
- 智能体与 App 都能调用。
这不仅让工具列表更整洁,也构成了一道真实的访问边界。
通信还可以反向流动。在前面的拒绝案例中,当用户点击"拒绝"并填写原因后,这段理由不必被锁在 App 内部。根据宿主能力和权限,MCP Apps 可以更新模型上下文。下一轮模型会直接理解用户为什么拒绝当前故事,并自动生成更合理的版本 2,无须用户在聊天框中重新解释一遍。
MCP Apps 不是第四种核心原语
这个概念需要澄清,因为"MCP Apps"这个名字很容易让人形成错误理解。
它并没有把原来的:
Tools / Resources / Prompts
变成:
Tools / Resources / Prompts / Apps
Apps 不是第四种核心原语。它是一项扩展。
它继续建立在现有 MCP 概念之上,只是增加了两层标准化关系:
- 工具与 UI 资源之间的关联;
- 服务器、宿主与视图之间的通信协议。
如果你已经理解 MCP 工具和资源,MCP Apps 并不是另一套突然拼接上来的协议。它只是围绕原有能力增加了一层交互界面。
如果聊天应用是你自己开发的
如果你不是把服务器接入现有 MCP Apps 宿主,而是在构建自己的智能体平台,这一点尤其重要。你的聊天应用本身就是 MCP 宿主。
因此,它也必须理解 MCP Apps,包括:
- 检测工具中的 UI 元数据;
- 读取关联资源;
- 在沙箱中渲染应用;
- 把工具输入和输出交给视图;
- 把视图允许发起的工具调用代理回服务器;
- 协商客户端与服务器能力;
- 执行权限限制。
这是一块真实的系统架构,不是勾选一下配置就会自动拥有的功能。
整个体系中确实存在三个参与者:
- MCP 服务器;
- MCP 宿主;
- MCP App 视图。
当前官方 SDK 甚至明确区分了三类开发角色:
官方 SDK 目前主要围绕 @modelcontextprotocol/ext-apps TypeScript 包构建。不过,这并不意味着所有业务逻辑都必须迁移到 Node.js。
传输层仍然围绕普通 MCP 能力展开:
- 工具元数据;
- 资源;
- 结构化内容;
- MCP 请求。
因此,只要 Python 或 FastMCP 服务器能够公开兼容宿主所需的元数据、资源和结构化输出,就完全可以继续使用。
不要把下面两句话混为一谈:
"官方辅助 SDK 使用 TypeScript。"
和:
"整个 MCP Apps 后端必须使用 TypeScript。"
前端视图自然会使用 Web 技术,但业务层与现有服务完全可以留在原来的技术栈中。
安全绝不能最后才考虑
当 MCP 服务器能够交付可执行 UI 时,安全模型就变得至关重要。
现有架构已经提供了基础保护:
- 视图运行在沙箱化 iframe 中;
- UI 通过宿主通信;
- App 不能无限制访问父应用。
然而,从企业架构角度看,仍然应该把每个 App 都视为不可信 UI。尤其当它能够触发以下操作时:
restart_service()
delete_resource()
approve_payment()
deploy_to_production()
对于破坏性操作,当然应该添加明确的确认对话框。但确认框绝不是唯一防线。后端仍然必须实施:
- 身份验证;
- 授权控制;
- 输入验证;
- 策略执行;
- 审计日志;
- 幂等处理;
- 版本检查;
- 速率限制。
UI 不是信任边界。后端才是。
按钮显示为红色、对话框要求用户再次确认,都只能改善体验,不能替代服务器端的安全控制。
哪些场景真正适合 MCP Apps
不应该为每项工具都开发一个 MCP App。如果工具结果只是 42,直接返回 42 即可。如果用户只想知道生产环境当前运行哪个版本,也不需要为了显示版本号加载 React。
MCP Apps 真正值得使用的地方,是交互本身具有结构的场景。
运维仪表盘
服务、部署、基础设施、日志、指标、任务与队列,都很适合使用可交互界面。用户通常需要先检查状态,再执行操作。
审批工作流
例如:
- 批准或拒绝;
- 接受或要求修改;
- 部署或取消;
- 发布或保留草稿。
这可能是 MCP Apps 最有价值的企业场景。
RAG 与企业知识搜索
相比把 20 条搜索结果全部转换成普通文字,App 可以提供:
- 筛选器;
- 复选框;
- 排序;
- "比较已选内容"按钮。
智能体则继续负责结果周围的自然语言推理。
智能体治理
自然语言很适合询问:
"目前哪些智能体能够访问生产环境中的 Salesforce?"
然而,在审核权限和批准变更时,结构化注册表往往更加清晰可靠。自然语言和界面并不冲突,它们可以互相补充。
表单与配置
要求用户用自然语言描述:
"把 CPU 设为 2,内存设为 4GB,区域改成 us-east-1,副本数量设为 3。"
显然不如直接提供表单。MCP Apps 允许智能体在对话进行到恰当时机时,把配置表单直接带进聊天。
不要为每项工具单独做一个 App
另一种应该避免的设计,是把工具与 App 机械地一一对应:
tool_1 → app_1
tool_2 → app_2
tool_3 → app_3
更合理的做法,是围绕业务能力或领域设计应用。
例如,一套"部署管理"App 可以同时使用:
get_deployment
get_logs
restart_deployment
scale_deployment
rollback_deployment
其中某项工具作为入口,负责把 App 展示出来。App 加载完成后,则可以调用整个相关工具家族。这样得到的 UI 更加完整,MCP 架构也比一堆用途单一的小应用清晰得多。
更大的架构变化
MCP Apps 最让我感兴趣的,并不是 iframe 本身。
真正重要的是:过去,我们花了大量时间为智能体设计操作;如今,同一批操作终于能够在上层公开一套标准化的人类交互界面。
业务操作
↓
机器接口:MCP Tool
+
人类接口:MCP App
如果你已经开始用"智能体插件"而不是"孤立 MCP 服务器"的方式思考,这件事会更加有意思。
例如,一套 Salesforce 能力包可以同时提供:
- 使用说明;
- MCP 工具,如
search_accounts、create_opportunity 和 update_lead;
- 权限定义;
- 评估用例;
- MCP Apps,例如账户浏览器、商机表单和销售管道仪表盘。
这套能力不再只是一袋工具,而是同时面向智能体和人类的完整交互层。
值得注意的是,智能体甚至不需要知道这些 UI 存在。它不需要理解 React,也不用了解 iframe,更不必知道按钮如何被渲染。智能体只需要调用:
get_services(...)
或者:
present_user_story_for_approval(...)
工具元数据会告诉宿主,当前存在可用界面。于是:
- UI 问题留在 UI 层;
- 推理问题交给智能体;
- 业务逻辑保留在后端。
这正是生产级系统希望拥有的职责分离。
我会如何把它引入现有系统
假设我已经拥有一套生产级智能体平台,其中包括:
- 自定义聊天界面;
- 多个智能体;
- FastMCP 服务器。
我不会为了采用 MCP Apps 而重新设计整个系统。我会从一项只读能力开始。例如:
get_agents()
它只返回一份较小的智能体列表。随后,为它关联一个尽可能简单的视图:
ui://agents/list
第一版界面只显示:
暂时不添加按钮。第一个目标,只是验证完整链路:
智能体调用工具
↓
宿主发现UI元数据
↓
读取资源
↓
渲染iframe
↓
工具结果成功进入视图
当这条链路稳定运行后,再逐步增加能力:
- 添加一个"刷新"按钮;
- 增加一项真正的操作,例如"禁用智能体";
- 加入授权控制;
- 完善审计日志;
- 最后再开发更复杂的 App。
这种方式可以渐进采用 MCP Apps,而且完全不需要改变现有智能体的推理架构。
最后的思考
长期以来,MCP 回答的是一个重要问题:
AI 智能体如何通过统一协议与外部系统交互?
MCP Apps 又增加了一个新问题:
人类如何在不离开对话的情况下,与同一批业务能力直接交互?
这个变化,远比第一眼看上去更大。
旧模式是:
智能体 → 工具 → JSON → 文本
新模式则是:同一项业务能力分出两种接口。
┌→ MCP Tool → 智能体
业务能力 ────────┤
└→ MCP App → 人类
两条路径最终都进入同一套底层业务逻辑。智能体仍然负责推理,工具仍然执行操作。
然而,当交互本身需要结构时,协议现在可以把真正的界面直接带进对话:
- 仪表盘;
- 表单;
- 审批界面;
- 数据可视化;
- 配置页面;
- 人工参与的工作流。
而且,它们全部建立在智能体已经使用的同一层 MCP 能力之上。
因此,我不认为 MCP Apps 只是"MCP 返回了更漂亮的结果"。它代表了一个更大的变化:MCP 服务器正在从单纯面向机器的集成端点,进化为同时服务智能体与人类的可移植交互层。
别再只想着返回 JSON。当用户需要的不只是答案,而是下一步操作时,真正应该交付的,也许就是 UI。