ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

A2UI Angular 客户端实战:通过 MCP Apps、Iframe URL 与 Srcdoc 三种方式嵌入外部 Web 应用

A2UI Angular 客户端实战:通过 MCP Apps、Iframe URL 与 Srcdoc 三种方式嵌入外部 Web 应用 A2UI Angular 客户端实战通过 MCP Apps、Iframe URL 与 Srcdoc 三种方式嵌入外部 Web 应用【免费下载链接】a2ui项目地址: https://gitcode.com/GitHub_Trending/a2/a2ui本文基于samples/community/client/angular/projects/mcp_calculator示例讲解如何在 A2UI Angular 客户端中加载并展示一个由 Agent 驱动的交互式 Pong 游戏界面。该示例通过同一套 A2UI 协议演示了三种截然不同的 Web 应用嵌入路径——MCP Apps沙箱内嵌、Iframe by URL远程地址直载与 Iframe by Srcdoc内联 HTML 注入读者读完可以掌握完整的运行配置、三种嵌入方式的原理与差异、以及沙箱隔离与消息安全的核心实现要点并能够直接复刻到自己的 A2UI 客户端项目中。示例概览一个客户端、三种召唤方式mcp_calculator是一个 Angular 单页应用它通过 A2A 协议连接后端的 MCP Apps Proxy Agent由 Agent 根据用户指令返回对应的 A2UI 布局 payload客户端据此渲染出 Pong 游戏界面与计分板。示例的独特之处在于它把如何把一个 Web 游戏嵌入到客户端这一问题的三种解法放在同一个界面里对比召唤方式对应 A2UI 组件内容来源关键特征MCP AppsMcpAppAgent 将 HTML 与 JS 逻辑直接打包进组件 payload沙箱 iframe JSON-RPC over window messagingIframe by URLWebAppFrameUrl远程 Pong Web 服务器的 URLhttp://localhost:8081/pong_app_web_frame.html浏览器直接从 HTTP 端点加载游戏帧Iframe by SrcdocWebAppFrameSrcdocAgent 在后端抓取远程 HTML 后内联传输使用 iframe 的srcdoc属性渲染 HTML 字符串这三种方式共享同一个游戏逻辑Pong但内容投递与隔离策略完全不同非常适合作为理解 A2UI 嵌入式内容渲染机制的对照实验。环境准备与依赖运行本示例需要满足以下前置条件Node.js 18 或更高版本以及Yarn包管理器Python 3.9 或更高版本并安装uvPong Web 服务器托管游戏端点运行说明见 samples/community/web/pong/README.mdMCP Apps Proxy Agent 后端运行说明见 samples/community/agent/adk/mcp_app_proxy/README.md。其中 Agent 后端基于 Agent Development KitADK与 A2A 协议构建本身是一个 A2A 服务器需要配置 LLM 的 API Key复制.env.example为.env后填写。完整运行步骤按照以下顺序依次启动即可跑通整个示例第 1 步构建仓库根目录的共享工作区依赖yarn build:all该命令会构建 A2UI 的共享 workspace 依赖如a2ui/angular、a2ui/web_core等供后续的示例项目引用。第 2 步安装samples/community目录下的本地依赖cd samples/community yarn install第 3 步启动 Pong Web 服务器cd samples/community/web/pong uv run .服务器会监听8081端口控制台输出Serving at port 8081。它实际是一个 Python HTTP 静态服务器请求/pong_app_web_frame.html时动态读取共享的pong_base.html与pong_engine.js位于samples/community/agent/adk/mcp_app_proxy/目录注入本地桥接脚本pong_web_frame_bridge.js后组装成完整页面并附带相应的 CORS 头。它还额外提供了/pong_app_web_frame_srcdoc.html端点供WebAppFrameSrcdoc场景由 Agent 在后端远程抓取。第 4 步在另一个终端启动 Agent 后端cd samples/community/agent/adk/mcp_app_proxy uv run .第 5 步启动 Angular 应用cd samples/community/client/angular yarn start mcp_calculator第 6 步打开浏览器访问http://localhost:4200/?disable_security_self_testtrueURL 中的disable_security_self_test查询参数用于关闭沙箱 iframe 的原生安全自测origin toggle 相关检查便于在本地开发环境中直接运行。界面交互三个建议按钮触发三种渲染路径应用加载后主界面基于A2aChatCanvas构建Agent 名称显示为 MCP Calculator见 app.ts会展示三个建议按钮Open Pong as MCP AppOpen Pong from remote web serverOpen Pong with WebApp Srcdoc点击任意按钮客户端即向后端 Agent 发送一条消息通过ChatService.sendMessageAgent 返回对应的 A2UI 布局 payload界面随即更新为游戏区域 计分板 解说组件。计分板是 A2UI 原生组件PongScoreBoard通过playerScore、cpuScore、commentary三个动态属性实时展示比分与解说而游戏区域则由三种不同的嵌入组件分别承载。整个布局由 pong-layout.ts 中的PongLayout组件组合而成。方式一MCP Apps 沙箱内嵌这是三种方式中能力最完整的一种。Agent 将游戏的全部 HTML 与 JavaScript 逻辑直接写入McpApp组件的htmlContent属性客户端在沙箱 iframe 中渲染宿主与内嵌应用之间通过 window messaging 上的 JSON-RPC 协议通信。组件属性契约从 catalog.ts 中McpAppSchema的定义可以看到McpApp支持的属性属性类型说明htmlContent动态字符串游戏 HTML 内容支持url_encoded:前缀的 URL 编码形式allowedTools字符串数组允许内嵌应用调用的工具白名单allowedFunctions字符串数组允许内嵌应用调用的本地函数白名单data动态值数据绑定路径映射实现与宿主 Data Model 的双向同步title动态字符串iframe 的标题渲染链路与桥接机制McpApp 组件的渲染流程可以概括为内容解析读取htmlContent若以url_encoded:开头则先decodeURIComponent解码加载沙箱宿主iframe 首先加载同源的mcp_apps_inner_iframe/sandbox.html不直接加载游戏内容从而获得宿主页面的window.location.origin桥接握手沙箱页面加载完成后通过SANDBOX_PROXY_READY_METHOD消息通知宿主宿主收到后通过AppBridgePostMessageTransport建立 JSON-RPC 连接并把解析出的 HTML 通过sendSandboxResourceReady注入沙箱sandbox属性为allow-scripts双向数据绑定宿主侧通过surface.dataModel.subscribe(dataPath, ...)订阅 Data Model 变化把增量变化以ui/notifications/data-model-update通知推送给应用对对象类型还会逐键 diff避免子路径相互覆盖应用侧通过ui/notifications/data-model-change通知写回宿主写入前会经过validateMessageSecurity安全校验并用isProcessingAppWrite标志抑制回显防止反馈循环函数与工具调用应用发起ui/requests/function-call时先校验参数安全性再检查函数名是否在allowedFunctions白名单内最终通过surface.catalog.invoker在客户端本地执行应用发起工具调用oncalltool时先校验参数与allowedTools白名单再通过surface.dispatchAction以 Action 形式派发给宿主处理动态尺寸同步应用可通过onsizechange回调请求调整尺寸宿主侧实现了一整套节流策略——宽度钳制在 200–3000px、高度钳制在 100–2000px变化超过 5px 阈值才生效且用 100ms 定时器节流避免频繁重排同时用ResizeObserver把宿主容器尺寸反向同步给应用setHostContext。组件销毁时还会依次清理数据订阅、消息监听、ResizeObserver并关闭AppBridge避免内存泄漏与僵尸事件监听。方式二Iframe by URL 远程直载WebAppFrameUrl组件的载荷只有一个 URL指向远程 Pong 服务器http://localhost:8081/pong_app_web_frame.html由浏览器直接加载该页面。它的核心价值在于游戏逻辑完全托管在远端客户端只负责提供一个安全的外壳。从 web-app-frame-url.ts 的实现可以看到几个关键的安全设计协议白名单targetUrl计算时先通过new URL()解析仅允许http:与https:协议从根源上阻止javascript:、data:、file:等协议的注入origin 传递URL 上会附加origin查询参数当前客户端窗口的window.location.origin供远端页面识别宿主来源期望源校验expectedOrigin从 URL 推导出目标页面的 origin沙箱握手时据此校验消息来源宿主外壳复用iframe 首先加载同源的sandbox-url.html沙箱宿主待SandboxResourceReady握手后再把带 origin 参数的目标 URL 通过 postMessage 交给沙箱去加载从而保证远程内容在受控的沙箱中渲染。方式三Iframe by Srcdoc 内联注入WebAppFrameSrcdoc与方式二的差别在于内容投递路径Agent 在后端主动抓取远程 Web 服务器的游戏 HTMLpong_app_web_frame_srcdoc.html把原始 HTML 字符串内联到htmlContent属性中随 A2UI payload 一起传输客户端再把该字符串塞进 iframe 的srcdoc属性渲染。由于 HTML 内容完全来自可能不可信的Agentweb-app-frame-srcdoc.ts 在注入前做了两道强化防护强制注入 Content-Security-PolicyCSP任何作者自带的 CSP meta 标签都会被剥离并注入如下受限默认策略default-src self unsafe-inline unsafe-eval data:; connect-src none; form-action none; base-uri none; object-src none; frame-src none;该策略的效果是connect-src none阻断fetch、XMLHttpRequest、WebSocket、EventSource等一切外发网络连接form-action none封堵表单提交外泄路径base-uri none阻止基址劫持object-src与frame-src禁止插件对象与嵌套子帧。注入链接点击拦截器页面内所有a链接点击都会被拦截除锚点与javascript:外一律preventDefault并把目标 URL 以a2ui_action/open_url消息转发给宿主由宿主决定如何处理而不是让沙箱内页面直接跳转。同时sandbox属性被设定为allow-scripts allow-forms allow-modals——刻意省略allow-same-origin保持源隔离、allow-top-navigation防框架逃逸与allow-popups防一键链接外泄且消息发送目标固定为window.location.origin保证消息只在同源宿主间传递。三种方式的取舍与选型建议综合源码实现三种方式形成了清晰的取舍光谱MCP AppsMcpApp能力最完整——支持双向数据绑定、本地函数调用、工具调用白名单与动态尺寸协商适合需要与宿主深度交互、共享 A2UI Data Model 的嵌入式应用代价是需要在客户端实现完整的AppBridge桥接逻辑且所有交互消息都要经过安全校验与白名单过滤validateMessageSecurity在数据写回、函数参数、工具参数三个入口都会执行见 web-frame-messages.ts。Iframe by URLWebAppFrameUrl实现最轻、隔离性最好内容托管在远端可独立演进适合内容更新频繁、不需要与宿主数据模型深交互的场景代价是依赖远端服务器可用性与 CORS 配置。Iframe by SrcdocWebAppFrameSrcdoc把从 URL 加载变成内容随载荷走一次握手即可注入且通过强制 CSP 与链接拦截提供了最严格的静态内容防护适合内容由 Agent 聚合、但无需与宿主双向通信的场景。扩展实验自定义目录组件与本地函数mcp_calculator还展示了如何为客户端定义一套完整的自定义目录。catalog.ts 中的DEMO_CATALOG注册了六个组件McpApp、PongScoreBoard、PongLayout、WebAppFrameUrl、WebAppFrameSrcdoc、Column其 Catalog ID 与 Agent 侧的mcp_app_catalog.json保持一致https://a2ui.org/samples/community/agent/adk/mcp_app_proxy/catalogs/0.9/mcp_app_catalog.json保证 Agent 返回的 payload 能被客户端正确解析。此外示例还通过createFunctionImplementation注册了showWinnerModal本地函数比赛结束时内嵌应用调用该函数客户端弹出胜者对话框Play Again 按钮通过context.surface.dataModel.set(/pong_state/player_score, 0)等调用直接重置 A2UI Data Model 中的比分——这正是本地函数执行与数据模型回写两个机制的直观演示。安全提醒A2UI 的嵌入式内容机制iframes、web views会引入不可信内容参照 mcp_app_proxy README 的免责声明Agent 返回的任何 UI 定义与数据流都应视为不可信输入恶意 Agent 可能伪造界面诱导用户钓鱼、通过属性值注入恶意脚本XSS或构造过重的布局拖垮客户端DoS。本文示例中的协议白名单、期望源校验、CSP 注入、链接拦截与allowedTools/allowedFunctions白名单机制正是生产环境必备的加固手段在实际项目中还应对 AgentCard、消息与任务状态等全部外部数据做同样严格的清洗与隔离。【免费下载链接】a2ui项目地址: https://gitcode.com/GitHub_Trending/a2/a2ui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表