尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
在 A2UI 中安全集成 MCP App:基于 mcp-apps-in-a2ui-sample 的双 iframe 沙箱实战解析
在 A2UI 中安全集成 MCP App基于 mcp-apps-in-a2ui-sample 的双 iframe 沙箱实战解析【免费下载链接】a2ui项目地址: https://gitcode.com/GitHub_Trending/a2/a2ui本文基于 a2ui 仓库中的mcp-apps-in-a2ui-sample示例完整讲解如何把第三方 MCPModel Context ProtocolApp 以双重 iframe 沙箱隔离架构嵌入 A2UI 界面并实现「iframe 内 UI 触发动作 → A2UI Client 转发 → Agent 通过 A2A 协议处理 → 返回 surfaceUpdate 更新界面」的双向通信闭环。读完本文你将掌握该示例的架构分层、端到端消息流、McpApp组件与AppBridge桥接机制、sandbox.ts沙箱代理的安全校验逻辑以及开发调试和生产化部署时必须注意的 CORS、CSP 与不可信输入防护要点。示例整体构成Agent Lit Client 的最小可运行闭环该示例位于仓库samples/community/agent/adk/mcp-apps-in-a2ui-sample目录下由两个可独立启动的进程组成Agentagent.py一个基于 FastAPI 的 Python 服务承担两层职责——对外通过/a2a端点响应 A2A 协议消息返回包含McpApp组件的 UI manifest同时处理由 Client 转发过来的工具调用tool call。Clientsamples/community/client/lit/mcp-apps-in-a2ui-sample一个基于 Lit 的浏览器应用渲染 A2UI surface 与McpApp组件并充当 A2A 消息的转发编排器。从 pyproject.toml 可以看到 Agent 侧的依赖画像a2a-sdk0.3.0、google-adk1.28.1、google-genai1.27.0以及fastapi、uvicorn、starlette、python-dotenv。其中python-dotenv对应agent.py开头的load_dotenv()说明 Agent 支持通过环境变量注入密钥等配置a2a-sdk则对应端点返回的 JSON-RPC 2.0 格式的 A2A 任务响应。运行环境要求为 Python 3.10配uv包管理器与 Node.js v18。双 iframe 沙箱隔离模型为什么需要两层运行不受信任的第三方组件代码安全的核心思路是隔离 受限权限。该示例采用双重 iframe 隔离模型三层参与者各司其职Host Page宿主页主 A2UI 应用即 Lit Client持有对整体界面的控制权。Sandbox Proxy沙箱代理托管在独立源127.0.0.1上的 iframe用于强制实现源隔离origin isolation。Untrusted App不受信任的 App真正的 MCP App 内容被动态注入到一个权限受限的内层 iframe 中。宿主与 App 之间的通信由modelcontextprotocol/ext-apps包提供基于标准的postMessage通道完成。这样设计的关键价值在于即便内层 App 代码完全失控它也只能在一个权限被裁剪、来源被隔离的笼子里运行无法直接访问宿主页面的 DOM、存储或网络凭证。这一模型在 sandbox.ts 中有完整实现。沙箱代理在加载时会执行一组自检与校验详见后文并创建内层 iframeconst inner document.createElement(iframe); inner.style.cssText width:100%; height:100%; border:none;; inner.setAttribute(sandbox, allow-scripts allow-forms allow-modals); inner.setAttribute(allow, buildPermissionsPolicy()); document.body.appendChild(inner);注意内层 iframe 的 sandbox 属性只包含allow-scripts、allow-forms、allow-modals并且刻意省略了allow-same-origin——这样浏览器会给该 frame 分配一个匿名唯一源序列化为字符串null使 App 无法以宿主源身份执行特权操作。这也是sandbox.ts中多处使用postMessage(..., *)与允许event.origin null的原因详见下文消息转发小节。三步启动把示例跑起来1. 启动 Client 开发服务器进入客户端示例目录并启动 Vite 服务cd samples/community/client/lit/mcp-apps-in-a2ui-sample yarn dev服务将运行在http://localhost:5173。注意客户端的 vite.config.ts 中server.host被设置为0.0.0.0并配置了fs.allow白名单包括node_modules、shared目录与 renderers/lit 源码这是 Vite 开发服务器允许fs前缀访问工作区文件的前提。2. 启动 Agent另开一个终端进入 Agent 示例目录并启动cd samples/community/agent/adk/mcp-apps-in-a2ui-sample uv run agent.pyAgent 将运行在http://localhost:8000。agent.py末尾的uvicorn.run(app, host0.0.0.0, port8000)将服务绑定到 8000 端口并通过/.well-known/agent-card.json端点对外暴露 A2A 端点地址返回{url: http://localhost:8000/a2a, endpoint: http://localhost:8000/a2a}这符合 A2A 协议中 AgentCard 的发现约定。3. 在浏览器中查看打开http://localhost:5173A2UI 界面会自动加载 MCP App。点击 iframe 内的Call Agent Tool按钮会触发一个由 Agent 处理的动作Agent 读取参数示例中为{foo: bar}后返回surfaceUpdate把 App 替换为一条成功消息Agent processed action: ...。端到端通信链路从加载到工具调用回传客户端示例的 README 将整条链路归纳为 8 步结合源码可以还原出完整的消息流初始加载A2UI Client 加载示例后向 Agent 发送{request: Load MCP App}。对应 mcp-app.ts 中connectedCallback里this.#sendAndProcessMessage({request: Load MCP App})的调用。UI 交付Agent 响应的beginRendering/surfaceUpdate中包含自定义组件McpApp其htmlContent属性携带 App 的原始 HTMLagent.py启动时从 mcp_app.html 读入内存。沙箱隔离Client 用严格的 sandbox 属性allow-scripts allow-forms allow-popups allow-modals allow-same-origin渲染 iframe 隔离 App。握手mcp-apps-component.ts与 iframe 内容通过 postMessage 建立桥接iframe 发送ui/initialize确认就绪。动作触发点击 iframe 内按钮向父窗口A2UI Client发送tools/call消息。动作转发A2UI Client 拦截该消息校验工具名在allowedTools白名单内随后派发a2ui.action事件。Agent 执行框架经 A2A 协议把该动作转发给 Agent。UI 更新Agent 处理后返回surfaceUpdate用成功消息替换原 App 界面。在 Agent 侧agent.py 的/a2a端点用两个分支覆盖上述两段链路当解析到req_text Load MCP App时返回包含McpApp组件的 surface其中三个关键字段是resourceUri: custom://mcp-sample-appApp 的资源标识htmlContent: MCP_APP_HTML从mcp_app.html读取的原始 HTML注入沙箱渲染allowedTools: [trigger_agent_action]允许 App 调用的工具白名单与McpApp组件的allowedTools属性一一对应。当user_action.name trigger_agent_action时打印收到的 contextagent.py使用print(Agent handling trigger_agent_action with context:, context)输出调试信息并返回一个名为mcp-response-surface的新 surface其组件为Text文本为Agent processed action: json.dumps(context)从而实现点击按钮 → 回显处理结果的完整闭环。McpApp 组件A2UI 与 MCP App 之间的桥接器核心桥接逻辑位于 mcp-apps-component.ts 的McpApp类中。它通过property声明了与服务端契约一致的属性resourceUristring、htmlContentstring、heightnumber|undefined缺省时按 4:3 宽高比渲染、allowedToolsstring[]。组件在收到htmlContent且 iframe 就绪后调用initializeSandbox()核心步骤为确定沙箱代理 URL优先读取VITE_MCP_SANDBOX_URL环境变量用于生产部署否则回退到本地开发用的http://127.0.0.1:${window.location.port}${SANDBOX_IFRAME_PATH}——利用127.0.0.1与localhost在浏览器中被视为不同源的特点模拟跨源隔离。实例化 AppBridgenew AppBridge(null, {...}, {serverTools: {}, updateModelContext: {text: {}}}, {hostContext: {theme: light, platform: web, displayMode: inline}})。注释明确说明不需要 MCP client因为 A2UI 自身充当编排器。监听尺寸变化bridge.onsizechange允许 App 动态调整 iframe 容器尺寸带 300ms 的 ease-out 过渡动画。拦截工具调用bridge.oncalltool中校验params.name是否命中allowedTools白名单——命中则调用dispatchAgentAction并把工具调用转换为 A2UI 的a2ui.action事件通过dispatchEvent(new v0_8.Events.StateEvent(eventPayload))派发否则console.warn并抛出Error(Tool not allowed)拒绝执行。等待代理就绪监听ui/notifications/sandbox-proxy-ready通知McpUiSandboxProxyReadyNotification确认沙箱代理 iframe 加载完毕。连接桥bridge.connect(new PostMessageTransport(this.iframe.contentWindow!, this.iframe.contentWindow!))——只向沙箱代理窗口发送消息。下发 UI 资源bridge.sendSandboxResourceReady({html: this.htmlContent, sandbox: allow-scripts allow-forms allow-popups allow-modals allow-same-origin})把内层 App 的 HTML 与 sandbox 属性交给代理去注入内层 iframe。在dispatchAgentAction中工具参数会被扁平化为 A2UI Action 的 context 数组字符串映射为literalString、数字为literalNumber、布尔为literalBoolean、对象为JSON.stringify后的literalString——这与a2ui.action事件的标准数据结构保持一致。沙箱代理 sandbox.ts源码级安全机制拆解sandbox.ts 是双 iframe 架构中代理一层的完整实现其安全设计可以从五个层面理解加载时自检文件在顶层窗口window.self window.top直接抛错This file is only to be used in an iframe sandbox.无document.referrer或 referrer 不匹配ALLOWED_REFERRER_PATTERN时同样抛错。默认的 referrer 模式为/^http:\/\/(localhost|127\.0\.0\.1)(:|\/|$)/可通过VITE_ALLOWED_HOST_ORIGIN环境变量覆盖为生产域名正则做了转义处理。安全自测self-test除非 URL 带disable_security_self_testtrue参数否则沙箱会尝试window.top!.alert(...)——如果能在顶层弹出 alert说明沙箱隔离失效直接抛错。这是对iframe 是否真的被隔离的运行时探测。敏感权限默认禁用SENSITIVE_PERMISSIONS列出camera、microphone、geolocation、clipboard-read、clipboard-write五项buildPermissionsPolicy会为未显式授予的敏感特性追加none指令最终拼成内层 iframe 的allow属性。消息来源双重校验来自父窗口event.source window.parent的消息其 origin 必须与从 referrer 推导出的期望宿主源一致normalizeOrigin会把127.0.0.1归一化为localhost再比较来自内层窗口event.source inner.contentWindow的消息其 origin 必须等于沙箱自身源或字符串null因为内层 iframe 未开allow-same-origin浏览器强制其为匿名唯一源。消息转发与通知收到sandbox-resource-ready类通知后把html/htmlContent用srcdoc注入或url用src加载写进内层 iframe并在加载完成后向 App 发送{type: sandbox-init}。其余消息在父子窗口间透明转发。最后沙箱代理会同时向宿主发送两种就绪通知标准的 JSON-RPC 2.0 格式ui/notifications/sandbox-proxy-ready供标准 MCP Apps 组件使用和 A2UI 扁平信封格式a2ui_sandbox_proxy_ready供 A2UI WebAppFrame 组件使用且注释说明两种通知对另一方均为可安全忽略的 no-op——这使得同一套沙箱代理可以兼容两种宿主组件。此外注释特别强调内层 iframe严格省略allow-top-navigation/allow-top-navigation-by-user-activation防止内嵌脚本劫持顶层导航的 frame-busting以及allow-popups/allow-popups-to-escape-sandbox防止一键点击式链接外泄。开发环境注意事项模块解析、CORS 与 CSP原文档的 Development Notes 是本地调试最容易踩坑的三个点这里结合 vite.config.ts 展开模块解析Module Resolution开发环境下iframe 通过 Vite 的/fs/前缀从工作区node_modules动态加载app-with-deps.js打包产物vite.config.ts中的serve-sandbox插件把/sandbox/...路径SANDBOX_BASE_PATH改写为fs绝对路径并把.js回写为.tsapp-bridge.js、app-with-deps.js除外同时把/lit/node_modules/直接映射到../node_modules下的真实文件。mcp_app.html内部则用script typeimportmap把modelcontextprotocol/sdk的裸导入解析到对应 ESM 文件保证沙箱内的 bundle 能正常加载依赖。CORSiframe 必须从127.0.0.1加载以匹配沙箱代理期望的源并避免 CORS 拦截。这正是mcp-apps-component.ts中sandboxOrigin默认值取http://127.0.0.1:${window.location.port}${SANDBOX_IFRAME_PATH}的原因。CSPContent Security Policysandbox.html使用静态 CSP允许unsafe-inline和unsafe-eval以兼容开发工具生产部署必须移除这些宽松设置并按 MCP Apps 规范实现由 App 元数据动态推导的 CSP。此外VITE_MCP_SANDBOX_URL沙箱代理地址与VITE_ALLOWED_HOST_ORIGIN宿主源白名单这两个环境变量即为生产化改造的预留扩展点。安全边界与生产化建议把外部 Agent 当不可信实体原文档的 Disclaimer 部分是本示例最重要的工程结论值得在实现层面展开A2A 协议数据一律视为不可信输入任何在直接控制之外运行的 Agent 都应被当作潜在恶意实体。其 AgentCard、messages、artifacts、任务状态等运营数据若未经清洗直接拼入 LLM 提示词可能引发prompt injection例如恶意 Agent 在name、skills.description字段中构造对抗数据。UI 定义与数据流同样不可信恶意 Agent 可能伪造合法界面诱骗用户phishing、通过属性值注入恶意脚本XSS、或生成过度复杂的布局拖垮客户端性能DoS。内嵌内容需额外防护如果应用支持 iframe / web view 之类的可选内嵌内容必须防止跳转到恶意外部站点。示例本身的双 iframe 沙箱 referrer/origin 校验 敏感权限默认禁用正是这一要求的落地示范。开发者责任不充分校验数据、不严格沙箱化渲染内容会引入严重漏洞。生产实现必须落实输入清洗input sanitization、内容安全策略CSP、对内嵌内容的严格隔离、安全凭证管理secure credential handling。与仓库内其他示例的关系该示例在实现模式上参考了仓库中custom-components-example尤其是 floor plan map 的通信方式iframe 内使用原生 postMessage而非从unpkg.com这类外部 CDN 加载AppBridge脚本从而对网络抖动和沙箱内 CSP 限制更加健壮同时把编排职责交给 A2UI 宿主由它把 MCP 工具调用翻译为 A2UI action。这一宿主即编排器的模式也让 sandbox.ts 这份共享实现能够同时服务McpApp组件与 WebAppFrame 两种宿主形态是整个 MCP-in-A2UI 方案里可复用性最强的部分。【免费下载链接】a2ui项目地址: https://gitcode.com/GitHub_Trending/a2/a2ui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

Paramics交通仿真结果分析:从数据指标到工程决策的完整指南

Paramics交通仿真结果分析:从数据指标到工程决策的完整指南

做交通仿真最容易被忽略的一个环节,其实是最后的结果分析。模型标定得再仔细、路网搭建得再精细,仿真跑完不把数据看透,前面所有功夫都白费。我在用Paramics做交通仿真项目时,对这一点的体会特别深:很多新手盯着3D动画…

📅 2026/9/14 22:23:40
Kuikly跨端实践:把DeepSeek Harness装进口袋

Kuikly跨端实践:把DeepSeek Harness装进口袋

先交代一下背景。DeepSeek Harness 这个名字,最近在群里和社区里频繁出现,很多人搜的是“怎么安装”“怎么下载”“有没有插件”,但深入用一圈之后会发现,它本质上做的是同一件事:把 DeepSeek 这套模型能力的管理、调度…

📅 2026/9/14 22:18:39
500kW光伏逆变器仿真模型设计与优化实践

500kW光伏逆变器仿真模型设计与优化实践

1. 项目背景与核心需求500kW三相光伏并网逆变器作为中大型光伏电站的核心设备,其仿真模型的准确性直接影响系统设计的经济性和可靠性。在实际工程中,我们常遇到三个典型问题:MPPT动态响应与光照突变的匹配度不足、SVPWM调制引发的THD超标、并…

📅 2026/9/14 22:18:39
MORE NEWS

更多资讯

📰

老奶奶C语言入门教程系列——第7课_同时存好几个数

100个老奶奶看了都懂的C语言教程 — 同时存好几个数 ——用多个变量记住多个数据,一起打印出来 位置地图 第一章 让计算机听你的话 └── 第1课:让计算机开口说话 └── 第2课:让计算机认识你的名字 └── 第3课:在屏幕上打印带数字的句子 └── 第4课:往代码里加…

📰

JWT安全攻防:七种致命弱点与加固方案

1. JWT安全攻防全景透视JSON Web Token(JWT)作为现代Web应用的身份验证利器,其安全性直接影响整个系统的防护等级。在渗透测试实践中,我们常发现开发者对JWT的认知往往停留在"配置即安全"的层面,却忽视了协议…

📰

嵌入式实时数据压缩技术:Heatshrink在物联网中的应用

1. 实时数据压缩库的核心价值与应用场景在物联网设备和边缘计算场景中,我们经常遇到这样的困境:传感器每秒钟产生数百条数据记录,但设备的存储空间可能只有几十KB;工业设备需要将运行日志实时上传到云端,但2G网络的带宽…

📰

Java SpringBoot+Vue3构建高校交流生管理平台实践

1. 项目概述:本科生交流培养管理平台的技术架构这个基于Java SpringBootVue3MyBatis的本科生交流培养管理平台,是一个典型的前后端分离架构的教育管理系统。我在实际开发这类系统时发现,高校对交流生管理的需求往往集中在学籍异动、课程对接、…

📰

币圈止盈止损全攻略:六大策略原理与实战细节

做交易这几年,我见过太多人研究怎么买、怎么选币,花大把时间盯K线、找消息,却很少认真想过怎么卖。结果就是:赚了拿不住,亏了死扛到底,账户从大亏变小亏、从小赚变不赚。说实话,止盈止损这件事&…

📰

iloader:前端资源加载统一管理,图片懒加载与并发控制实战

搞前端时间久了,你会发现很多项目到最后根本不是败在业务逻辑上,而是栽在“资源加载”这一点上。尤其是图片多、脚本杂、登录态要校验、加载顺序还敏感的页面,随便一个加载策略没想清楚,首屏白屏、图片闪跳、滚动卡顿全来了。我整…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬