首页 / 文章 / 停止返回 JSON:通过 MCP 应用交付交互式用户界面

停止返回 JSON:通过 MCP 应用交付交互式用户界面

MCP 应用允许服务器在工具旁提供沙箱化的用户界面,这样人们无需离开代理对话即可进行审批、配置和操作。

2858 词

MCP在早期的大部分时间里,其交互流程都相当简单:智能体调用工具,工具执行任务,服务器以文本或结构化数据的形式回复,模型则将结果转化为人类可理解的语言。

仍有许多问题适合用这种模式解决。统计正常的部署情况无需复杂操作:只需调用get_deployments()函数,读取类似{"total": 12, "healthy": 10, "degraded": 2}这样的紧凑对象,即可用一行代码得出答案。

当人们希望与处理结果进行交互时,情况就变了。仪表板、审批流程、可筛选的表格、配置表单、图表、部署面板、多步骤工作流以及需要人工干预的环节,这些都需要比单纯的JSON数据更复杂的处理方式。长期以来,MCP并未为此提供跨主机的标准解决方案,但现在通过MCP Apps实现了这一目标,同时也拓展了MCP服务器所能表示的内容范围。

MCP服务器主要是机器接口

最初的思维模型是以代理为导向的。服务器会提供诸如search_projects()、create_ticket()、restart_service()和get_customer()之类的工具。模型会发现这些工具并调用它们,接收数据,而人类则只能看到模型选择展示的内容。这种方式的优点在于所有操作都通过统一协议完成,无需为每个代理单独设计集成方案。但其局限性在于这些功能原本就是为机器设计的,因此人类始终无法直接获取原始数据。

在交互本身为可视化形式时,这种方式就显得不够用了。虽然“显示所有生产服务”可能会返回包含状态、CPU使用率和内存信息的正确JSON数据,但带有进度条、徽章以及实时日志/重启/扩展控制功能的简洁仪表板要实用得多。MCP应用旨在填补这一差距。

那么MCP Apps究竟是什么?

MCP Apps是对Model Context Protocol的扩展,它让服务器能够在工具之外提供交互式用户界面——不是截图,也不是伪装成UI的Markdown格式,而是直接在MCP主机中渲染的真实HTML/JavaScript应用程序。相关文档描述了工具如何声明ui://格式的资源,主机如何在独立的iframe中展示这些资源,以及同一主机如何既将工具功能注入视图,又仅通过该主机让视图再次调用这些工具。

MCP App = MCP Tool + UI Resource + Host/View protocol

这类工具仍然会调用 API、查询数据库、执行业务逻辑,并返回结构化数据。它还可能提供一个用于展示结果的界面。该界面属于 MCP 资源,例如 ui://services/dashboard,其中可能包含完整的前端元素——HTML、CSS、JavaScript、React、图表、表单和按钮。因此,一次操作就能同时具备面向机器的功能与面向人类的界面,且这些标准在各类主机上都是统一的。

MCP 应用从何而来?

这种扩展机制是较新的。那些在 2025 年之前就部署了 MCP 服务器的开发者并未错过任何隐藏功能,因为当时还不存在官方标准。

2025年11月底,SEP-1865正式提出,这一应用提案是由来自各大模型实验室的MCP-UI贡献者和维护者共同制定的。虽然此前已经存在相关的实验性项目(MCP-UI、Apps SDK),但缺失的是一种让服务器能够提供工具、数据及界面,且任何符合标准的宿主都能渲染这些内容的互操作方式。MCP博客在2025年11月发布的关于应用提案的文章中记录了SEP-1865的公布情况。

到2026年1月底,同一团队将Apps定义为首个正式扩展功能,且已具备投入生产的条件,同时还制定了更为明确的规范、SDK以及相应的宿主环境。ChatGPT、Claude、Goose和Visual Studio Code被列为早期的客户端。2026年1月的MCP博客文章宣布Apps为首个正式扩展功能。

2026年7月的发布候选版提升了扩展功能的整体水平——包括稳定的标识符、能力协商机制、独立的存储仓库、单独的版本控制,以及一个列出各类应用的扩展轨道。该版本还强调,基于按钮的调用仍会通过宿主端的JSON-RPC路径传递,从而使Agent → Tool和Human → Button → Tool都处于同一个控制平面之下。MCP博客上关于2026年7月发布候选版的文章详细介绍了这个扩展轨道。

2026年9月,AWS Bedrock AgentCore的相关文章展示了一种具体的托管模式,且并不要求应用只能运行在AWS平台上:宿主端 → 网关 → 运行时环境 → MCP服务器(包含工具和UI资源)→ Lambda/DynamoDB。演示中使用了ChatGPT,并指出Claude及其他应用宿主也采用相同的工作方式。如需详细操作指南,请参阅AWS机器学习博客上关于结合Bedrock AgentCore实现交互式MCP应用的文章。

究竟有哪些厂商支持MCP应用?

支持 MCP 并不等同于支持 MCP 应用。客户端无需实现 Apps 扩展即可处理普通工具和资源。2026 年 1 月的公告提到了 ChatGPT、Claude、Goose 和 VS Code;当前文档描述了 Claude、ChatGPT 以及其他兼容客户端的内联渲染功能,同时指出主机支持程度各异。设计时应考虑到这一现实:不要假设所有客户端都理解 Apps。正确的关系式是 服务器支持 MCP 应用 + 主机支持 MCP 应用 = 交互式界面,而一个严谨的服务器在主机无法渲染应用时应当回退到文本或结构化内容。

唯一真正重要的问题

如果 UI 资源是静态 HTML,那么如何让工具数据影响到它呢?CPU 使用率现在可能是 21%,十秒后可能升至 87%;如果每次调用工具都重新生成整个前端,那显然是不合理的。解决方案在于分离:UI 和数据是不同的组成部分。将工具视为数据生成器,资源视为展示外壳,主机则起到连接作用。工具返回动态数据,资源则返回能够渲染该数据的应用程序,主机在运行时将二者连接起来。

MCP 应用实际的工作原理

以 get_servers() 为例,它返回包含 id、名称、状态、CPU 使用率及内存使用率的服务器对象,而这些对象的元数据指向 ui://servers/dashboard。其生命周期如下:

模型调用 get_servers()。服务器执行其相关逻辑——包括数据库、云 API、Kubernetes 以及内部服务——并返回结构化数据。主机获取 ui://servers/dashboard 的 UI 元数据后,会发出 resources/read 请求。服务器随后返回前端资源包(React、Vue 或纯 JavaScript)。当前服务器的详细信息并不会被嵌入到该 HTML 中;UI 只知道预定的数据结构(如 servers[].id、servers[].status 等)。

主机在沙箱化的 iframe 中渲染应用程序,这样 MCP 服务器的 UI 代码就无法随意访问主机的 DOM、会话或凭证。之后,主机通常通过 postMessage 在主机与沙箱化视图之间以 JSON-RPC 的方式,将工具处理结果传递到正在运行的界面中。

前端以普通单页应用的方式接收数据并据此进行渲染。一台服务器生成一张卡片,五十台服务器则生成五十张卡片。应用程序本身保持不变,只有数据会发生变化。

与传统的网页应用——React调用GET /api/servers——相比,MCP应用会让代理触发tools/call,工具返回JSON数据,然后主机将该JSON注入到iframe内的React应用中。用户界面并不总是自行获取数据,主机也可以直接推送结果。

但MCP应用并不仅仅是更美观的工具结果

更深层次的特性是,这类应用本身也可以调用工具。按钮、表单和图表并非仅用于装饰,它们可以通过主机调用以代理所使用的相同MCP工具。

MCP App → call tool → Host → tools/call → MCP Server → restart_server()

例如,控制面板中的“重启”按钮可以通过主机调用restart_server,而无需创建额外的通道:

async function restartServer(serverId) {
  return app.callServerTool({
    name: "restart_server",
    arguments: { server_id: serverId }
  });
}

主机依然是权限管理、日志记录及策略控制的掌控者。

一种功能,两种界面

因此,同一后端操作可以呈现两种形式:一种是模型在对话中调用的工具,另一种是人类可直观操作的应用程序。二者并不互相替代。智能体在处理开放式推理任务时依然表现优异;而在需要结构化处理、高信息密度或重复操作的场景中,应用程序则更为适用。

实际应用案例:智能体工作流中的人为审批

想象一下,有工作人员在起草用户故事,该故事必须获得批准才能存档。在没有应用的情况下,模型会将草稿粘贴到聊天中,然后等待人类输入“批准”或“拒绝并说明原因”。而有了应用之后,像present_user_story_for_approval这样的工具就可以返回草稿以及一个包含“接受”“请求修改”“拒绝”选项的界面元素。点击“拒绝”会打开一个原因输入框;提交后则可以调用相关工具来记录决策结果,必要时还会更新模型上下文,这样在下一轮对话中模型就能知道为什么版本1会失败。

这种模式是有人类参与且拥有真实操作界面的,而非那种脆弱的自然语言处理流程。

并非每个按钮都必须是模型能看到的工具

有些工具应仅供智能体使用,有些仅适用于应用,还有些可共享。控制面板内的分页功能应只能从视图端调用,这样就不会让模型被大量分页相关操作所困扰。服务器可以定义不同的可见性层级,从而建立真正的访问边界,而不仅仅是依靠命名规则。

在主机允许的情况下,通信也可以从应用流向模型上下文:用户在界面中输入的拒绝理由可以更新上下文,这样在后续模型处理时就能直接优化草稿,无需用户再次在聊天中输入反馈。

MCP Apps并非新的核心原语

尽管名为“Apps”,但它并非工具/资源/提示词之外的第四类元素。它实际上是一种扩展,用于标准化工具与UI资源之间的关系以及主机/视图协议。既然工具和资源已经为人所熟知,Apps就是围绕它们的交互层,而非额外附加的独立协议。

拥有聊天应用后会带来哪些变化

构建自有智能体平台的团队将成为MCP主机。该主机必须能够检测UI元数据、读取资源、对应用进行沙箱隔离、将工具输入输出传递给视图层、代理允许的工具调用返回服务器、协商功能能力,并管控权限。这实际上构成了一个复杂的架构体系。

系统中涉及三方参与者——MCP服务器、MCP主机以及MCP应用视图层——官方SDK将视图层开发者、主机开发者与服务器开发人员区分开来。在TypeScript中,相关辅助包位于@modelcontextprotocol/ext-apps目录下,这样就不必将业务逻辑强制集成到Node环境中。基于线路层的MCP仍然使用工具元数据、资源、结构化内容以及MCP请求,因此只要Python/FastMCP服务器能提供具备应用功能的主机所期望的内容,它同样可以正常工作。视图层采用Web技术实现,现有的后端系统无需做任何改动即可继续使用。

安全绝不能被忽视

可执行的用户界面会提升风险等级。沙箱化的iframe以及由主机中介的调用虽能起到一定作用,但所有应用都应被视为不可信的用户界面——尤其是当它们能够触发restart_service()、delete_resource()、approve_payment()或deploy_to_production()这些操作时。确认对话框固然有用,但远远不够。身份验证、授权机制、数据校验、策略设置、审计日志、幂等性处理、版本检查以及速率限制等功能仍需在后台实现。用户界面并非信任边界。

MCP应用真正有用的场景

不必将每个工具都封装在应用中。返回42码或版本字符串根本不需要React框架。只有当交互具有明确结构时,应用才能发挥价值:

操作控制面板——涵盖服务、部署、基础设施、日志、指标、任务及队列等功能,便于用户查看并执行操作。

审批工作流——批准/拒绝、接受/请求修改、部署/取消、发布/保留草稿。这通常是与企业需求最匹配的方案。

RAG与企业级知识搜索——通过过滤器、复选框以及“比较所选项”功能,其效果远优于二十条纯文本搜索结果,同时智能体仍可负责逻辑推理。

智能体管理——可通过自然语言确定谁有权限访问生产环境中的Salesforce;使用注册表视图则更便于审核和批准权限变更。

表单与配置——直接说出“CPU 2核,内存4GB,区域us-east-1,副本3个”这样的描述,远不如让智能体在需要时调出表单来得高效。

无需为每个工具都开发独立应用

避免 tool_1 → app_1 这种模式的大量出现。建议使用域级应用。“部署管理”应用可以将获取数据、记录日志、重启、扩展规模以及回滚操作整合在一起。通过一个入口工具即可调用该应用,之后界面可直接执行相关操作。这样既能保持用户界面的一致性,也能让MCP接口更加简洁。

更大的架构变革

真正值得关注的变化并非iframe本身。原本为智能体设计的操作现在可以提供标准化的人机交互界面:

OPERATION → Machine Interface (MCP Tool) + Human Interface (MCP App)

如果各项功能被打包为智能体插件,那么Salesforce的包就可以同时包含指令、工具(search_accounts、create_opportunity、update_lead)、权限、评估功能以及各类应用(账户浏览器、商机表单、流程仪表板)。这样一来,这些功能就能为智能体和人类用户提供完整的交互界面。

代理无需了解 React 或 iframe 的存在。它只需调用 get_services(...) 或 present_user_story_for_approval(...);元数据会告知主机存在相应接口。用户界面位于 UI 层,负责与代理进行交互,而业务逻辑则存在于后端。

我如何将此功能引入现有系统

在已经具备自定义聊天功能、代理以及 FastMCP 服务器的平台上,应避免重新设计。可以先从仅支持读取的功能开始,例如 get_agents(),再搭配最简单的视图(ui://agents/list),仅显示名称、状态和工具数量,不设置任何按钮。通过这样的循环验证功能:代理调用工具,主机检测到 UI 后读取资源并渲染 iframe,最终将工具的返回结果展示在界面上。

接着添加“刷新”功能,再加入诸如“禁用代理”之类的实际操作功能,随后是授权与审计日志功能,最后才是更丰富的应用功能。如此一来,无需重写代理逻辑,采用率也能逐步提升。

评估是否采用的团队还应为主机能力协商预留预算。如果主机能够忽略未知字段,且工具的结构化内容依然完整,那么具备应用感知能力、始终能附加UI元数据的服务器在较旧的客户端上仍能正常运行。相反,那些声称支持应用功能的主机必须在为最终用户启用相关特性之前实现沙箱机制、资源读取功能以及工具调用代理功能;那些实施不完整的主机会产生有缺陷的iframe,其破坏信任的速度甚至超过纯JSON格式。

可观测性也应纳入同一发布计划中。需记录哪些工具承载了 UI 资源、哪些主机负责渲染这些资源、哪些由按钮触发的工具调用成功了,以及哪些不得不转而使用文本显示。这些指标能告诉你应用是否真的承载了交互式内容,还是用户仍然更喜欢直接在聊天框中输入文字。如果没有这些监控数据,就很容易发布出没人会使用的控制面板。

最后,要为架构规范保持版本控制。应用与工具必须就字段名称和类型达成一致。如果在不更新 UI 规范的情况下将 servers[].cpu 拆分为嵌套对象,那么虽然代理程序仍能接收到正确的 JSON 数据,但对应的组件将会显示为空。应将应用期望接收的数据视为由与工具开发团队相同的团队所拥有的公共 API。

在评估应用上线后的成功程度时,需将“应用已渲染”与“应用已被使用”区分开来。那些仅显示一次内容且从未被点击的iframe只是无关紧要的存在;而那些能触发重启、审批或配置变更的应用才真正具有产品价值。应将产品分析数据与MCP审计日志结合使用,以便明确哪些操作是由应用发起的、哪些是由聊天界面发起的,以及这些操作是否缩短了客服人员处理工单的时间。

最后思考

MCP解决了通过单一协议实现客服人员与外部系统交互的问题;而MCP应用则着眼于让人类无需离开当前对话即可使用相同的功能。

昨天的发展路径是智能体、工具、JSON,然后是散文式文本。如今,单一功能可以实现分支:在对话过程中模型会持续调用工具,而人们则操作可视化应用,这两种分支最终都会指向相同的域服务。推理功能由智能体承担,执行任务则由工具完成;当工作需要结构化处理时,该协议可以在聊天界面中呈现控制面板、表单、审批流程、图表、配置面板以及人工审核环节——同时无需放弃智能体目前所依赖的MCP层。

正因如此,MCP应用的意义远不止于更美观的响应结果:服务器正在从面向机器的端点演变为同时服务于智能体和人类的通用交互层。

在服务器的README文件中明确说明文档回退方案:哪些工具负责声明UI资源,哪些主机能够正确渲染这些资源,以及当应用不可用时文本/结构化回退的内容是什么。这样的文档可以避免出现“MCP出故障”之类的支持请求,因为实际问题可能是某个主机未启用该扩展。

进一步阅读

关于Apps扩展的官方文档发布在apps.extensions.modelcontextprotocol.io上(包含概述和API部分)。与时间线相关的内容则发布在blog.modelcontextprotocol.io上,涉及2025年的提案、2026年的正式发布以及候选版本的扩展跟踪情况。AWS的机器学习博客后来还展示了仍保持与主机无关性的Bedrock AgentCore托管模式。

如果您目前维护多个MCP服务器,请勿轻易为每个团队创建私有的小型框架。应优先使用共享的宿主辅助功能来处理iframe的生命周期,为应用所使用的工具负载提供共享的TypeScript类型,并制定简短的设计审查清单:该工具是否需要界面?是否有现有的应用可以承担其功能?当宿主无法渲染应用时该如何处理?按钮触发调用时的授权问题由谁负责?这四个问题就能避免大多数不必要的应用泛滥情况。

培训同样重要。当某些操作被移至仅应用内可见的范围,导致智能体可使用的工具突然减少时,它们的行为也会发生变化。在划分访问层级时,需更新系统提示和评估套件,并保留一个标准测试流程:打开应用、点击安全操作,然后确认后端审计记录已生成。如果没有这样的测试,功能缺陷就会隐藏在iframe中,直到有客户反馈按钮无法使用。

有了这些措施,MCP应用就不再显得新奇,而会逐渐成为智能体平台中的普通功能模块。通过明确的备用方案和可量化的使用数据,就能形成完整的采用闭环。