沙箱工具容器

MCP App 沙箱工具容器(MCP Apps 对齐):iframe sandbox="allow-scripts"(绝不加 allow-same-origin)+ postMessage JSON-RPC 2.0 双向桥。load 三种来源(ui:// 声明式资源走 opts.resolve / html 片段 srcdoc / URL),idle / loading / ready / error 四态由 data-state 纯 CSS 驱动;call(name, args) 返回 Promise(默认 8s 超时,重载 / 销毁一律 reject pending);全部出入站信封经 icen:kb-sandbox-message + onMessage 双通道可观测,子帧 app.ready 通知即就绪。

先 load 等 app.ready 转 ready,再 call:子应用把参数原样回传 · 入站双闸(只信自家帧 + 可选 origin 白名单)

<div class="toolbar" style="margin-bottom:10px">  <button class="btn btn-sm btn-primary" type="button" id="kb-sandbox-load">load('ui://demo/echo')</button>  <button class="btn btn-sm" type="button" id="kb-sandbox-call">call('echo', { text, n })</button></div><div id="kb-sandbox-demo"></div><p class="demo-label" id="kb-sandbox-log" style="margin-top:8px">先 load 等 app.ready 转 ready,再 call:子应用把参数原样回传 · 入站双闸(只信自家帧 + 可选 origin 白名单)</p>

安装

bun add @icen.ai/ui

一行引入该组件(CSS 与 behavior 自动带上,无需关心内部 CSS 文件名):

import '@icen.ai/ui/kit/kb-sandbox';

也可以用脚手架直接打印引入行:

bunx --bun @icen.ai/ui add kb-sandbox

Astro 项目注意:kit 入口里的 CSS import 在页面 <script> 里会被 Astro 的 client bundle 摇掉——请改在布局 frontmatter 里引 CSS(import '@icen.ai/ui/components/kb-agent.css';),JS 行为仍可走 kit。Vite SPA / webpack 项目无此问题。

用法

import { createKbSandbox } from '@icen.ai/ui/kit/kb-sandbox'; const sb = createKbSandbox(el, {  timeoutMs: 8000,                          // 单次 call 超时(默认 8000)  resolve: (uri) => (uri === 'ui://demo/echo' ? ECHO_APP_HTML : null),  // ui:// 声明式资源(可异步)  onMessage: (m) => audit(m.channel, m.payload),   // 与 icen:kb-sandbox-message 事件双通道});sb.load('ui://demo/echo');                  // 也接受 html 片段 / http(s) URLsb.call('app.callServerTool', { id: 42 }).then((result) => apply(result));sb.destroy(); // 子帧内最小 bootstrap(放进沙箱 html;闭合标签请拆开写 '<\/script>' 防宿主脚本被截断):// <script>//   window.addEventListener('message', (e) => {//     const m = e.data;//     if (!m || m.jsonrpc !== '2.0' || m.method == null) return;//     e.source.postMessage({ jsonrpc: '2.0', id: m.id, result: { ok: true } }, '*');//   });//   parent.postMessage({ jsonrpc: '2.0', method: 'app.ready' }, '*');// <\/script>