metabase-full-app-to-modular-embedding-upgrade

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Execution contract

执行约定

Follow the workflow steps in order — do not skip any step. Create the checklist first, then execute each step and explicitly mark it done with evidence. Each step's output feeds into the next, so skipping steps produces wrong migrations.
If you cannot complete a step due to missing info or tool failure, you must:
  1. record the step as ❌ blocked,
  2. explain exactly what is missing / what failed,
  3. stop (do not proceed to later steps).
按顺序遵循工作流步骤——不要跳过任何步骤。首先创建检查清单,然后执行每个步骤并明确标记完成状态及证据。每个步骤的输出作为下一个步骤的输入,因此跳过步骤会导致错误的迁移。
如果因信息缺失或工具故障无法完成某一步骤,你必须:
  1. 将该步骤标记为 ❌ 受阻
  2. 准确说明缺失的内容/故障原因
  3. 停止操作(不要继续后续步骤)

Required output structure

要求的输出结构

Your response should contain these sections in this order:
  1. Step 0: Metabase Version Detection
  2. Step 0.1: Migration Plan Checklist
  3. Step 1: Project Scan
  4. Step 2: iframe Analysis & Web Component Mapping
  5. Step 3: Migration Plan
  6. Step 4: Applied Code Changes
  7. Step 5: Validation
  8. Step 6: Final Summary
Each step section should end with a status line:
  • Status: ✅ complete
    or
    Status: ❌ blocked
Steps are sequential — do not start a step until the previous one is ✅ complete.
你的响应应按以下顺序包含这些部分:
  1. 步骤0:Metabase版本检测
  2. 步骤0.1:迁移计划检查清单
  3. 步骤1:项目扫描
  4. 步骤2:iframe分析与Web组件映射
  5. 步骤3:迁移计划
  6. 步骤4:应用代码变更
  7. 步骤5:验证
  8. 步骤6:最终总结
每个步骤部分末尾应包含状态行:
  • Status: ✅ complete
    Status: ❌ blocked
步骤按顺序执行——前一个步骤标记为 ✅ complete 后才能开始下一个步骤。

Evidence requirements

证据要求

  • Step 0: Metabase version detected (source: Docker tag, env var, or user answer).
  • Step 1: every matched file path, every iframe location, SSO endpoint, layout/head file, Metabase config variables.
  • Step 2: per iframe — parsed URL, content type, ID, hash params, mapped web component with attributes.
  • Step 3: the complete file-by-file change plan with exact old/new code.
  • Step 4: per file — what was changed and exact diffs applied.
  • Step 5: each validation check's pass/fail result with evidence.
  • 步骤0:检测到Metabase版本(来源:Docker标签、环境变量或用户回答)。
  • 步骤1:所有匹配的文件路径、所有iframe位置、SSO端点、布局/头部文件、Metabase配置变量。
  • 步骤2:每个iframe的解析URL、内容类型、ID、哈希参数、带属性的映射Web组件。
  • 步骤3:完整的逐文件变更计划,包含精确的新旧代码。
  • 步骤4:每个文件的变更内容及精确的差异对比。
  • 步骤5:每个验证检查的通过/失败结果及证据。

Architectural conformance

架构一致性

Follow the app's existing architecture, template engine, layout/partial system, code style, and route patterns. Do not switch paradigms (e.g., templates to inline HTML or vice versa). If the app has middleware for shared template variables, prefer that over duplicating across route handlers.
遵循应用现有的架构、模板引擎、布局/部分系统、代码风格和路由模式。不要切换范式(例如,从模板切换到内联HTML,反之亦然)。如果应用有用于共享模板变量的中间件,优先使用中间件,而非在路由处理程序中重复代码。

Performance

性能

  • Maximize parallelism within each step. Use parallel Grep/Glob/Read calls in a single message wherever possible.
  • Do not use sub-agents for project scanning — results need to stay in the main context for cross-referencing in later steps.
  • Do not parse repo branches, commits, PRs, or issues.
  • 在每个步骤内最大化并行性。尽可能在单个消息中使用并行Grep/Glob/Read调用。
  • 不要使用子代理进行项目扫描——结果需要保留在主上下文中,以便在后续步骤中交叉引用。
  • 不要解析仓库分支、提交、PR或问题。

Scope

范围

This skill converts Full App / Interactive embedding (iframe-based) to Modular embedding (web-component-based via
embed.js
).
The consumer's app may be written in any backend language (Node.js, Python, Ruby, PHP, Java, Go, .NET, etc.) with any template engine. Keep instructions language-agnostic unless a specific language is detected in Step 1.
此技能将全应用/交互式嵌入(基于iframe)转换为模块化嵌入(基于
embed.js
的Web组件)。
用户的应用可使用任何后端语言编写(Node.js、Python、Ruby、PHP、Java、Go、.NET等),搭配任何模板引擎。除非在步骤1中检测到特定语言,否则保持说明与语言无关。

What this skill handles

此技能处理的内容

  • Replacing
    <iframe>
    elements pointing to Metabase with appropriate web components (e.g.
    <metabase-question>
    ,
    <metabase-dashboard>
    )
  • Adding the
    embed.js
    script tag (exactly once at app layout level)
  • Adding
    window.metabaseConfig
    setup code (exactly once at app layout level)
  • Modifying SSO/JWT endpoints to support modular embedding's JSON response format
  • Mapping iframe URL customization parameters to theme config and component attributes
  • 将指向Metabase的
    <iframe>
    元素替换为合适的Web组件(例如
    <metabase-question>
    <metabase-dashboard>
  • 添加
    embed.js
    脚本标签(在应用布局级别仅添加一次)
  • 添加
    window.metabaseConfig
    设置代码(在应用布局级别仅添加一次)
  • 修改SSO/JWT端点以支持模块化嵌入的JSON响应格式
  • 将iframe URL自定义参数映射到主题配置和组件属性

What this skill does not handle

此技能不处理的内容

  • Migrating from Static (signed/guest) embedding
  • 从静态(签名/访客)嵌入迁移

Allowed documentation sources

允许的文档来源

Fetch the version-specific
llms-embedding-full.txt
using this URL:
https://www.metabase.com/docs/v0.{VERSION}/llms-embedding-full.txt
The version in the URL uses the format
v0.58
(normalize: strip leading
v
or
0.
, drop patch — e.g.,
0.58.1
58
→ URL uses
v0.58
). This single file contains all embedding documentation for that version, optimized for LLM consumption.
Other constraints:
  • No GitHub PRs/issues or npm pages
  • Do not follow changelog links to GitHub or guess URLs
使用以下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 triggers

AskUserQuestion触发条件

Use AskUserQuestion and halt until answered if:
  • The Metabase instance URL cannot be determined from project code or environment variables
  • Always ask for the Metabase instance version — do not rely solely on code detection
  • An iframe URL pattern does not match any known resource type (dashboard, question, collection, home)
  • No SSO/JWT endpoint can be identified in the project
  • No layout/head file can be identified (unclear where to inject embed.js)
  • Multiple layout files exist and it is unclear which one(s) to use
  • The backend language cannot be determined
  • Multiple iframes specify different
    locale
    values (ask user which locale to set in
    window.metabaseConfig
    )
在以下情况下使用AskUserQuestion并暂停操作,直到获得回答:
  • 无法从项目代码或环境变量中确定Metabase实例URL
  • 始终询问用户Metabase实例版本——不要仅依赖代码检测
  • iframe URL模式与任何已知资源类型(仪表板、问题、集合、主页)不匹配
  • 在项目中无法识别到SSO/JWT端点
  • 无法识别到布局/头部文件(不清楚应在何处注入embed.js)
  • 存在多个布局文件,且不清楚应使用哪一个
  • 无法确定后端语言
  • 多个iframe指定了不同的
    locale
    值(询问用户应在
    window.metabaseConfig
    中设置哪个区域设置)

Pre-workflow steps

工作流前置步骤

Migration Plan Checklist

迁移计划检查清单

Create a checklist to track progress. In Claude Code, use TaskCreate/TaskUpdate tools:
  • Step 0: Detect Metabase version
  • Step 1: Scan project + fetch target version docs
  • Step 2: Analyze iframes and map to web components (using docs)
  • Step 3: Plan migration changes
  • Step 4: Apply code changes
  • Step 5: Validate changes
  • Step 6: Final summary
创建检查清单以跟踪进度。在Claude Code中,使用TaskCreate/TaskUpdate工具:
  • 步骤0:检测Metabase版本
  • 步骤1:扫描项目 + 获取目标版本文档
  • 步骤2:分析iframe并映射到Web组件(使用文档)
  • 步骤3:规划迁移变更
  • 步骤4:应用代码变更
  • 步骤5:验证变更
  • 步骤6:最终总结

Workflow

工作流

Step 0: Detect Metabase instance version

步骤0:检测Metabase实例版本

Always AskUserQuestion for the Metabase instance version — even if a version appears in Docker tags or env vars, confirm it with the user. Abort if v52 or older (modular embedding was introduced in v53).
Then fetch
llms-embedding-full.txt
for the confirmed version (see "Allowed documentation sources" for URL format).
Before anything else, determine the Metabase version. Grep the project for Docker image tags (
metabase/metabase:v
,
metabase/metabase-enterprise:v
),
METABASE_VERSION
, or version references. If undetected, AskUserQuestion (options:
v52 or older
,
v53
,
v54–v58
,
v59+
). Abort if v52 or older (modular embedding not available — it was introduced in v53). Record the version — it controls
jwtProviderUri
placement in later steps.
始终使用AskUserQuestion询问用户Metabase实例版本——即使在Docker标签或环境变量中显示了版本,也要与用户确认。如果版本为v52或更早版本,则中止操作(模块化嵌入在v53中引入)。
然后获取确认版本的
llms-embedding-full.txt
(请参阅“允许的文档来源”了解URL格式)。
在开始任何操作之前,先确定Metabase版本。在项目中搜索Docker镜像标签(
metabase/metabase:v
metabase/metabase-enterprise:v
)、
METABASE_VERSION
或版本引用。如果未检测到,使用AskUserQuestion(选项:
v52 or older
v53
v54–v58
v59+
)。如果版本为v52或更早版本,则中止操作(模块化嵌入不可用——它在v53中引入)。记录版本——它将控制后续步骤中
jwtProviderUri
的位置。

Step 1: Scan the project + fetch docs no sub-agent)

步骤1:扫描项目 + 获取文档(不使用子代理)

Perform the project scan and doc fetch concurrently — they are independent. Use parallel tool calls within a single message wherever there are no dependencies.
并行执行项目扫描和文档获取——它们相互独立。只要没有依赖关系,就在单个消息中使用并行工具调用。

1a: Fetch target version docs

1a:获取目标版本文档

Fetch
llms-embedding-full.txt
for the target version (see "Allowed documentation sources" for URL format). These docs are the authoritative source for web component attributes,
window.metabaseConfig
options, and SSO endpoint behavior for the target version. Use them in Step 2 for mapping instead of relying on hardcoded tables alone.
Launch this concurrently with the project scan steps below.
获取目标版本的
llms-embedding-full.txt
(请参阅“允许的文档来源”了解URL格式)。这些文档是Web组件属性、
window.metabaseConfig
选项和目标版本SSO端点行为的权威来源。在步骤2中使用这些文档进行映射,而非仅依赖硬编码表格。
与下面的项目扫描步骤同时启动此操作。

1b: Identify backend language and framework

1b:识别后端语言和框架

  • Check for dependency/build files (
    package.json
    ,
    requirements.txt
    ,
    Gemfile
    ,
    pom.xml
    ,
    go.mod
    ,
    composer.json
    , etc.).
  • Identify the template engine and record the language and framework.
  • 检查依赖/构建文件(
    package.json
    requirements.txt
    Gemfile
    pom.xml
    go.mod
    composer.json
    等)。
  • 识别模板引擎并记录语言和框架。

1c: Find ALL Metabase iframes

1c:查找所有Metabase iframe

Use Grep to search for all of these patterns (in parallel):
  • <iframe
    in all template/HTML/JSX/view files
  • iframe
    in all server-side code files (JS/TS/Python/Ruby/Go/Java/PHP) — catches iframes built via string concatenation or template literals
  • auth/sso
    adjacent to
    iframe
    or
    src
    attributes. Note: the SSO URL may be constructed in a separate variable or function and passed to the iframe
    src
    — if the iframe
    src
    is a variable, trace its definition to check for
    auth/sso
    .
For each file with a match, read the entire file.
使用Grep并行搜索所有这些模式:
  • 所有模板/HTML/JSX/视图文件中的
    <iframe
  • 所有服务器端代码文件(JS/TS/Python/Ruby/Go/Java/PHP)中的
    iframe
    ——捕获通过字符串拼接或模板字面量构建的iframe
  • iframe
    src
    属性相邻的
    auth/sso
    。注意:SSO URL可能在单独的变量或函数中构建,并传递给iframe的
    src
    ——如果iframe的
    src
    是变量,请跟踪其定义以检查是否包含
    auth/sso
对于每个匹配的文件,读取整个文件内容。

1d: Find SSO/JWT authentication code

1d:查找SSO/JWT认证代码

Use Grep to search for all of these patterns (in parallel):
  • /auth/sso
  • /sso/metabase
    or similar SSO route patterns
  • jwt.sign
    or
    jwt.encode
    or
    JWT
    or
    jsonwebtoken
    or
    PyJWT
    or
    jose
  • JWT_SHARED_SECRET
    or
    METABASE_JWT_SHARED_SECRET
  • return_to
    (Metabase SSO redirect parameter)
  • redirect
    near
    auth/sso
    (catches the SSO redirect logic)
For each matching file, read the entire file.
使用Grep并行搜索所有这些模式:
  • /auth/sso
  • /sso/metabase
    或类似的SSO路由模式
  • jwt.sign
    jwt.encode
    JWT
    jsonwebtoken
    PyJWT
    jose
  • JWT_SHARED_SECRET
    METABASE_JWT_SHARED_SECRET
  • return_to
    (Metabase SSO重定向参数)
  • auth/sso
    附近的
    redirect
    (捕获SSO重定向逻辑)
对于每个匹配的文件,读取整个文件内容。

1e: Find the layout/head file(s)

1e:查找布局/头部文件

Find the single file (or common code path) where the HTML
<head>
section is defined — this is where
embed.js
and
window.metabaseConfig
will be injected.
Search for:
  • <head>
    or
    <!DOCTYPE
    or
    <html
    in template/view files
  • Layout/wrapper patterns:
    include('head')
    ,
    <%- include
    ,
    {% extends
    ,
    {% block
    ,
    layout
    ,
    base.html
    ,
    _layout
    ,
    application.html
  • If the app builds HTML via inline strings in server code (e.g.,
    res.send(...)
    ), identify where the
    <head>
    content is generated
查找定义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: Find Metabase configuration

1f:查找Metabase配置

Grep for
METABASE_
and
MB_
prefixed variables. Record every Metabase-related variable name and where it is read.
搜索以
METABASE_
MB_
为前缀的变量。记录每个Metabase相关变量的名称及其读取位置。

Output: Structured Project Inventory

输出:结构化项目清单

Compile all findings into:
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})
将所有发现整理为:
后端:{language}, {framework}, {template engine}
Metabase配置:
  - 站点URL变量:{name}(读取位置:{file}:{line})
  - 仪表板路径变量:{name}(读取位置:{file}:{line})
  - JWT密钥变量:{name}(读取位置:{file}:{line})
  - 其他变量:...
布局/头部文件:{path}:{line range}(或“内联HTML位于{file}:{line range}”)
找到的iframe数量:{count}
  - {file}:{line} — {简要描述}
  - ...
SSO端点:{file}:{line} — {route} ({method})

Step 2: Analyze iframes and map to web components (only after Step 1 ✅)

步骤2:分析iframe并映射到Web组件(仅在步骤1 ✅完成后执行)

Use the documentation fetched in Step 1a as the authoritative reference for web component attributes,
window.metabaseConfig
options, and SSO endpoint behavior. The hardcoded tables below are fallbacks — if the docs describe additional attributes or different behavior for the target version, prefer the docs.
For each iframe found in Step 1:
使用步骤1a中获取的文档作为Web组件属性、
window.metabaseConfig
选项和SSO端点行为的权威参考。下面的硬编码表格是备选方案——如果文档描述了目标版本的额外属性或不同行为,优先使用文档内容。
对于步骤1中找到的每个iframe:

2a: Parse the iframe URL

2a:解析iframe URL

Extract from the iframe
src
attribute (which may be a template expression, variable, or literal):
  • Metabase base URL: may come from env var, constant, or be hardcoded
  • Resource path: the path after the base URL, e.g.,
    /dashboard/1
    ,
    /question/entity/abc123
    ,
    /collection/5
  • Resource type:
    dashboard
    ,
    question
    ,
    collection
    , or
    home
    (if path is
    /
    )
  • Entity ID or numeric ID: the resource identifier in the path.
    • An ID may be:
      • a numeric id, e.g. 123
      • a numeric id + slug, e.g. 123-slug. You need to remove the slug completely; including the slug will prevent the resource from loading.
      • an entity id — URLs with pattern
        /{resource_type}/entity/{entity_id}
        use entity IDs
  • URL hash/query parameters used for UI customization (e.g.,
    #logo=false&top_nav=false
    )
  • SSO wrapping: whether the iframe goes through an SSO endpoint first (e.g.,
    /sso/metabase?return_to=...
    )
从iframe的
src
属性(可能是模板表达式、变量或字面量)中提取:
  • Metabase基础URL:可能来自环境变量、常量或硬编码
  • 资源路径:基础URL之后的路径,例如
    /dashboard/1
    /question/entity/abc123
    /collection/5
  • 资源类型
    dashboard
    question
    collection
    home
    (如果路径为
    /
  • 实体ID或数字ID:路径中的资源标识符。
    • ID可能是:
      • 数字ID,例如123
      • 数字ID + 别名,例如123-slug。你需要完全移除别名;包含别名会阻止资源加载。
      • 实体ID——模式为
        /{resource_type}/entity/{entity_id}
        的URL使用实体ID
  • 用于UI自定义的URL哈希/查询参数(例如
    #logo=false&top_nav=false
  • SSO包装:iframe是否先经过SSO端点(例如
    /sso/metabase?return_to=...

2b: Map content type to web component

2b:将内容类型映射到Web组件

Full App iframe path patternModular Web ComponentRequired Attribute
/dashboard/{id}
or
/dashboard/entity/{entity_id}
<metabase-dashboard>
dashboard-id="{id or entity_id}"
/question/{id}
or
/question/entity/{entity_id}
<metabase-question>
question-id="{id or entity_id}"
/model/{id}
or
/model/entity/{entity_id}
<metabase-question>
question-id="{id or entity_id}"
/collection/{id}
or
/collection/entity/{entity_id}
<metabase-browser>
initial-collection="{id or entity_id}"
/
(Metabase home / root)
<metabase-browser>
initial-collection="root"
If the iframe path is built dynamically from a variable, the web component attribute should use the same variable/expression.
If an iframe path does not match any known pattern → AskUserQuestion.
全应用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: Map URL customization parameters

2c:映射URL自定义参数

Parameters to drop (not applicable — modular web components do not include Metabase application chrome):
Full App ParameterWhy it is dropped
top_nav
Web components have no Metabase top navigation bar
side_nav
Web components have no Metabase sidebar
logo
Web components have no Metabase or whitelabel logo
search
Web components have no Metabase search bar
new_button
No
+ New
button (use
with-new-question
/
with-new-dashboard
on
<metabase-browser>
if applicable)
breadcrumbs
Web components have no Metabase breadcrumbs
Parameters that map to web component attributes:
Full App ParameterModular Equivalent
header=false
with-title="false"
on the component
action_buttons=false
drills="false"
on the component
Parameters that map to
window.metabaseConfig
:
Full App ParametermetabaseConfig Property
locale={code}
locale: "{code}"
Locale migration rules:
  • If one locale value is found across all iframes → add
    locale: "{code}"
    to
    window.metabaseConfig
    automatically
  • If multiple different locale values are found across iframes → AskUserQuestion to let the user decide which single locale to set in
    window.metabaseConfig
    (modular embedding supports only one global locale)
需丢弃的参数(不适用——模块化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中只发现一个区域设置值→自动在
    window.metabaseConfig
    中添加
    locale: "{code}"
  • 如果在多个iframe中发现不同的区域设置值→使用AskUserQuestion让用户决定在
    window.metabaseConfig
    中设置哪个单一区域设置(模块化嵌入仅支持一个全局区域设置)

2d: Output Migration Mapping Table

2d:输出迁移映射表

For each iframe, output:
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}
对于每个iframe,输出:
iframe #{n}: {file}:{line}
  旧代码:{完整的iframe HTML或代码}
  内容类型:{dashboard|question|collection|home}
  ID:{静态值或变量表达式}
  丢弃的参数:{列表}
  映射的属性:{列表}
  新代码:{精确的替换Web组件HTML}

Step 3: Plan migration changes (only after Step 2 ✅)

步骤3:规划迁移变更(仅在步骤2 ✅完成后执行)

Create a complete file-by-file change plan covering all areas below. Every change should be specified with the target file, the old code, and the new code.
创建完整的逐文件变更计划,涵盖以下所有领域。每个变更应指定目标文件、旧代码和新代码。

3a: embed.js script injection — exactly once per app

3a:embed.js脚本注入——每个应用仅注入一次

  • Target: the layout/head file identified in Step 1e
  • Location: inside
    <head>
    (or as close as possible to other
    <script>
    tags)
  • Code to add:
    html
    <script defer src="{METABASE_SITE_URL}/app/embed.js"></script>
  • {METABASE_SITE_URL}
    should be rendered dynamically using the project's existing template expression syntax.
  • If the Metabase URL variable is only available in specific routes, pass it to the layout via middleware or template context.
  • Verify this will appear exactly once in the rendered HTML regardless of which page the user visits — if it loads twice, the SDK reinitializes and breaks auth state.
  • 目标:步骤1e中识别的布局/头部文件
  • 位置:在
    <head>
    内部(或尽可能靠近其他
    <script>
    标签)
  • 需添加的代码
    html
    <script defer src="{METABASE_SITE_URL}/app/embed.js"></script>
  • {METABASE_SITE_URL}
    应使用项目现有的模板表达式语法动态渲染。
  • 如果Metabase URL变量仅在特定路由中可用,请通过中间件或模板上下文将其传递到布局。
  • 验证无论用户访问哪个页面,此脚本在渲染后的HTML中仅出现一次——如果加载两次,SDK会重新初始化并破坏认证状态。

3b: metabaseConfig — exactly once per app

3b:metabaseConfig——每个应用仅设置一次

Modular embedding reads its configuration from
window.metabaseConfig
. There is no
defineMetabaseConfig()
function — assign the config object directly.
  • Target: same layout/head file as 3a
  • Location: before the embed.js script tag (the config must be set before embed.js loads, otherwise the SDK has no config to read)
  • Code to add (minimum — add auth fields only if the fetched docs list them for this version):
    html
    <script>
      window.metabaseConfig = {
        instanceUrl: "{METABASE_SITE_URL}",
        // Add auth fields here only if supported by the confirmed version's docs
      };
    </script>
  • Locale: If a
    locale
    parameter was found on any iframe in Step 2c, add
    locale: "{code}"
    to the config object. If multiple iframes had different locale values, the user will have already been asked which one to use (per AskUserQuestion trigger).
  • instanceUrl
    (and
    jwtProviderUri
    if supported) should be rendered dynamically using the project's template expression syntax.
  • Auth config fields: Consult the docs fetched in Step 0 to determine which fields
    window.metabaseConfig
    supports for the confirmed version. For example,
    jwtProviderUri
    may or may not be available. If the docs list it, include it as a full absolute URL (e.g.,
    http://localhost:9090/sso/metabase
    ) — relative paths don't work. If the docs don't list it, the JWT Identity Provider URI must be configured in Metabase admin settings instead (see Step 3f).
  • window.metabaseConfig
    should be set exactly once — if it appears in per-iframe code instead of the layout, each component will re-initialize the SDK.
模块化嵌入从
window.metabaseConfig
读取其配置。不存在
defineMetabaseConfig()
函数——直接赋值配置对象即可。
  • 目标:与3a相同的布局/头部文件
  • 位置:在embed.js脚本标签之前(必须在embed.js加载前设置配置,否则SDK无法读取配置)
  • 需添加的代码(基础版——仅当获取的文档列出该版本支持的认证字段时才添加):
    html
    <script>
      window.metabaseConfig = {
        instanceUrl: "{METABASE_SITE_URL}",
        // 仅当确认版本的文档支持时,才在此处添加认证字段
      };
    </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 endpoint modification

3c:SSO端点修改

The existing SSO endpoint currently redirects the browser to Metabase's
/auth/sso?jwt={token}&return_to={path}
.
For modular embedding, the embed.js SDK sends a fetch request to the JWT Identity Provider URI and expects a JSON response. The endpoint should be converted to return JSON only — do not keep a fallback to the old redirect-based auth flow.
This is a full migration, not a gradual one. The old iframe-based embedding is being completely replaced, so the redirect behavior is no longer needed.
Consult the auth docs fetched in Step 0 for the expected SSO endpoint response format for the confirmed version.
Constraints:
  • Do not modify the JWT signing logic — only change how the response is delivered
  • Remove the old redirect behavior entirely — the endpoint should only return JSON
  • The JSON response body should be exactly
    { "jwt": "<token>" }
    — no other fields, because the SDK parses this exact shape
  • Remove any code that builds the redirect URL (e.g.,
    new URL("/auth/sso", ...)
    ,
    searchParams.set("return_to", ...)
    ) as it is now dead code
现有的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 replacement plan

3d:iframe替换计划

For each iframe from Step 2d's Migration Mapping Table:
  • Specify: file path, exact old code to replace, exact new code
  • The new web component should preserve any dynamic ID expressions from the original iframe URL
  • If the iframe had explicit
    width
    /
    height
    attributes or inline
    style
    , apply them directly to the web component element (e.g.,
    <metabase-dashboard dashboard-id="1" style="width:800px;height:600px">
    ) — do not wrap in a
    <div>
  • If the iframe was styled via CSS classes, apply those classes directly to the web component
  • If the iframe was inside a container element with styles, keep that container
  • Remove any server-side SSO URL construction that was used only for the iframe src (e.g., building
    /sso/metabase?return_to=...
    ). But do not remove the SSO endpoint itself — it is still needed for modular embedding auth.
  • If the iframe src was built via a server-side route handler that sends inline HTML (e.g., Express
    res.send('<iframe ...')
    ), replace the iframe HTML within that handler's response string
对于步骤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: Dead code removal

3e:无用代码移除

After replacing iframes and converting the SSO endpoint, identify and remove:
  • Variables that built the iframe
    src
    URL (e.g.,
    iframeUrl
    ,
    mbUrl
    ) if they are no longer used anywhere
  • URL parameter/modifier strings that were appended to iframe URLs (e.g.,
    mods = "logo=false"
    ) if they are no longer referenced anywhere (check the SSO endpoint — if the redirect logic was removed, these strings may now be dead code too)
  • Redirect-related code removed from the SSO endpoint (e.g., URL construction for
    /auth/sso
    ,
    return_to
    parameter handling) — this is already handled as part of Step 3c
  • Helper functions that constructed Metabase iframe URLs if they are no longer called
  • Do not remove: the SSO endpoint itself, JWT signing function, environment variable reads, or any code that is used by other parts of the application
替换iframe并转换SSO端点后,识别并移除:
  • 构建iframe
    src
    URL的变量(例如
    iframeUrl
    mbUrl
    ),如果它们不再在任何地方使用
  • 附加到iframe URL的URL参数/修饰符字符串(例如
    mods = "logo=false"
    ),如果它们不再被引用(检查SSO端点——如果重定向逻辑已移除,这些字符串现在可能已无用)
  • 从SSO端点移除的重定向相关代码(例如
    /auth/sso
    的URL构造、
    return_to
    参数处理)——这已作为步骤3c的一部分处理
  • 构建Metabase iframe URL的辅助函数,如果它们不再被调用
  • 不要移除:SSO端点本身、JWT签名函数、环境变量读取代码,或应用其他部分使用的任何代码

3f: Metabase admin configuration notes (manual steps for the user)

3f:Metabase管理配置说明(用户需执行的手动步骤)

List these as part of the plan — they will be included in the final summary:
  1. Enable modular embedding: Admin > Embedding > toggle "Enable modular embedding"
  2. Configure CORS origins: Admin > Embedding > Modular embedding > add the host app's domain (e.g.,
    http://localhost:9090
    )
  3. Configure JWT Identity Provider URI: Admin > Authentication > JWT > set to the full URL of the SSO endpoint (e.g.,
    http://localhost:9090/sso/metabase
    ). Check the fetched docs to determine whether this is required or optional for the confirmed version (it depends on whether
    window.metabaseConfig
    supports a JWT provider field).
  4. JWT shared secret: No change needed — reuse the existing shared secret from Full App embedding setup
将这些步骤作为计划的一部分列出——它们将包含在最终总结中:
  1. 启用模块化嵌入:管理 > 嵌入 > 切换“启用模块化嵌入”
  2. 配置CORS源:管理 > 嵌入 > 模块化嵌入 > 添加宿主应用的域名(例如
    http://localhost:9090
  3. 配置JWT身份提供者URI:管理 > 认证 > JWT > 设置为SSO端点的完整URL(例如
    http://localhost:9090/sso/metabase
    )。查阅获取的文档,确定此步骤对于确认版本是必需还是可选(这取决于
    window.metabaseConfig
    是否支持JWT提供者字段)。
  4. JWT共享密钥:无需更改——重用全应用嵌入设置中现有的共享密钥

Step 4: Apply code changes (only after Step 3 ✅)

步骤4:应用代码变更(仅在步骤3 ✅完成后执行)

Apply all changes from Step 3 in this order (backend changes first to minimize the window where things are broken):
  1. First: Modify the SSO endpoint to return JSON (Step 3c) — this is backend-only
  2. Second: Add
    window.metabaseConfig
    assignment and embed.js script tag to the layout/head file (Step 3b + 3a, config before embed.js)
  3. Third: Replace each iframe with its web component (Step 3d), one file at a time
  4. Fourth: Remove dead code (Step 3e)
Constraints:
  • Use the Edit tool with precise
    old_string
    /
    new_string
    for every change
  • Do not add new package dependencies — modular embedding requires only the embed.js script served by the Metabase instance
  • Do not change environment variable names
  • If a file requires multiple edits, apply them top-to-bottom to avoid offset issues
按以下顺序应用步骤3中的所有变更(先进行后端变更,以减少系统故障的时间窗口):
  1. 首先:修改SSO端点以返回JSON(步骤3c)——这仅涉及后端
  2. 其次:在布局/头部文件中添加
    window.metabaseConfig
    赋值和embed.js脚本标签(步骤3b + 3a,配置在embed.js之前)
  3. 第三:将每个iframe替换为对应的Web组件(步骤3d),逐个文件进行
  4. 第四:移除无用代码(步骤3e)
限制条件
  • 使用Edit工具,为每个变更指定精确的
    old_string
    /
    new_string
  • 不要添加新的包依赖——模块化嵌入仅需要Metabase实例提供的embed.js脚本
  • 不要更改环境变量名称
  • 如果一个文件需要多次编辑,从上到下应用,避免偏移问题

Step 5: Validate changes (only after Step 4 ✅)

步骤5:验证变更(仅在步骤4 ✅完成后执行)

Perform all of these checks. Checks 5a–5c can run in parallel (all are independent grep searches). Check 5d and 5e require reading specific files. Each check should have an explicit pass/fail result.
执行所有这些检查。检查5a–5c可并行运行(均为独立的grep搜索)。检查5d和5e需要读取特定文件。每个检查应具有明确的通过/失败结果。

5a: No remaining Metabase iframes

5a:无剩余Metabase iframe

Use Grep to search for
<iframe
and
iframe
across all project files (excluding
node_modules
,
.git
, lockfiles). Verify that no Full App / Interactive Embedding iframes pointing to Metabase remain. Non-Metabase iframes should be untouched. Also leave any guest embedding (formerly "static embedding") or public embedding iframes untouched — those use different URL patterns (e.g.,
/embed/
or
/public/
) and are not part of this migration.
Pass criteria: zero Full App / Interactive Embedding iframes found (guest/public embed iframes are excluded).
使用Grep在所有项目文件中搜索
<iframe
iframe
(排除
node_modules
.git
、锁文件)。验证是否没有剩余指向Metabase的全应用/交互式嵌入iframe。非Metabase iframe应保持不变。同时保留任何访客嵌入(以前称为“静态嵌入”)或公共嵌入iframe——这些使用不同的URL模式(例如
/embed/
/public/
),不属于此迁移范围。
通过标准:未找到全应用/交互式嵌入iframe(访客/公共嵌入iframe除外)。

5b: embed.js appears exactly once

5b:embed.js仅出现一次

Use Grep to search for
/app/embed.js
across all project files (excluding
node_modules
,
.git
). This pattern is specific to Metabase's embed script URL and avoids false positives from other tools that may use a generic
embed.js
filename. Pass criteria: exactly one occurrence in the layout/head file.
使用Grep在所有项目文件中搜索
/app/embed.js
(排除
node_modules
.git
)。此模式特定于Metabase的嵌入脚本URL,可避免其他工具可能使用通用
embed.js
文件名导致的误报。 通过标准:在布局/头部文件中恰好出现一次。

5c: window.metabaseConfig is set exactly once

5c:window.metabaseConfig仅设置一次

Use Grep to search for
window.metabaseConfig
across all project files (excluding
node_modules
,
.git
). Pass criteria: exactly one occurrence (the assignment in the layout/head file).
使用Grep在所有项目文件中搜索
window.metabaseConfig
(排除
node_modules
.git
)。 通过标准:恰好出现一次(布局/头部文件中的赋值)。

5d: SSO endpoint returns JSON only

5d:SSO端点仅返回JSON

Read the SSO endpoint file. Verify:
  • The endpoint returns a JSON response with
    { jwt: token }
  • The old redirect logic (
    res.redirect
    ,
    new URL("/auth/sso", ...)
    ,
    return_to
    ) has been fully removed
  • No conditional check for
    response=json
    exists (since JSON is the only response format now)
Pass criteria: endpoint returns JSON only, no redirect fallback remains.
读取SSO端点文件。验证:
  • 端点返回包含
    { jwt: token }
    的JSON响应
  • 旧的重定向逻辑(
    res.redirect
    new URL("/auth/sso", ...)
    return_to
    )已完全移除
  • 不存在针对
    response=json
    的条件检查(因为现在仅返回JSON格式)
通过标准:端点仅返回JSON,无重定向回退。

5e: Spot-check modified files

5e:抽查修改后的文件

Read each modified file and verify:
  • Web components have required attributes (
    dashboard-id
    ,
    question-id
    , or
    initial-collection
    )
  • Template syntax is valid (no unclosed tags, correct expressions)
  • Dead-code variables identified in Step 3e have been removed
Pass criteria: all checks pass.
If any check fails:
  • Fix the issue immediately
  • Re-run the specific check
  • If unable to fix after 3 attempts, mark Step 5 ❌ blocked and report which check failed and why
读取每个修改后的文件并验证:
  • Web组件具有必填属性(
    dashboard-id
    question-id
    initial-collection
  • 模板语法有效(无未闭合标签、表达式正确)
  • 步骤3e中识别的无用代码变量已被移除
通过标准:所有检查通过。
如果任何检查失败:
  • 立即修复问题
  • 重新运行特定检查
  • 如果尝试3次仍无法修复,标记步骤5 ❌ 受阻并报告哪个检查失败及原因

Step 6: Output summary

步骤6:输出总结

Organize the final output into these sections:
  1. Changes applied: list every file modified and a one-line description of each change
  2. Web component mapping: table showing each old iframe → new web component
  3. Dropped parameters: list of Full App iframe parameters that were dropped, with brief explanation of why they don't apply to modular embedding
  4. Theme configuration: any theme/appearance settings mapped into
    window.metabaseConfig
  5. Manual steps required (Metabase admin configuration from Step 3f):
    • Enable modular embedding
    • Configure CORS origins
    • Configure JWT Identity Provider URI
    • Any other admin steps identified
  6. Behavioral differences the user should be aware of:
    • Users can no longer navigate between dashboards/questions/collections within a single embed (each web component is standalone)
    • The Metabase application shell (nav, sidebar, search) is no longer present
    • Any iframe parameters that could not be mapped
将最终输出整理为以下部分:
  1. 已应用的变更:列出每个修改的文件及每个变更的单行描述
  2. Web组件映射:显示每个旧iframe → 新Web组件的表格
  3. 已丢弃的参数:列出全应用iframe中已丢弃的参数,并简要说明它们不适用于模块化嵌入的原因
  4. 主题配置:映射到
    window.metabaseConfig
    的任何主题/外观设置
  5. 需执行的手动步骤(来自步骤3f的Metabase管理配置):
    • 启用模块化嵌入
    • 配置CORS源
    • 配置JWT身份提供者URI
    • 识别到的其他管理步骤
  6. 用户应注意的行为差异
    • 用户无法再在单个嵌入中导航不同的仪表板/问题/集合(每个Web组件都是独立的)
    • Metabase应用框架(导航、侧边栏、搜索)不再存在
    • 任何无法映射的iframe参数

Retry policy

重试策略

Doc fetching:
  • If fetching
    llms-embedding-full.txt
    returns 404, verify the Metabase version number and retry. If still failing, mark Step 1 ❌ blocked.
Validation:
  • If any validation check in Step 5 fails after 3 fix attempts, mark Step 5 ❌ blocked and report which check failed and why.
  • If AskUserQuestion is not answered, remain blocked on that step — do not guess or proceed with assumptions.
文档获取
  • 如果获取
    llms-embedding-full.txt
    返回404,验证Metabase版本号并重试。如果仍然失败,标记步骤1 ❌ 受阻。
验证
  • 如果步骤5中的任何验证检查在3次修复尝试后仍失败,标记步骤5 ❌ 受阻并报告哪个检查失败及原因。
  • 如果AskUserQuestion未得到回答,保持该步骤受阻——不要猜测或基于假设继续操作。