在 A2UI 客户端中嵌入 Web Frame 游戏:Pong Web Server 的搭建与桥接机制全解析
在 A2UI 客户端中嵌入 Web Frame 游戏Pong Web Server 的搭建与桥接机制全解析【免费下载链接】a2ui项目地址: https://gitcode.com/GitHub_Trending/a2/a2ui本文围绕 A2UI 仓库中的 Pong Web Server 示例samples/community/web/pong/README.md展开深入讲解如何用一个轻量 Python HTTP 服务器把基于 Canvas 的 Pong 游戏以标准 Web 应用形式托管并通过pong_web_frame_bridge.js桥接脚本安全地嵌入 A2UI 客户端的 Web Frame 组件。读完本文你将掌握该服务器的完整启动流程、动态页面组装原理、CORS 配置细节以及 Web Frame 与宿主之间基于MessagePort的通信协议与安全要点。这个包是做什么用的samples/community/web/pong/目录下存放的是一个基于标准库http.server实现的 Python HTTP 服务器pong_server.py它的作用有两层作为静态文件服务器把本地目录下的资源直接提供给浏览器作为动态装配器在请求特定路径时把共享的 Pong 页面模板、游戏引擎脚本和本地桥接脚本拼接成一份完整的 Web Frame HTML 页面。该服务器的直接目的是托管一个准备以 iframe即 A2UI 的 Web Frame 组件形式嵌入 A2UI 客户端的 Pong 游戏。这里需要特别区分两种 Pong 形态它们是理解本示例的关键形态通信方式说明MCP App 版 Pong通过 A2UI Agent 协议 / MCPwindow.postMessage JSON-RPC由代理端经 MCP Server 下发属于 samples/community/agent/adk/mcp_app_proxy 示例体系Web Frame 版 Pong本文标准 HTTP Web 应用 桥接脚本回连 Web Frame 环境由本文的 Pong Web Server 提供页面通过pong_web_frame_bridge.js与宿主环境建立连接换句话说本服务器不直接参与 A2UI 的 Agent 协议解析而是把一个普通的网页游戏以WebAppFrameUrl/WebAppFrameSrcdoc两种方式暴露给宿主客户端让 A2UI 客户端只需一个 URL或一段 srcdoc 内容即可完成内嵌。目录结构与关键文件samples/community/web/pong/ ├── README.md # 使用说明本文主题文档 ├── __main__.py # 支持 uv run . 的入口 ├── pong_server.py # HTTP 服务器核心实现 ├── pong_web_frame_bridge.js# Web Frame 桥接层本地注入脚本 ├── pyproject.toml # 项目元数据与命令入口声明 └── uv.lock # uv 依赖锁文件各文件职责pong_server.py核心服务器继承http.server.SimpleHTTPRequestHandler拦截两个专用路径并动态组装页面其余路径走默认静态文件逻辑。pong_web_frame_bridge.jsWeb Frame 通信层负责与宿主A2UI 客户端握手、收发动作、同步数据模型与函数调用结果。main.py调用pong_server.main()使uv run .可以直接启动。pyproject.toml声明项目名为a2ui-pong-server、要求 Python3.10并提供pong-server pong_server:main控制台脚本入口因此安装后也可直接执行pong-server命令。服务器动态组装所需的共享素材并不在本目录而是位于 samples/community/agent/adk/mcp_app_proxy即原文档所述samples/agent/adk/mcp_app_proxy/目录在实际仓库中的路径包含pong_base.htmlNeon Pong 页面模板内含canvas idpong、开始/暂停按钮、样式表以及两处占位注释// {{BRIDGE_SCRIPT}}与// {{ENGINE_SCRIPT}}pong_engine.js游戏引擎与渲染逻辑管理球、球拍、碰撞物理与 Canvas 渲染循环并对外暴露startGame、togglePause、isPaused、resize、hideOverlay、resetBall等供桥接层调用的全局能力。从源码结构看这种模板 引擎共享、桥接层按宿主形态替换的设计让同一款 Pong 游戏既能以 MCP App 形态运行也能以 Web Frame 形态运行桥接层是唯一的差异点。快速启动三行命令跑起 Web Frame 版 Pong启动服务器只需uv无需手动安装依赖。在原目录下执行uv run .也可以从仓库根目录直接执行uv run samples/community/web/pong启动成功后终端会输出Serving at port 8081此时服务器在本机8081端口持续运行直到在终端按CtrlC停止。启动后A2UI 客户端可按如下地址接入http://localhost:8081/pong_app_web_frame.html—— 供WebAppFrameUrl使用URL 模式页面标题徽标显示 Embedded Web App (URL)http://localhost:8081/pong_app_web_frame_srcdoc.html—— 供WebAppFrameSrcdoc使用该页面内容会由 Agent 远程抓取后作为 srcdoc 注入 iframe显示 Embedded Web App (Srcdoc)。值得注意的是pyproject.toml 还声明了pong-server控制台脚本因此若将该包安装进环境也可以直接以pong-server命令启动服务器效果等价。启动入口的源码细节main.py 的逻辑极简导入pong_server.main并sys.exit(main())。而 pong_server.py 的main()设置了socketserver.TCPServer.allow_reuse_address True允许端口快速复用便于开发调试时反复重启随后绑定(, PORT)监听所有网卡接口、打印Serving at port 8081并进入serve_forever()事件循环。默认端口常量定义在文件顶部PORT 8081。服务器核心逻辑请求如何被动态组装pong_server.py的关键在于对SimpleHTTPRequestHandler.do_GET的重写pong_server.py其处理流程如下路径识别用urllib.parse.urlparse解析请求路径若命中PONG_APP_PATHS即/pong_app_web_frame.html与/pong_app_web_frame_srcdoc.html两个常量进入动态组装分支。响应头返回200声明Content-type: text/html并显式附加 CORS 头Access-Control-Allow-Origin: http://localhost:4200localhost:4200是 Angular 开发服务器的默认端口即示例客户端常跑的地址。读取三份素材从samples/community/agent/adk/mcp_app_proxy/读取共享的pong_base.html从本目录读取本地桥接脚本pong_web_frame_bridge.js从共享目录读取游戏引擎pong_engine.js。占位符替换把pong_base.html中的// {{BRIDGE_SCRIPT}}替换为桥接脚本内容、// {{ENGINE_SCRIPT}}替换为引擎脚本内容从而完成模板 引擎 本地桥接的页面装配。模式区分若请求的是 srcdoc 路径把页面徽标文本 Embedded MCP App 替换为 Embedded Web App (Srcdoc)URL 模式则替换为 Embedded Web App (URL)方便肉眼区分当前页面形态。写出响应将最终 HTML 以 UTF-8 编码写入响应流。对于其他路径则回退到super().do_GET()走标准静态文件服务。此外end_headers()也被重写pong_server.py对所有非专用路径的响应也统一追加Access-Control-Allow-Origin: http://localhost:4200确保被 iframe 远程引用时浏览器跨域访问不受阻。从实现看这种占位符注入的方式让页面模板保持单一来源引擎逻辑与样式完全共享服务器只负责按宿主形态注入不同的通信桥改动成本被控制在最小范围。桥接层深度剖析Web Frame 如何与宿主安全通信pong_web_frame_bridge.js是整个 Web Frame 方案的灵魂。它在pong_base.html的head中注入替换{{BRIDGE_SCRIPT}}占位符在引擎脚本之前执行因此可以先行注册全局通信能力。安全模型origin 校验与 targetOrigin脚本开头的注释即点明了安全原则为防止跨站消息被截获调用window.parent.postMessage()时必须显式指定targetOrigin。其取值逻辑pong_web_frame_bridge.js如下优先读取 URL 查询参数?origin由 A2UI 宿主在加载 iframe 时提供作为父页面源若没有origin参数且当前处于 srcdoc/sandbox 模式window.location.origin null、协议为about:或data:则回退为*并打印提示日志若在 URL 模式下缺失origin参数则输出错误日志并返回null拒绝向*广播 postMessage——这是刻意为之的 fail-closed 行为。同时初始握手的入站消息也会校验event.origin是否等于PARENT_ORIGIN*除外不匹配的消息直接忽略pong_web_frame_bridge.js防止恶意父页面注入伪造消息。握手协议从 window 消息到 MessagePort 通道桥接层定义了一组以a2ui_前缀命名的消息类型常量a2ui_action/a2ui_data_model_change/a2ui_function_call/a2ui_function_result出站消息方向为 Web Frame → 宿主a2ui_app_frame_init/a2ui_host_context_update/a2ui_data_model_update入站消息方向为宿主 → Web Framea2ui_app_frame_ready页面加载完成后立即向父窗口发送的就绪信号。完整流程为脚本加载即执行window.parent.postMessage({type: MSG_TYPE_APP_FRAME_READY}, PARENT_ORIGIN)向宿主宣告Web Frame 已就绪宿主收到就绪信号后通过window.postMessage回传a2ui_app_frame_init并携带一个MessagePort位于event.ports[0]桥接层调用configureAppPort(port)pong_web_frame_bridge.js关闭旧端口、保存新端口、移除 window 层监听此后所有通信都走MessagePort——注释明确说明建立 MessagePort 后端口上的后续消息无需再做 origin 校验因为通道本身已经建立在一对一可信连接之上若宿主未提供 MessagePort则抛出A2UI Protocol Violation错误并说明ambient postMessage 将被宿主忽略。这种先经 window 握手、再切换至 MessagePort的两阶段模式兼顾了初始鉴权的必要性与持续通信的性能/安全收益。数据模型同步与游戏状态联动桥接层维护了localPlayerScore/localCpuScore两份本地分数并通过两类回调与宿主双向同步applyInitialPayload(data)在a2ui_app_frame_init时解析初始负载包括config.matchingScore获胜分数覆盖引擎默认的DEFAULT_WINNING_SCORE 3、initialData.state初始双方比分以及hostContext.containerDimensions宿主容器尺寸handleDataModelUpdate(data)接收a2ui_data_model_update按key state与subpath/player_score、/cpu_score或整包 value 更新本地分数。其中有个精巧的联动逻辑pong_web_frame_bridge.js当检测到双方比分同时归零时自动解除暂停、隐藏遮罩、复位小球并派发a2ui_action动作commentate_pong携带game_event: Match started! ...、silent: true——即宿主代理侧通过该动作感知比赛重启事件。出站消息封装桥接层向宿主暴露三类出站原语dispatchAction(action, data)派发a2ui_action对应 MCP 语义中的tools/call见sendRequest对method tools/call的分发供游戏引擎触发宿主侧工具如评论解说dispatchDataModelChange(key, subpath, value)派发a2ui_data_model_change对应ui/notifications/data-model-changedispatchFunctionCall(call, args)派发a2ui_function_call返回一个 Promise通过监听端口上的a2ui_function_result按callId匹配、status success判定异步等待宿主函数执行结果对应ui/requests/function-call。functionCallId自增计数保证多个并发函数调用互不串扰dispatchFunctionCall内还刻意捕获了端口局部引用避免挂起期间全局appPort被重新赋值导致监听器绑错实例。容器尺寸适配applyContainerDimensions会把宿主下发的宽高直接写到document.documentElement与document.body的样式上并调用引擎的resize()引擎据此按600x400参考尺寸重新计算缩放系数与球拍、小球尺寸。这一机制配合 pong_engine.js 的响应式渲染让嵌入页面可以跟随宿主布局动态伸缩。与 MCP App 版桥接层的对比把 pong_web_frame_bridge.js 与 MCP 版的 pong_mcp_bridge.js 对照阅读可以清晰看出两套体系在同一款游戏上的差异维度MCP App 版pong_mcp_bridge.jsWeb Frame 版pong_web_frame_bridge.js协议形态完整 JSON-RPC 2.0jsonrpc: 2.0、id、method、params精简的自定义a2ui_*消息类型初始化发起ui/initialize并携带appInfo、appCapabilities、protocolVersion再发ui/notifications/initialized发a2ui_app_frame_ready等待宿主a2ui_app_frame_init下发 MessagePort通道全程window.postMessage发送目标为*握手后切换到MessagePort通道宿主来源代理侧 MCP ServerA2UI 客户端宿主页面从源码结构可以推断Web Frame 版的桥接层剥离了完整 MCP 能力协商只保留动作、数据模型、函数调用三类最小交互面这正是嵌入式 Web 应用场景下的轻量取舍。安全注意事项与调试建议显式 targetOrigin 是硬性要求URL 模式下缺失?origin参数时桥接层会拒绝通信返回null。A2UI 客户端在加载 iframe 时必须正确追加该参数。CORS 白名单是示例配置服务器将Access-Control-Allow-Origin硬编码为http://localhost:4200Angular 开发服务器默认端口。若你的 A2UI 客户端运行在其他地址如 React 的 5173、自建域名需要按实际场景调整 pong_server.py 中的该响应头。srcdoc 模式的安全回退srcdoc/sandbox 环境下无法获知父页面源桥接层只能退化为*此时应依赖宿主侧的 sandbox 属性与严格的内容来源控制来兜底。代理端安全立场正如 samples/community/agent/adk/mcp_app_proxy/README.md 强调的任何来自外部 Agent 的 UI 定义与数据流都应视为不可信输入嵌入的 iframe/web view 内容必须严格沙箱化防止恶意外部站点如 XSS、钓鱼、DoS 型布局攻击借道注入。验证启动效果浏览器直接访问http://localhost:8081/pong_app_web_frame.html可看到 Neon Pong 页面若控制台出现Missing required ?origin parameter in URL mode类日志说明宿主未正确携带 origin 参数页面将拒绝向父窗口广播消息。小结Pong Web Server 示例演示了一条非常实用的嵌入路径用标准 HTTP 服务器托管普通网页游戏通过一个小巧的桥接层把它接入 A2UI 的 Web Frame 组件。它完整覆盖了静态服务、模板动态装配、CORS 配置、origin 安全校验、MessagePort 握手以及数据模型双向同步等关键环节是理解 A2UI Web Frame 能力与嵌入式 Web 应用接入方式的最小可运行范本。相关实现可直接在 samples/community/web/pong 与 samples/community/agent/adk/mcp_app_proxy 两个目录中继续研读。【免费下载链接】a2ui项目地址: https://gitcode.com/GitHub_Trending/a2/a2ui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →