跳转到主要内容
在 ChatKit 对话中把 MCP 工具调用结果渲染为可交互的内联应用。
MCP Apps 允许 MCP server 在工具结果中返回一个 HTML 应用。在 Xpert + ChatKit 中,只要 MCP 工具通过 _meta.ui.resourceUri 声明 UI 资源,ChatKit 就会把该资源渲染为对话中的内联 iframe,并通过标准 MCP Apps JSON-RPC bridge 与 Xpert 后端通信。 适合使用 MCP Apps 的场景包括图表、仪表盘、地图、表单、媒体浏览器、下钻分析等需要交互的工具结果。如果只是静态结构化卡片,优先使用 ChatKit Widgets。如果是工作台页面、集成配置页或长期存在的平台页面,继续使用 Xpert extension view。

Xpert 支持的能力

Xpert MCP Apps Host 支持:
  • 通过 _meta.ui.resourceUri 发现工具 UI 元数据
  • ChatKit McpApp 消息组件
  • ui:// MCP resource,MIME 类型为 text/html;profile=mcp-app
  • JSON-RPC bridge 方法,包括 ui/initializeui/notifications/tool-inputui/notifications/tool-resulttools/callresources/readui/open-linkui/messageui/update-model-contextui/request-display-modeui/notifications/size-changed
  • 通过 _meta.ui.visibility = ['app'] 暴露 app-only 工具
  • 从 MCP App resource metadata 读取 _meta.ui.csp_meta.ui.permissionsdomainprefersBorder
  • 短生命周期 app instance,并支持刷新历史消息后用安全元数据恢复
  • 通过 Xpert plugin 安装的 plugin-managed MCP server
ChatKit 实现的是标准 MCP Apps bridge,不实现 ChatGPT 专属的 window.openai API。为了保持跨 host 兼容,MCP App 应优先使用标准 bridge。

架构

聊天历史只保存安全元数据,例如 appInstanceIdresourceUritoolNametoolsetIdserverName,以及受大小限制的初始工具输入/结果快照。原始 HTML 不写入对话历史。页面刷新后,如果内存中的 app instance 已经过期,后端可以根据这些元数据重新连接 Toolset 并恢复 instance。

历史回放和初始工具结果

MCP App 首次渲染时需要收到触发工具调用的 tool-inputtool-result。Xpert + ChatKit 的读取顺序是:
  1. 优先使用 live app instance:只要内存中的 app instance 仍有效,resource 响应会直接返回 live instance 中保存的完整初始 toolResult
  2. live instance 不存在时 revive:刷新历史、Agent Toolset 已关闭或后端进程重启后,MCP Apps Host 会重新连接 Toolset,读取 ui:// resource,并创建新的 app instance。
  3. 最后使用聊天历史中的小型快照:如果消息中保存了受控大小的初始 toolResult,ChatKit 会用它回放 ui/notifications/tool-result;如果没有可用结果,则只发送 ui/notifications/tool-input,不会伪造空结果。
为避免把聊天消息变成大型数据存储,历史消息不会无条件保存完整工具结果。默认情况下,只有序列化后不超过 128KB 的标准 CallToolResult 会内联保存;超过阈值时,消息只保存 toolResultSizetoolResultTruncated: true。阈值可通过后端环境变量调整:
这意味着实时运行或 live instance 未过期时,App 可以拿到完整结果;历史回放时,小结果可以直接复原,大结果需要 App 提供降级体验,例如展示摘要、提示重新运行,或通过 app-visible 工具按需重新读取/分页加载数据。长期需要完整历史回放的大数据结果,应进入专门的 artifact store,而不是继续扩大 message content。

运行时元数据约定

ChatKit 不直接理解任意 MCP server 的实现细节,而是依赖 Xpert 后端保留从当前 Toolset 中发现的 MCP App 元数据。 运行时,当 Xpert 看到 _meta.ui.resourceUri,并且 resource URI 使用 ui:// scheme 时,该 MCP 工具就具备 App 能力。工具 _meta.ui 只应该承载 resourceUrivisibility。CSP、浏览器权限、domainprefersBorder 等资源安全与呈现元数据应放在 MCP App resource 的 _meta.ui 上;Xpert 会优先读取 resources/read content item metadata,并在缺失时使用 resources/list metadata 作为 fallback。 visibility 控制谁可以调用工具: 常见设计是:一个 model-visible 工具负责打开 MCP App,另有一个或多个 app-only 工具供 iframe 交互使用。 Xpert 会把非 model-visible 的工具从 LLM 工具列表中过滤掉。MCP Apps Host 也会拒绝 iframe 调用不可见于 app 或在 Toolset 中被禁用的工具。 插件侧 metadata 和工具注册示例请参考 MCP Tools 和 MCP Apps

Resource 要求

MCP App resource 必须返回带 MCP App profile 的 HTML,MIME 类型为 text/html;profile=mcp-app。注册 resource 是 MCP server 的职责;校验和 sandbox 则由 host 负责。 resource 也可以在 _meta.ui 中声明展示 metadata:titledescriptionicontitledescription 可以是字符串,也可以是 Xpert 风格的 I18nObjecticon 使用共享的 IconDefinition 结构。ChatKit 只把这些安全描述符写入消息历史,并根据当前 ChatKit 语言解析文本,在 MCP App 消息头部渲染 icon/title/description。 安全默认值是严格的:
  • 初始 App HTML 只接受 ui:// resource
  • 原始 HTML 在渲染时读取,不写入聊天历史
  • CSP 默认 deny-by-default,只允许 resource _meta.ui.csp 中声明的域名
  • camera、microphone、geolocation、clipboard-write 默认拒绝,只有 resource _meta.ui.permissions 显式请求时才会通过 iframe allow 放行
  • iframe 内通过 resources/read 读取的资源限定在同一个 MCP server 内,拒绝 http://https://javascript://data://blob:// 等浏览器或脚本 scheme
  • resource domain 当前不会创建 dedicated origin;v1 中视为 host 暂不支持的 metadata
  • iframe 的所有工具调用都经过 Xpert 后端,并执行租户、组织、工作区、Toolset、工具启用状态等校验

主题变量

ChatKit 会在 MCP App HTML 写入 iframe 之前,向 <head> 注入一段宿主主题样式。变量名使用通用 --mcp-app-* 前缀,其他 MCP Apps host 也可以复用同一契约:
MCP App 应使用 --mcp-app-* 公共变量,而不是依赖 ChatKit 内部 CSS class 或私有 token。当前 host 提供: 推荐 App 样式:
如果图表库需要在 JavaScript 中读取颜色,可以直接读取 CSS 变量:
注意:--mcp-app-color-chart-* 是 host 提供的图表色提示,不保证一定适合具体业务图表。如果 host 主题的图表 token 偏灰或仅用于弱化 UI,MCP App 可以定义自己的语义数据色板,例如 --sales-chart-revenue--sales-chart-margin--risk-chart-high,同时继续使用 --mcp-app-* 控制背景、文本、边框、字体和圆角。 ui/initializehostContext.theme 仍返回 light / dark 字符串;同一份变量也会出现在 hostContext.themeCssVariables 中,供 App 初始化图表主题或生成 canvas 配色。 ChatKit 也会通过 hostContext.localehostContext.languagehostContext.direction 传递当前 UI 语言。在 iframe 文档运行前,ChatKit 会把同样的值写入 App HTML 的 langdir 属性。MCP App 应在自己的前端资源中使用这些字段完成标签、数字/日期格式、图表标题和校验消息的本地化。

Bridge 方法

iframe 内部通过 postMessage 发送 JSON-RPC 消息。App 应先初始化并读取 host 能力。初始化请求需要包含 App 信息、能力和协议版本:
ChatKit 会返回标准 McpUiInitializeResult
初始化后,ChatKit 先发送原始工具输入,再发送标准 MCP CallToolResult 形状的工具结果:
App 可以继续调用 app-visible 工具:
App 可以请求 host 安全打开外链:
App 也可以把后续用户消息或模型上下文交回 host:
如果内容高度变化,渲染后通知 ChatKit:

编写和打包

在 Xpert 中推荐用 plugin-managed MCP server 交付 MCP App。插件负责 MCP server 入口、工具元数据、ui:// resource、app-only 工具和安装策略;ChatKit 只负责托管工具调用产生的 app instance。 插件侧实现流程、manifest schema、包结构和本地测试清单请参考 MCP Tools 和 MCP Apps

与其他 Xpert UI 能力的关系

MCP Apps 是 Xpert 的 UI 扩展点之一: 不要把任意 HTML 塞进 widget renderer,也不要让 middleware 承担 resource host 的职责。MCP Apps 应走 MCP resource 和 bridge 流程;extension view 仍然走 Xpert view manifest 以及平台 data/action provider。

启用和运维

生产环境需要显式启用:
ChatKit 只把安全的 MCP App component metadata 写入聊天历史。后端还会为每个 app instance 签发 appInstanceToken,ChatKit 在 resource 和 RPC 请求中带上它。生产环境中,如果签名 token 缺失、过期,或与 tenant、workspace、Toolset、server、tool、resource URI 不匹配,revive、tools/callresources/read 都会被拒绝。 本地非生产环境默认启用 MCP Apps,并兼容没有 appInstanceToken 的旧消息。插件构建、安装、受控 stdio runtime 和运行副本检查请参考 MCP Tools 和 MCP Apps

故障排查

相关文档