在MCP过去的大部分发展时间里,它的交互模式都很简单:
智能体调用一项工具,工具完成某个操作,服务器返回文本或结构化数据,最后再由模型把结果解释给用户。
对于很多场景来说,这种方式已经足够。
如果我问:“现在有多少个正在运行的部署?”
我并不需要为此打开一个完整的前端应用。
智能体只要调用get_deployments(),得到以下结果:
{"total":12,"healthy":10,"degraded":2}然后回答一句:
当前共有12个部署,其中10个运行正常,2个处于降级状态。
任务完成。
然而,当用户不只是想“阅读结果”,而是希望与结果进行交互时,事情就变得复杂了。
如果我想查看一个仪表盘呢?
如果我需要批准某项操作呢?
如果我需要一张能够筛选的表格、一个配置表单、一幅图表、一套部署控制面板、一段多步骤流程,或者一个必须由人工确认的审批环节呢?
长期以来,MCP并没有提供一种能够跨宿主应用统一解决这些问题的标准方式。
现在,它终于有了。
这套扩展叫作MCP Apps。
一个很香的 AI 平台:GPT-5.6 低倍率且不降智 和 Claude Code 4.8 只要 0.25倍率,包含 image-2生图。重点是 首字请求都在 5s 内。入口:https://ai.aiyuhub.com
过去的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只需要知道工具名称和输入契约:
asyncfunctionrestartServer(serverId){returnapp.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生成用户故事和验收标准。
在普通对话流程中,任务到这里基本就结束了。
用户会看到一段文字,然后回复:
“看起来不错。”
或者:
“把第二条验收标准改一下。”
后端必须根据自然语言猜测,这究竟是在批准、提出修改,还是仅仅发表意见。
这种流程很脆弱。
使用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 / AppsApps不是第四种核心原语。
它是一项扩展。
它继续建立在现有MCP概念之上,只是增加了两层标准化关系:
- 工具与UI资源之间的关联;
- 服务器、宿主与视图之间的通信协议。
如果你已经理解MCP工具和资源,MCP Apps并不是另一套突然拼接上来的协议。
它只是围绕原有能力增加了一层交互界面。
如果聊天应用是你自己开发的
如果你不是把服务器接入现有MCP Apps宿主,而是在构建自己的智能体平台,这一点尤其重要。
你的聊天应用本身就是MCP宿主。
因此,它也必须理解MCP Apps,包括:
- 检测工具中的UI元数据;
- 读取关联资源;
- 在沙箱中渲染应用;
- 把工具输入和输出交给视图;
- 把视图允许发起的工具调用代理回服务器;
- 协商客户端与服务器能力;
- 执行权限限制。
这是一块真实的系统架构,不是勾选一下配置就会自动拥有的功能。
按回车键或点击查看完整尺寸图片
image
整个体系中确实存在三个参与者:
- MCP服务器;
- MCP宿主;
- MCP App视图。
当前官方SDK甚至明确区分了三类开发角色:
- 视图开发者;
- 宿主开发者;
- 服务器作者。
官方SDK目前主要围绕@modelcontextprotocol/ext-appsTypeScript包构建。
不过,这并不意味着所有业务逻辑都必须迁移到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。