A2UI in MCP Apps:构建可渲染 A2UI 载荷的单文件 MCP 应用工件
【免费下载链接】a2ui项目地址: https://gitcode.com/GitHub_Trending/a2/a2ui
导读
本文围绕samples/community/mcp/a2ui-in-mcpapps示例中MCP Server 托管应用(Hosted Application)的构建流程展开,讲解如何把一个 Angular 应用编译、内联为单个自包含的app.html工件,并让 MCP Server 以ui://资源的形式对外提供、进而在沙箱 iframe 中直接渲染 A2UI JSON 载荷。读完本文,你将掌握 MCP Apps 场景下微应用"源码 → 构建 → 单文件内联 → 资源化托管"的完整链路,以及 A2UI 渲染器在其中的接入方式。
一、托管应用在示例中的定位
在samples/community/mcp/a2ui-in-mcpapps这套参考实现中,server/apps/目录承载的是MCP Server 托管的独立应用(MCP App)。其职责是:把原始 A2UI JSON 载荷直接翻译成富交互 UI 渲染结果,演示 A2UI 在 MCP 生态中的集成方式。
其整体架构包含三个层次:
- client/:宿主容器应用(Angular),承载外层安全 iframe;
- server/:MCP Server(Python/uv),提供微应用资源与工具;
- server/apps/:被隔离的微应用源码与构建产物,其中
src/对应Basic计数器应用,editor/对应Editor富文本编辑器应用。
server/apps/README.md(即本文核心文档)描述的正是最后一个层次:托管应用的目录结构、构建工作流与单文件内联过程。
二、目录结构与构建工作流
2.1 三个目录的职责划分
server/apps/下的目录遵循"源码 — 临时构建 — 最终产物"三段式布局:
| 目录 | 作用 | Git 状态 |
|---|---|---|
src/ | 托管应用的源码(如 Angular 应用basic-mcp-app-angular) | 纳入版本管理 |
dist/ | 原始构建输出目录(Angular 编译结果),内联脚本的输入 | 被 git 忽略 |
public/ | 最终打包/内联后的单文件工件输出目录 | 被 git 忽略 |
2.2 三步工作流
文档明确给出标准工作流:
- 写源码:在
src/中修改应用代码; - 构建:运行
src/下的构建脚本,编译应用并将资源内联; - 发布:构建过程生成一个完全自包含的产物(如
public/app.html),由服务器对外提供。
由于dist/与public/均被 git 忽略,全新 clone 的仓库中并不包含最终工件——这与示例根 README 中的提示一致:server/apps/public/下的工件需要自行构建,服务器在缺少该文件时仍能正常启动,只是对应 surface 无法加载。
三、为什么必须单文件内联:MCP App 的安全隔离约束
文档指出,内联是MCP App 安全隔离要求的产物:这类应用通常运行在沙箱化 iframe 中。结合仓库实现可以进一步理解这一约束的根源:
- editor/inline.js 中的注释明确指出:现代 Angular 17+ 默认启用 ES Module 代码分割,
index.html只引用main.js,而main.js依赖外部相对路径的分块(如 markdown 渲染器、zone.js 等)。当这些文件运行在沙箱化的srcdociframe 中时,由于缺少可访问的 base origin,浏览器会阻止相对路径请求。 - server.py 通过
resources/read返回整个 HTML 文本,宿主将其作为text/html;profile=mcp-app资源载入沙箱 iframe——这进一步印证了"一切资源必须内联进单个 HTML"的必要性。
因此,内联并非可选项,而是让 Angular 应用能在沙箱 iframe 中正常运行的强制性前提。
四、内联脚本 inline.js 的工作原理
文档介绍了内联过程的三步:收集dist/raw的 Angular 构建输出 → 将所有 JS/CSS 动态内联进index.html→ 输出public/app.html。仓库中的 inline.js 给出了具体实现:
4.1 输入输出
node inline.js --input <input_dir> --output <output_file>--input:Angular 构建输出目录(实际构建中为../dist/raw);--output:最终单文件路径(实际构建中为../public/app.html)。
脚本会先通过getActualBuildDir()兼容两种输出布局:优先查找<dir>/browser/index.html(Angular 17+ 的浏览器目标目录),否则回退到<dir>/index.html。
4.2 关键处理步骤
JS 内联:把
<script src="...">替换为<script type="module">内联内容</script>。其中main.js会被 esbuild 显式打包:npx -y esbuild "<file>" --bundle --outfile="main.bundled.js" --format=esm --allow-overwrite这样能自动遍历所有 ES Module import,把代码分割产生的多个 chunk 合并为单一自包含文件(editor 版本的 inline.js 对这一 "CRITICAL" 步骤有更详细的注释说明);打包失败时回退为原始文件。
移除 source map 引用:用正则删除
//# sourceMappingURL=...行,减小体积并避免报错。清理 modulepreload:Angular 会自动注入
<link rel="modulepreload">指向动态 chunk,由于已被强制打包进main.js,这些残留的相对请求会在 iframe 内产生 404/CORS 网络错误,因此被整体剔除。CSS 内联:把
<link rel="stylesheet" href="...">替换为<style>内容</style>。落盘:递归创建输出目录并写入
app.html,最后打印产物字节数。
五、构建托管应用的具体命令
文档给出在src/目录下的构建命令:
cd src yarn install yarn build:all这条命令实际执行两个步骤(见 src/package.json):
"build": "ng build --output-path=../dist/raw --configuration production", "inline": "node ../inline.js --input ../dist/raw --output ../public/app.html", "build:all": "yarn run build && yarn run inline"即先以 production 配置运行 Angular 编译到../dist/raw,再触发node inline.js将其单文件内联到public/app.html。
对于示例中的第二个应用(Editor),在 editor/ 目录下执行同样的yarn install && yarn build:all,其inline脚本输出到../public/editor.html。
重要提示:由于
public/被 git 忽略,每次修改src/中的源码后都必须重新执行build:all重新生成工件,否则服务器对外提供的仍是旧版本;服务器启动本身不依赖该文件存在,但对应 surface 将无法渲染。
六、托管应用如何消费 A2UI 载荷
构建出的单文件应用并不仅仅是静态页面,它内置了完整的 A2UI 渲染能力。以 Basic 应用为例,其入口 main.ts 展示了关键集成点:
6.1 启动时注册 A2UI 渲染器
bootstrapApplication(McpAppRoot, { providers: [ provideZonelessChangeDetection(), provideA2UI({ catalog: DEFAULT_CATALOG, theme: theme, }), provideMarkdownRenderer(renderMarkdown), ], });provideA2UI使用DEFAULT_CATALOG(默认组件目录)与自定义主题,provideMarkdownRenderer接入 markdown 渲染支持;依赖声明见 src/package.json(@a2ui/angular与@a2ui/markdown-it)。
6.2 三条核心消息通道
组件通过window.parent.postMessage与宿主进行 JSON-RPC 通信(main.ts):
- 初始化握手:发送
ui/initialize,声明protocolVersion: '2026-01-26'与appCapabilities: {availableDisplayModes: ['inline']},收到响应后再发送ui/notifications/initialized; - 接收工具结果:监听
ui/notifications/tool-result,从content中筛选application/a2ui+json(或application/json+a2ui)类型的EmbeddedResource,解析为消息数组后交给processor.processMessages()渲染; - 动作路由:订阅
MessageProcessor事件流,捕获 A2UI 组件产生的userAction,将其映射为tools/call请求转发给宿主,再把返回结果回灌给处理器更新 UI。
6.3 界面模板
main.html 提供了"获取计数器 A2UI"按钮、状态徽章与 A2UI 渲染 surface(<a2ui-surface>),并带有一个用于调试的原始 JSON 展示区。
七、服务器端如何对接该工件
托管应用的消费端是 server.py:
- 资源声明:
resources/list暴露ui://basic/app与ui://editor/app,MIME 类型为text/html;profile=mcp-app(server.py); - 资源读取:
resources/read按 URI 映射到apps/public/app.html或apps/public/editor.html并返回文件文本(server.py),注释强调 MCP Apps 要求resources/read的返回内容也必须携带text/html;profile=mcp-appMIME 类型; - 工具声明:
get_basic_app通过_meta.ui.resourceUri = "ui://basic/app"预声明 UI 模板,宿主用resources/read获取模板而不会在工具结果中内嵌资源;fetch_counter_a2ui返回 simple_counter_a2ui.json 中定义的初始 A2UI 载荷(dataModelUpdate+surfaceUpdate+beginRendering三段消息,包含 Card/Column/Text/Button 组件树与increase_counter动作);increase_counter累加内存计数器并返回dataModelUpdate更新counter值(server.py)。
由此形成完整闭环:宿主tools/call→ 服务器返回 A2UI JSON → 宿主经沙箱代理转发给 MCP App → Angular 应用MessageProcessor解析渲染 → 用户点击 A2UI Button 触发userAction→ 应用映射为tools/call回传服务器 → 服务器返回dataModelUpdate→ 界面增量更新。
八、运行与验证
按示例根 README 的步骤即可端到端验证整个链路(前提:仓库根目录已执行过yarn install链接 workspace 包):
构建微应用(至少构建一个,否则对应 surface 无法加载):
cd server/apps/src yarn install yarn build:all # 生成 server/apps/public/app.html启动 MCP Server:
cd server uv sync uv run python server.py --transport sse --port 8000(也可使用
--transport stdio走标准输入输出。)启动宿主客户端:
cd client yarn start访问
http://localhost:4200,点击宿主界面中的 CTA,即可看到沙箱 iframe 内的 Basic 应用渲染出由fetch_counter_a2ui返回的计数器 A2UI 界面,点击 "Increase counter" 按钮后数字随increase_counter工具的dataModelUpdate实时递增。
九、小结
server/apps/README.md所描述的托管应用构建流程,是 MCP Apps + A2UI 集成方案中承上启下的关键一环:
- 目录上,
src/(源码)、dist/(中间构建)、public/(单文件工件)三者职责清晰、git 策略明确; - 构建上,
ng build+inline.js(esbuild 打包 + JS/CSS 内联 + modulepreload 清理)产出完全自包含的app.html,满足沙箱 iframe 无法发起相对请求的安全约束; - 运行上,服务器以
text/html;profile=mcp-app资源形式对外提供,应用内部通过 JSON-RPC 与宿主握手、接收 A2UI 载荷、回传用户动作,构成可交互的富 UI 闭环。
对希望在自己项目中复刻"隔离化 MCP 微应用 + A2UI 渲染"模式的开发者而言,inline.js、main.ts 与 server.py 三份文件分别对应构建、渲染、服务三个维度,可作为最小可运行参考直接借鉴。
【免费下载链接】a2ui项目地址: https://gitcode.com/GitHub_Trending/a2/a2ui
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考