关于 metabase-full-app-to-modular-embedding-upgrade
面向使用 Metabase 全应用嵌入的开发者,此 Agent 技能将指向 Metabase 页面的 iframe 迁移为模块化嵌入 Web 组件。它按版本查阅文档,并检查 SSO、页面配置、URL 参数和组件属性的对应关系。
主要功能
- 01按步骤执行迁移清单
- 02扫描 iframe、SSO 和项目配置
- 03将页面类型映射为 Web 组件
- 04转换 URL 参数与组件属性
- 05验证修改并报告证据
适用场景
- 01将仪表板 iframe 迁移为模块化嵌入
- 02将问题或集合页面映射为 Web 组件
- 03按实例版本规划并验证迁移
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ metabase/ agent-skills --skill metabase-full-app-to-modular-embedding-upgrade安装后在 Claude Code 中输入/metabase-full-app-to-modular-embedding-upgrade确认已加载。
详细介绍
执行约定
请按顺序执行工作流步骤,不要跳过任何步骤。先创建检查清单,然后执行每个步骤,并提供证据明确标记为已完成。每个步骤的输出都会用于后续步骤,因此跳过步骤会导致迁移错误。
如果因缺少信息或工具故障而无法完成某个步骤,你必须:
- 将该步骤记录为 ❌ blocked,
- 准确说明缺少什么信息/发生了什么故障,
- 停止(不要继续后续步骤)。
必需的输出结构
你的回复应按以下顺序包含这些部分:
- 步骤 0:Metabase 版本检测
- 步骤 0.1:迁移计划检查清单
- 步骤 1:项目扫描
- 步骤 2:iframe 分析与 Web 组件映射
- 步骤 3:迁移计划
- 步骤 4:应用代码更改
- 步骤 5:验证
- 步骤 6:最终总结
每个步骤部分都应以状态行结尾:
Status: ✅ complete或Status: ❌ blocked
步骤必须按顺序执行——只有前一步状态为 ✅ complete 后,才能开始下一步。
证据要求
- 步骤 0:检测到的 Metabase 版本(来源:Docker 标签、环境变量或用户答复)。
- 步骤 1:每个匹配到的文件路径、每个 iframe 位置、SSO 端点、布局/head 文件、Metabase 配置变量。
- 步骤 2:逐个 iframe 提供已解析的 URL、内容类型、ID、hash 参数,以及映射后的 Web 组件及其属性。
- 步骤 3:完整的逐文件更改计划,并列出精确的旧/新代码。
- 步骤 4:逐个文件说明更改内容并提供实际应用的精确差异。
- 步骤 5:提供每项验证检查的通过/失败结果及证据。
架构一致性
遵循应用现有的架构、模板引擎、布局/局部模板系统、代码风格和路由模式。不要切换范式(例如,不要从模板改为内联 HTML,反之亦然)。如果应用有用于共享模板变量的中间件,请优先使用它,而不是在各个路由处理程序中重复配置。
性能
- 在每个步骤中尽可能并行执行。只要可行,请在同一条消息中并行调用 Grep/Glob/Read。
- 扫描项目时不要使用子 Agent——结果需要留在主上下文中,以便在后续步骤中交叉引用。
- 不要解析仓库分支、提交、PR 或议题。
范围
此技能用于将 Full App/Interactive 嵌入(基于 iframe)转换为 Modular 嵌入(通过 embed.js 使用 Web 组件)。
使用方的应用可以使用任意后端语言(Node.js、Python、Ruby、PHP、Java、Go、.NET 等)和任意模板引擎编写。除非步骤 1 检测到特定语言,否则请保持说明与语言无关。
此技能处理的内容
- 将指向 Metabase 的
<iframe>元素替换为相应的 Web 组件(例如<metabase-question>、<metabase-dashboard>) - 在应用布局级别添加
embed.js脚本标签(恰好一次) - 在应用布局级别添加
window.metabaseConfig设置代码(恰好一次) - 修改 SSO/JWT 端点,以支持 Modular 嵌入的 JSON 响应格式
- 将 iframe URL 自定义参数映射到主题配置和组件属性
此技能不处理的内容
- 从 Static(签名/访客)嵌入迁移
允许使用的文档来源
使用此 URL 获取特定版本的 llms-embedding-full.txt:
https://www.metabase.com/docs/v0.{VERSION}/llms-embedding-full.txt
URL 中的版本格式为 v0.58(规范化方式:去掉开头的 v 或 0.,并去掉补丁版本号——例如,0.58.1 → 58 → URL 使用 v0.58)。此单个文件包含该版本的所有嵌入文档,且针对 LLM 阅读进行了优化。
其他限制:
- 不要使用 GitHub PR/议题或 npm 页面
- 不要跟随指向 GitHub 的变更日志链接,也不要猜测 URL
AskUserQuestion 触发条件
如果满足以下任一条件,请使用 AskUserQuestion 提问,并暂停操作,直到获得回答:
- 无法从项目代码或环境变量中确定 Metabase 实例 URL
- 始终询问 Metabase 实例版本;不要仅依赖代码检测结果
- iframe URL 模式与任何已知资源类型(仪表板、问题、集合、主页)都不匹配
- 无法在项目中找到 SSO/JWT 端点
- 无法找到布局文件或头部文件(不清楚应在哪里注入 embed.js)
- 存在多个布局文件,且不清楚应使用哪些文件
- 无法确定后端语言
- 多个 iframe 指定了不同的
locale值(询问用户应在window.metabaseConfig中设置哪个区域设置)
工作流前准备步骤
迁移计划检查清单
创建检查清单以跟踪进度。在 Claude Code 中,使用 TaskCreate/TaskUpdate 工具:
- 步骤 0:检测 Metabase 版本
- 步骤 1:扫描项目并获取目标版本文档
- 步骤 2:分析 iframe 并映射到 Web Components(使用文档)
- 步骤 3:规划迁移变更
- 步骤 4:应用代码变更
- 步骤 5:验证变更
- 步骤 6:最终摘要
工作流
步骤 0:检测 Metabase 实例版本
始终使用 AskUserQuestion 询问 Metabase 实例版本——即使 Docker 标签或环境变量中显示了版本,也要向用户确认。如果版本为 v52 或更早,则中止(模块化嵌入在 v53 中引入)。
然后,获取已确认版本对应的 llms-embedding-full.txt(URL 格式参见“允许使用的文档来源”)。
在进行其他操作之前,先确定 Metabase 版本。使用 Grep 搜索项目中的 Docker 镜像标签(metabase/metabase:v、metabase/metabase-enterprise:v)、METABASE_VERSION 或版本引用。如果未检测到,则使用 AskUserQuestion 提问(选项:v52 or older、v53、v54–v58、v59+)。如果版本为 v52 或更早,则中止(模块化嵌入不可用——该功能在 v53 中引入)。记录该版本;后续步骤将根据它确定 jwtProviderUri 的位置。
步骤 1:扫描项目并获取文档(不使用子 Agent)
并行执行项目扫描和文档获取——二者相互独立。凡是不存在依赖关系时,尽可能在同一条消息中并行调用工具。
1a:获取目标版本文档
获取目标版本对应的 llms-embedding-full.txt(URL 格式参见“允许使用的文档来源”)。这些文档是目标版本中 Web Component 属性、window.metabaseConfig 选项和 SSO 端点行为的权威来源。步骤 2 中应使用这些文档进行映射,而不是仅依赖硬编码表格。
与下方的项目扫描步骤并行执行。
1b:识别后端语言和框架
- 检查依赖项/构建文件(
package.json、requirements.txt、Gemfile、pom.xml、go.mod、composer.json等)。 - 识别模板引擎,并记录语言和框架。
1c:查找所有 Metabase iframe
使用 Grep 并行搜索以下所有模式:
- 在所有模板/HTML/JSX/view 文件中搜索
<iframe - 在所有服务端代码文件(JS/TS/Python/Ruby/Go/Java/PHP)中搜索
iframe——用于捕获通过字符串拼接或模板字面量构建的 iframe - 搜索与
iframe或src属性相邻的auth/sso。注意:SSO URL 可能在单独的变量或函数中构造,然后传入 iframe 的src——如果 iframe 的src是变量,请追踪其定义以检查是否包含auth/sso。
对于每个匹配的文件,读取整个文件。
1d:查找 SSO/JWT 身份验证代码
使用 Grep 并行搜索以下所有模式:
/auth/sso/sso/metabase或类似的 SSO 路由模式jwt.sign或jwt.encode或JWT或jsonwebtoken或PyJWT或joseJWT_SHARED_SECRET或METABASE_JWT_SHARED_SECRETreturn_to(Metabase SSO 重定向参数)redirect(在auth/sso附近,用于捕获 SSO 重定向逻辑)
对于每个匹配的文件,读取整个文件。
1e:查找布局/head 文件
找到定义 HTML <head> 部分的单个文件(或通用代码路径)——embed.js 和 window.metabaseConfig 将注入此处。
搜索以下内容:
- 模板/视图文件中的
<head>、<!DOCTYPE或<html - 布局/包装器模式:
include('head')、<%- include、{% extends、{% block、layout、base.html、_layout、application.html - 如果应用通过服务器代码中的内联字符串构建 HTML(例如
res.send(...)),请确定生成<head>内容的位置
1f:查找 Metabase 配置
搜索以 METABASE_ 和 MB_ 为前缀的变量。记录每个与 Metabase 相关的变量名称及其读取位置。
输出:结构化项目清单
汇总所有发现并填入:
Backend: {language}, {framework}, {template engine}
Metabase config:
- Site URL variable: {name} (read at {file}:{line})
- Dashboard path variable: {name} (read at {file}:{line})
- JWT secret variable: {name} (read at {file}:{line})
- Other variables: ...
Layout/head file: {path}:{line range} (or "inline HTML in {file}:{line range}")
Iframes found: {count}
- {file}:{line} — {brief description}
- ...
SSO endpoint: {file}:{line} — {route} ({method})
第 2 步:分析 iframe 并映射到 Web 组件(仅在完成第 1 步 ✅ 后)
使用第 1a 步获取的文档作为 Web 组件属性、window.metabaseConfig 选项和 SSO 端点行为的权威参考。以下硬编码表格仅作为备选——如果文档针对目标版本说明了其他属性或不同的行为,请以文档为准。
对于第 1 步中找到的每个 iframe:
2a:解析 iframe URL
从 iframe 的 src 属性中提取以下内容(该属性可能是模板表达式、变量或字面值):
- Metabase 基础 URL:可能来自环境变量、常量或硬编码值
- 资源路径:基础 URL 后的路径,例如
/dashboard/1、/question/entity/abc123、/collection/5 - 资源类型:
dashboard、question、collection或home(如果路径为/) - 实体 ID 或数字 ID:路径中的资源标识符。
- ID 可能是:
- 数字 ID,例如 123
- 数字 ID + slug,例如 123-slug。必须完全移除 slug;包含 slug 会导致资源无法加载。
- 实体 ID——符合
/{resource_type}/entity/{entity_id}模式的 URL 使用实体 ID
- ID 可能是:
- 用于自定义 UI 的 URL 哈希/查询参数(例如
#logo=false&top_nav=false) - SSO 包装:iframe 是否先经过 SSO 端点(例如
/sso/metabase?return_to=...)
2b:将内容类型映射到 Web 组件
| 完整应用 iframe 路径模式 | 模块化 Web 组件 | 必需属性 |
|---|---|---|
/dashboard/{id} 或 /dashboard/entity/{entity_id} | <metabase-dashboard> | dashboard-id="{id or entity_id}" |
/question/{id} 或 /question/entity/{entity_id} | <metabase-question> | question-id="{id or entity_id}" |
/model/{id} 或 /model/entity/{entity_id} | <metabase-question> | question-id="{id or entity_id}" |
/collection/{id} 或 /collection/entity/{entity_id} | <metabase-browser> | initial-collection="{id or entity_id}" |
/(Metabase 主页/根路径) | <metabase-browser> | initial-collection="root" |
如果 iframe 路径由变量动态构建,Web 组件属性应使用相同的变量/表达式。
如果 iframe 路径与任何已知模式都不匹配 → AskUserQuestion。
2c:映射 URL 自定义参数
需要舍弃的参数(不适用 — 模块化 Web 组件不包含 Metabase 应用界面元素):
| 完整版应用参数 | 舍弃原因 |
|---|---|
top_nav | Web 组件没有 Metabase 顶部导航栏 |
side_nav | Web 组件没有 Metabase 侧边栏 |
logo | Web 组件没有 Metabase 或白标 Logo |
search | Web 组件没有 Metabase 搜索栏 |
new_button | 没有 + New 按钮(如果适用,可在 <metabase-browser> 上使用 with-new-question / with-new-dashboard) |
breadcrumbs | Web 组件没有 Metabase 面包屑导航 |
映射到 Web 组件属性的参数:
| 完整版应用参数 | 模块化等效项 |
|---|---|
header=false | 组件上的 with-title="false" |
action_buttons=false | 组件上的 drills="false" |
映射到 window.metabaseConfig 的参数:
| 完整版应用参数 | metabaseConfig 属性 |
|---|---|
locale={code} | locale: "{code}" |
语言区域迁移规则:
- 如果所有 iframe 中都找到相同的语言区域值 → 自动将
locale: "{code}"添加到window.metabaseConfig - 如果不同 iframe 中找到多个不同的语言区域值 → 使用 AskUserQuestion 让用户决定要在
window.metabaseConfig中设置哪个单一语言区域(模块化嵌入仅支持一个全局语言区域)
2d:输出迁移映射表
针对每个 iframe,输出:
iframe #{n}: {file}:{line}
Old: {full iframe HTML or code}
Content type: {dashboard|question|collection|home}
ID: {static value or variable expression}
Dropped params: {list}
Mapped attributes: {list}
New: {exact replacement web component HTML}
第 3 步:规划迁移变更(仅在第 2 步 ✅ 后执行)
创建完整的逐文件变更计划,涵盖以下所有方面。每项变更都应列出目标文件、旧代码和新代码。
3a:注入 embed.js 脚本 — 每个应用恰好一次
- 目标:第 1e 步中识别的布局/头部文件
- 位置:
<head>内(或尽可能靠近其他<script>标签) - 要添加的代码:
<script defer src="{METABASE_SITE_URL}/app/embed.js"></script> {METABASE_SITE_URL}应使用项目现有的模板表达式语法动态渲染。- 如果 Metabase URL 变量仅在特定路由中可用,请通过中间件或模板上下文将其传递给布局。
- 确认无论用户访问哪个页面,该脚本在渲染后的 HTML 中都只出现一次 — 如果加载两次,SDK 会重新初始化并破坏身份验证状态。
3b:metabaseConfig — 每个应用恰好一次
模块化嵌入从 window.metabaseConfig 读取其配置。没有 defineMetabaseConfig() 函数 — 直接赋值配置对象。
- 目标:与 3a 相同的布局/头部文件
- 位置:
embed.js脚本标签之前(必须先设置配置,再加载 embed.js,否则 SDK 无法读取配置) - 要添加的代码(最低限度 — 仅当获取的文档列出了此版本支持的身份验证字段时,才添加这些字段):
<script> window.metabaseConfig = { instanceUrl: "{METABASE_SITE_URL}", // Add auth fields here only if supported by the confirmed version's docs }; </script> - 语言区域:如果在第 2c 步中于任何 iframe 上发现了
locale参数,请在配置对象中添加locale: "{code}"。如果多个 iframe 使用了不同的语言区域值,用户此前已被询问要使用哪个值(根据 AskUserQuestion 触发条件)。 instanceUrl(以及受支持时的jwtProviderUri)应使用项目现有的模板表达式语法动态渲染。- 身份验证配置字段:查阅第 0 步获取的文档,确定
window.metabaseConfig对已确认版本支持哪些字段。例如,jwtProviderUri可能可用,也可能不可用。如果文档列出了该字段,请将其设为完整绝对 URL(例如,http://localhost:9090/sso/metabase)— 相对路径无效。如果文档未列出该字段,则必须在 Metabase 管理设置中配置 JWT 身份提供方 URI(参见第 3f 步)。 window.metabaseConfig应仅设置一次 — 如果它出现在每个 iframe 的代码中,而不是布局中,每个组件都会重新初始化 SDK。
3c:修改 SSO 端点
现有的 SSO 端点会将浏览器重定向到 Metabase 的 /auth/sso?jwt={token}&return_to={path}。
对于模块化嵌入,embed.js SDK 会向 JWT 身份提供方 URI 发送 fetch 请求,并期望收到 JSON 响应。该端点应改为仅返回 JSON——不要保留旧的基于重定向的身份验证流程作为回退方案。
这是一次完整迁移,而非渐进式迁移。旧的基于 iframe 的嵌入方式将被完全替换,因此不再需要重定向行为。
请参阅第 0 步获取的身份验证文档,确认对应版本的 SSO 端点预期响应格式。
限制:
- 不要修改 JWT 签名逻辑——只更改响应的交付方式
- 完全移除旧的重定向行为——该端点只能返回 JSON
- JSON 响应正文必须严格为
{ "jwt": "<token>" }——不得包含其他字段,因为 SDK 会解析这个确切的结构 - 移除所有构建重定向 URL 的代码(例如
new URL("/auth/sso", ...)、searchParams.set("return_to", ...)),因为这些代码现已成为死代码
3d:替换 iframe 的计划
对于第 2d 步的迁移映射表中的每个 iframe:
- 指定:文件路径、要替换的确切旧代码、确切的新代码
- 新的 Web 组件应保留原始 iframe URL 中的任何动态 ID 表达式
- 如果 iframe 有明确的
width/height属性或内联style,应将它们直接应用于 Web 组件元素(例如<metabase-dashboard dashboard-id="1" style="width:800px;height:600px">)——不要用<div>包裹 - 如果 iframe 通过 CSS 类设置样式,应将这些类直接应用于 Web 组件
- 如果 iframe 位于带有样式的容器元素内,应保留该容器
- 移除仅用于 iframe src 的服务器端 SSO URL 构建代码(例如,构建
/sso/metabase?return_to=...)。但不要移除 SSO 端点本身——模块化嵌入身份验证仍需要该端点。 - 如果 iframe src 是通过发送内联 HTML 的服务器端路由处理程序构建的(例如 Express
res.send('<iframe ...')),应替换该处理程序响应字符串中的 iframe HTML
3e:移除死代码
替换 iframe 并转换 SSO 端点后,请识别并移除以下内容:
- 构建 iframe
srcURL 的变量(例如iframeUrl、mbUrl),前提是这些变量不再在任何地方使用 - 附加到 iframe URL 的 URL 参数或修饰符字符串(例如
mods = "logo=false"),前提是它们不再在任何地方引用(检查 SSO 端点——如果重定向逻辑已移除,这些字符串也可能成为死代码) - 从 SSO 端点移除的重定向相关代码(例如,构建
/auth/sso的 URL、处理return_to参数)——这已在第 3c 步中处理 - 构建 Metabase iframe URL 的辅助函数,前提是它们不再被调用
- 不要移除:SSO 端点本身、JWT 签名函数、环境变量读取,或应用程序其他部分使用的任何代码
3f:Metabase 管理员配置说明(用户需手动执行的步骤)
将以下内容列入计划——它们会包含在最终摘要中:
- 启用模块化嵌入:Admin > Embedding > 切换“Enable modular embedding”
- 配置 CORS 来源:Admin > Embedding > Modular embedding > 添加宿主应用的域名(例如,
http://localhost:9090) - 配置 JWT 身份提供方 URI:Admin > Authentication > JWT > 将其设置为 SSO 端点的完整 URL(例如,
http://localhost:9090/sso/metabase)。查阅获取的文档,确定对于已确认的版本,此项是必需还是可选(取决于window.metabaseConfig是否支持 JWT 提供方字段)。 - JWT 共享密钥:无需更改——沿用 Full App 嵌入设置中现有的共享密钥
第 4 步:应用代码更改(仅在第 3 步 ✅ 后)
按以下顺序应用第 3 步中的所有更改(先修改后端,以尽量缩短功能异常的时间窗口):
- 首先:修改 SSO 端点,使其返回 JSON(第 3c 步)——这只涉及后端
- 其次:在布局/head 文件中添加
window.metabaseConfig赋值和 embed.js 脚本标签(第 3b + 3a 步,配置要放在 embed.js 之前) - 第三:将每个 iframe 替换为 Web 组件(第 3d 步),一次修改一个文件
- 第四:移除无用代码(第 3e 步)
约束:
- 每项更改都使用 Edit 工具,并通过精确的
old_string/new_string进行修改 - 不要添加新的软件包依赖项——模块化嵌入只需要由 Metabase 实例提供的 embed.js 脚本
- 不要更改环境变量名称
- 如果一个文件需要多处修改,请按从上到下的顺序进行,以避免偏移问题
第 5 步:验证更改(仅在第 4 步 ✅ 后)
执行以下所有检查。检查 5a–5c 可以并行执行(这些都是独立的 grep 搜索)。检查 5d 和 5e 需要读取特定文件。每项检查都应明确标记通过或失败。
5a:不再存在 Metabase iframe
使用 Grep 在整个项目文件中搜索 <iframe 和 iframe(排除 node_modules、.git 和锁文件)。
确认不再存在指向 Metabase 的 Full App / Interactive Embedding iframe。
非 Metabase iframe 应保持不变。访客嵌入(以前称为“静态嵌入”)或公开嵌入 iframe 也应保持不变——它们使用不同的 URL 模式(例如 /embed/ 或 /public/),不属于此次迁移范围。
通过标准:未找到 Full App / Interactive Embedding iframe(访客/公开嵌入 iframe 除外)。
5b:embed.js 恰好出现一次
使用 Grep 在整个项目文件中搜索 /app/embed.js(排除 node_modules、.git)。此模式专指 Metabase 的嵌入脚本 URL,可避免将其他工具可能使用的通用 embed.js 文件名误判为匹配项。
通过标准:在布局/head 文件中恰好出现一次。
5c:window.metabaseConfig 恰好设置一次
使用 Grep 在整个项目文件中搜索 window.metabaseConfig(排除 node_modules、.git)。
通过标准:恰好出现一次(即布局/head 文件中的赋值)。
5d:SSO 端点仅返回 JSON
读取 SSO 端点文件。确认:
- 端点返回包含
{ jwt: token }的 JSON 响应 - 旧的重定向逻辑(
res.redirect、new URL("/auth/sso", ...)、return_to)已完全移除 - 不存在
response=json条件检查(因为现在仅使用 JSON 响应格式)
通过标准:端点仅返回 JSON,不再保留重定向回退逻辑。
5e:抽查已修改的文件
读取每个已修改的文件并确认:
- Web 组件具有必需的属性(
dashboard-id、question-id或initial-collection) - 模板语法有效(标签已闭合,表达式正确)
- 第 3e 步中识别出的无用代码变量已移除
通过标准:所有检查均通过。
如果任何检查失败:
- 立即修复问题
- 重新运行相应的检查
- 如果尝试 3 次后仍无法修复,将第 5 步标记为 ❌ 阻塞,并说明失败的检查及原因
第 6 步:输出摘要
将最终输出整理为以下部分:
- 已应用的更改:列出每个已修改文件,并用一句话描述各项更改
- Web 组件映射:以表格展示每个旧 iframe → 新 Web 组件
- 已删除的参数:列出已删除的 Full App iframe 参数,并简要说明它们为何不适用于模块化嵌入
- 主题配置:列出映射到
window.metabaseConfig中的主题/外观设置 - 所需的手动步骤(第 3f 步中的 Metabase 管理员配置):
- 启用模块化嵌入
- 配置 CORS 来源
- 配置 JWT Identity Provider URI
- 发现的其他管理员步骤
- 用户应注意的行为差异:
- 用户无法再在单个嵌入中浏览仪表板、问题和集合(每个 Web 组件均独立运行)
- 不再显示 Metabase 应用外壳(导航、侧边栏、搜索)
- 无法映射的 iframe 参数
重试策略
文档获取:
- 如果获取
llms-embedding-full.txt时返回 404,请确认 Metabase 版本号并重试。如果仍然失败,将第 1 步标记为 ❌ 已阻塞。
验证:
- 如果第 5 步中的任何验证检查在尝试修复 3 次后仍未通过,请将第 5 步标记为 ❌ 已阻塞,并报告失败的检查及原因。
- 如果 AskUserQuestion 未得到回答,则该步骤继续保持阻塞状态;不要猜测或基于假设继续操作。