☰
别再返回JSON,MCP开始交付UI
2026/10/1 10:12:24 网站建设 项目流程

在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 / Apps

Apps不是第四种核心原语。

它是一项扩展。

它继续建立在现有MCP概念之上,只是增加了两层标准化关系:

  • 工具与UI资源之间的关联;
  • 服务器、宿主与视图之间的通信协议。

如果你已经理解MCP工具和资源,MCP Apps并不是另一套突然拼接上来的协议。

它只是围绕原有能力增加了一层交互界面。

如果聊天应用是你自己开发的

如果你不是把服务器接入现有MCP Apps宿主,而是在构建自己的智能体平台,这一点尤其重要。

你的聊天应用本身就是MCP宿主。

因此,它也必须理解MCP Apps,包括:

  • 检测工具中的UI元数据;
  • 读取关联资源;
  • 在沙箱中渲染应用;
  • 把工具输入和输出交给视图;
  • 把视图允许发起的工具调用代理回服务器;
  • 协商客户端与服务器能力;
  • 执行权限限制。

这是一块真实的系统架构,不是勾选一下配置就会自动拥有的功能。

按回车键或点击查看完整尺寸图片

image

整个体系中确实存在三个参与者:

  1. MCP服务器;
  2. MCP宿主;
  3. 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 ↓ 工具结果成功进入视图

当这条链路稳定运行后,再逐步增加能力:

  1. 添加一个“刷新”按钮;
  2. 增加一项真正的操作,例如“禁用智能体”;
  3. 加入授权控制;
  4. 完善审计日志;
  5. 最后再开发更复杂的App。

这种方式可以渐进采用MCP Apps,而且完全不需要改变现有智能体的推理架构。

最后的思考

长期以来,MCP回答的是一个重要问题:

AI智能体如何通过统一协议与外部系统交互?

MCP Apps又增加了一个新问题:

人类如何在不离开对话的情况下,与同一批业务能力直接交互?

这个变化,远比第一眼看上去更大。

旧模式是:

智能体 → 工具 → JSON → 文本

新模式则是:同一项业务能力分出两种接口。

┌→ MCP Tool → 智能体 业务能力 ────────┤ └→ MCP App → 人类

两条路径最终都进入同一套底层业务逻辑。

智能体仍然负责推理,工具仍然执行操作。

然而,当交互本身需要结构时,协议现在可以把真正的界面直接带进对话:

  • 仪表盘;
  • 表单;
  • 审批界面;
  • 数据可视化;
  • 配置页面;
  • 人工参与的工作流。

而且,它们全部建立在智能体已经使用的同一层MCP能力之上。

因此,我不认为MCP Apps只是“MCP返回了更漂亮的结果”。

它代表了一个更大的变化:

MCP服务器正在从单纯面向机器的集成端点,进化为同时服务智能体与人类的可移植交互层。

别再只想着返回JSON。

当用户需要的不只是答案,而是下一步操作时,真正应该交付的,也许就是UI。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询