Skip to main content

metabase-data-app-setup

Scaffold a new Metabase data-app into the connected remote-sync repository's `data_apps/<app>/` directory from the `data-app-template`. Use when the user asks to start, create, scaffold, or set up a data-app from scratch.

الانتقال إلى التثبيت

معلومات المصدر

المستودع
metabase/metabase
آخر نشاط في المصدر
١٨ سبتمبر ٢٠٢٦ في ١٤:٥٧
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٤٩٬٣٤٤
التفرعات
٦٬٨٣٣

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

مستكشف الملفات
14 ملفات

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
metabase-data-app-setup
description
Scaffold a new Metabase data-app into the connected remote-sync repository's `data_apps/<app>/` directory from the `data-app-template`. Use when the user asks to start, create, scaffold, or set up a data-app from scratch.
# Create a Metabase Data App A Metabase **data-app** is a single JS bundle that the host loads inside a Near Membrane sandbox and renders inside its own React tree. The scaffold is a Vite + React + TypeScript project: source under `src/`, a dev server that previews the app against a real Metabase **through the same Near Membrane sandbox + distortion rules Metabase uses in production** — so `npm run dev` behaves like production, including for third-party libraries the app bundles — and `npm run build` producing a single `dist/index.js`. (Because the sandbox runs a built bundle, a change rebuilds it and does a *soft reload* — re-evaluates the bundle in the sandbox and remounts the app, keeping auth/SDK loaded — rather than hot-swapping modules; component state resets, but there's no full browser refresh.) The dev preview also shows a corner **⚠ Diagnostics** toolbar that captures runtime errors — including the sandbox's otherwise-opaque blocked-API messages — so failures surface instead of being swallowed. The same data is served as JSON at `http://localhost:5174/__data-app/diagnostics`, which is how *you* read it (see "Reading the diagnostics feed" below) — you have a shell, not a browser, and these failures are invisible from the terminal otherwise. **Data apps are served from Git, not uploaded.** A single repository is connected to Metabase via remote-sync (Admin → Settings → Remote sync). Each app lives in its own directory `data_apps/<app>/` inside that repo — its source, a `data_app.yaml` (name/path), and the committed built bundle at the `path` its `data_app.yaml` declares (`dist/index.js` by default). On each remote-sync import Metabase materializes one app per directory and serves it at `/apps/<slug>` url, where the slug **is** the directory's name. So this skill always scaffolds **into the connected repo's `data_apps/<app>/` directory**, never as a standalone project. **The scaffold ships inside this skill at `./template/`** — a Vite + React + TypeScript project that was installed alongside the skill. Step 3 just copies it into the app directory; the skill then guides you through the customization + first-app-content steps — it never generates project files from scratch. If you find yourself writing `package.json`, `vite.config.ts`, `tsconfig.json`, or `src/index.tsx` by hand, stop — copy the template instead. ## When to invoke this skill - "scaffold a new data app" / "create a Metabase data app" / "set up a data-app project" - "I want to build a data app" / any vague intent to author a data app - Starting a fresh agent task that will produce a data-app bundle. - Do **not** use this skill for an existing data-app project when the task is to build screens, use Metabase data, generate or refresh schema files, wire saved questions / tables / metrics / actions, add filters, or author data hooks. Treat those as existing-data-app editing tasks and use the agent's normal skill-discovery flow from the user's wording. ## Step 1 — Locate the remote-sync repository Data apps live inside the Git repository connected to Metabase via remote-sync. Find it before scaffolding: - Ask: **"Do you already have a Git repository connected to this Metabase via remote-sync?"** - **Yes** → ask for its local path. - **No** → ask the user to **create one** (a plain Git repo they will connect under Admin → Settings → Remote sync) and share its path. The skill does **not** create or connect the repo — the user owns that. - Verify the path exists and is a Git working tree (it has a `.git`). This repo is the working directory for every step below. ## Step 2 — Name the app and create its directory 1. Settle on the app's **directory name** before scaffolding — it is used verbatim as the slug (the `/apps/<slug>` URL), so it **must be dash-cased**: lowercase letters, numbers, and single dashes (`[a-z0-9]+(?:-[a-z0-9]+)*`), e.g. `sales-overview`. Anything else (uppercase, spaces, underscores) is rejected on sync. If the purpose isn't clear yet, ask a one-line "what's this app for?" and propose a name; confirm it. 2. Ensure `<repo>/data_apps/` exists; create it if missing. 3. Create `<repo>/data_apps/<slug>/`. **If it already exists**, treat it as an existing project (see below) — never overwrite without confirmation. ### Detecting an existing app If `<repo>/data_apps/<slug>/` already holds a project, verify it matches the current `data-app-template`. Check **all** of: 1. `vite.config.ts` is a one-liner: `export default dataAppConfig()` (from `@metabase/embedding-sdk-react/data-app-dev/config`). There is **no** local `config/` directory: the whole bundle contract (externals/globals, the dev sandbox entry, CSS/SVG handling) lives inside that SDK config, not the scaffold. `dataAppConfig` takes only a curated set of overrides (currently just `port`); the contract plugin is always applied and can't be overridden. 2. `src/index.tsx` default-exports a `DataAppFactory` (type from `@metabase/embedding-sdk-react/data-app`) returning `{ component, providerProps? }` (no args). **All checks pass** → template-shaped. Ask: "Extend this app, or scaffold a new one under a different slug?" If extend → skip the copy step, edit `src/`. If new → pick a different slug and restart at Step 2. **Any check fails** → not template-shaped (older scaffold or drift). **Stop.** Tell the user the structure differs from the current template, extending it risks breaking the bundle contract, and ask whether to (1) migrate it, (2) scaffold fresh under a new slug and port the code over, or (3) proceed anyway at their risk. Wait for the answer. Never overwrite existing files without explicit confirmation. ## Step 3 — Copy the template into the app directory The template ships **inside this skill** at `./template/` (installed alongside the skill via `skills add metabase/metabase/skills#release-x.<major>.x`). Copy it into the app directory: ```bash APP_DIR="<repo>/data_apps/<slug>" # `<skill-dir>` = the directory this SKILL.md was loaded from # (e.g. `.claude/skills/metabase-data-app-setup`); the template is its `template/` subfolder. cp -R "<skill-dir>/template/." "$APP_DIR/" ``` A data app is a *subdirectory* of the remote-sync repo, not its own repository — so this is a plain copy, never a nested `git clone` / `git init`. Everything below runs **inside `$APP_DIR`**. The copy includes two root-level directories, `queries/` and `actions/`, each holding only a `README.md`. Keep both, even while empty: every query the app runs is a `defineQuery(...)` export in `queries/`, every action a `defineAction(...)` export in `actions/`, and the hooks refuse anything else at compile time. Read both READMEs before writing the first `useMetabaseQuery` / `useMetabaseQueryObject` / `useAction` call. ## Step 4 — Customize Once the template is in `<repo>/data_apps/<slug>/` (run everything below from that directory): 1. Edit `package.json` `name` to match the slug. 2. Pin `@metabase/embedding-sdk-react` to the published data-apps tag (the template ships with `*`): ```bash npm install @metabase/embedding-sdk-react@64-alpha ``` This resolves to the current internal-testing SDK build with the `@metabase/embedding-sdk-react/data-app` entrypoint (the app's APIs) and the `@metabase/embedding-sdk-react/data-app-dev/config` entrypoint `vite.config.ts` uses (the dev/build preset, which serves the sandbox entry). Do not use `latest` or `64-stable` as data apps are not yet released. 3. **Ensure the repo-root `.gitignore` ignores `.env.local`** — do this *before* creating any credentials file so the secret can never be committed. Create the `.gitignore` if the repo doesn't have one, then add the entry if it's missing: ```bash ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" if [ -z "$ROOT" ]; then echo "MISSING (run this from inside the connected git repo)" else GITIGNORE="$ROOT/.gitignore" # Create the repo-root .gitignore if absent, then ensure `.env.local` is # ignored so the credentials file (next step) can never be committed. [ -f "$GITIGNORE" ] || : > "$GITIGNORE" grep -qxF ".env.local" "$GITIGNORE" || echo ".env.local" >> "$GITIGNORE" fi ``` 4. Set up the Metabase credentials at the **repository root** — `<repo>/.env.local` (usually two levels up from the app dir), **not** the app dir. One `.env.local` there serves every data app in the repo. Create it from the example if absent, then verify the two vars are set **without printing the file** (it may hold other secrets) — `source` it and echo only a pass/fail signal, never the values: ```bash # Resolve the repo root first; an unguarded $(git ...) would expand to # "/.env.local" outside a repo and touch a system-level file. ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" if [ -z "$ROOT" ]; then echo "MISSING (run this from inside the connected git repo)" else ENV_FILE="$ROOT/.env.local" [ -f "$ENV_FILE" ] || cp .env.local.example "$ENV_FILE" # Source inside a subshell so the vars never leak into your environment. ( source "$ENV_FILE" 2>/dev/null [ -n "$DATA_APP_MB_URL" ] && [ "$DATA_APP_MB_URL" != "mb_replace_me" ] && [ -n "$DATA_APP_MB_API_KEY" ] && [ "$DATA_APP_MB_API_KEY" != "mb_replace_me" ] ) && echo "creds present" || echo "MISSING" fi ``` If it prints `MISSING`, **ask the user to fill `DATA_APP_MB_URL` (the running Metabase instance) and `DATA_APP_MB_API_KEY` (Admin → Authentication → API keys) in `<repo>/.env.local` themselves** — up front, before anything needs the key. > **Never ask the user to paste the API key into the chat, and never `cat` / `echo` / print `.env.local` or its variables.** It's git-ignored and may hold *other* secrets — the file's contents and the key must never enter the conversation or your context. Every command that needs the key `source`s the file (as above) so the shell uses the value directly; you only ever see the `creds present` / `MISSING` signal, never the secret itself. (`creds present` only means both vars are filled and not the default `mb_replace_me` placeholder — not that the URL or key are valid; a bad key surfaces later when a request fails.) 5. `npm install` (or whichever package manager the user prefers — the template ships with no lockfile, so `npm` / `yarn` / `pnpm` / `bun` all work; use the project's existing lockfile if one appears post-clone). 6. **Fix the app's `.gitignore` so the lockfile *and* the built bundle get committed.** Two things must end up tracked in the remote-sync repo: - **Lockfile** — strip the lockfile-ignoring block (the chunk between `# Lockfiles —` and `bun.lockb`, covering `package-lock.json` / `yarn.lock` / `pnpm-lock.yaml` / `bun.lock` / `bun.lockb`) so the project commits its lockfile for reproducible installs. - **The built bundle** — Metabase serves the file at the `path` declared in `data_app.yaml` (the template builds to `dist/index.js`, the default `path`) straight from the committed Git tree, so **that file must be committed**. If the template's `.gitignore` ignores `dist/` (or wherever your build outputs), remove that line. **Verify with `git status`** — after `npm install` + a build, both the generated lockfile and the built bundle (the file `path` points at) must appear as untracked/committable files. If either doesn't, the relevant `.gitignore` line is still there; remove it and re-check. Do **not** skip this — agents have repeatedly shipped projects with no committed lockfile or an un-synced bundle. 7. `npm run dev` and confirm the preview at http://localhost:5174 renders the starter "Hello, data app" message. 8. If the preview hits CORS, add `http://localhost:5174` under Admin → Embedding → Embedded analytics SDK → CORS. 9. **Edit `data_app.yaml`** (it ships with the template, in the app directory). This is the per-app config Metabase reads on sync — one file per app. Fill in its fields for this app: ```yaml name: Sales App # display name shown in the admin UI description: Pipeline health and quota attainment by region # optional — see below version: 1 # data app contract version — leave as-is (see below) path: ./dist/index.js # bundle path, relative to this app's directory — leave as-is unless you change the build output # allowed_hosts: # optional — external origins the app may fetch/XHR (see below) # - https://api.example.com # - https://*.internal.acme.com ``` Commit it alongside the built bundle (the file `path` points at). **`description`** — optional: a single short sentence saying what the app does, shown under its name in the admin UI so admins can tell apps apart at a glance. Sync folds any whitespace into single spaces and rejects anything over 255 characters; the admin list wraps what is left rather than cutting it off, so a sentence reads well there and a paragraph crowds out the rows around it. Replace the template's placeholder with a real sentence about *this* app, or delete the line entirely if it adds nothing beyond the name. **`version`** — the data app contract version this app's code targets, a whole number. The template ships the version this skill targets; do not change it by hand. Metabase bumps the version it serves only on a breaking change to the contract. An app on an older version is marked *Outdated* in the admin list, hidden from every other user, and refuses to open until it is migrated to the current contract, its `version` raised to match, rebuilt, and synced. Migrating an app from one version to the next is a separate, instructed procedure; never do it ad hoc. **`allowed_hosts`** — only needed if the app calls an **external** API directly with `fetch`/`XHR`. The sandbox blocks all network egress by default; listing an origin here (exact or a `*.` subdomain wildcard) opens it in both `npm run dev` (dev-server CSP) and Metabase (iframe CSP + sandbox). Do **not** list the Metabase instance — Metabase data is read via the `useMetabaseQuery` data hooks and written via `useAction` (the SDK handles auth), never raw `fetch`. Leave `allowed_hosts` out entirely when the app only talks to Metabase. Native `<form action="…">` submissions and `<iframe src="…">`/navigations obey the **same** allowlist. Prefer a client-side `<form onSubmit>` that `preventDefault`s and writes via `useAction`/`fetch`: a native submit *navigates the sandboxed iframe away* from the app. If you do use one, the target host must be in `allowed_hosts` or it's blocked (`form-action` for submits, `frame-src` for embeds/navigations). A host you navigate to or embed must also permit framing (`X-Frame-Options`/`frame-ancestors`) — many public sites don't. ## Step 5 — Verify the starter app At this point the data app exists. Keep this workflow focused on creating the project scaffold and proving the starter bundle works. 1. Run `npm run typecheck`. 2. Run `npm run build`. 3. Confirm `git status` shows the app source, lockfile, `data_app.yaml`, and the built bundle (`dist/index.js` by default) as committable files. 4. If the user asked for a live preview, run `npm run dev` and confirm the starter "Hello, data app" screen renders through the sandbox preview. 5. With the preview open, check the diagnostics feed once — it is the only place runtime failures appear to you (see *Reading the diagnostics feed*): ```bash curl -s "http://localhost:5174/__data-app/diagnostics?startEventId=0" ``` Expect `clients: 1` and no entries with `"alert": true`. `clients: 0` means no preview tab is open, so an empty feed proves nothing. Stop here if the user only asked to create, scaffold, or set up a data app. If the next task is to build or iterate on the actual app UI — especially if it mentions an existing data app, Metabase data, generated schema files, saved questions, tables, metrics, actions, filters, semantic-layer entities, or data hooks — treat it as a separate existing-data-app editing task. Use the agent's normal skill-discovery flow from those terms. Do not expand this scaffold skill with data-layer authoring rules. **Do not modify `src/index.tsx` or `tsconfig.json` unless the change is genuinely required.** The whole build/dev setup lives in the SDK behind `dataAppConfig()` (which also serves the dev HTML shell — there's no `index.html` to edit), so `vite.config.ts` is just: ```ts import { dataAppConfig } from "@metabase/embedding-sdk-react/data-app-dev/config"; export default dataAppConfig(); ``` `dataAppConfig` exposes only a curated set of overrides (currently just `port`). The whole contract — factory shape, externals/globals, the dev sandbox entry, CSS inlining, and SVG-as-component support — is baked in and **can't** be overridden; that's deliberate, so a data app can't drift from what Metabase loads. There's no local build config to touch (and no `index.html` — the SDK's dev server serves the HTML shell), and tweaks to `src/index.tsx` still risk breaking the factory shape and silently break drill popups and routing. There is intentionally **no escape hatch** for extra Vite plugins, aliases, or `define`s — `port` is the only knob. If you think you need more, you almost certainly don't; solve it in `src/` instead. **After every meaningful round of edits, run `npm run typecheck`.** It runs `tsc --noEmit` over `src/` and `vite.config.ts` — catches wrong prop shapes against the SDK types, broken refactors, missing imports, etc. The Vite dev server does NOT typecheck (it only transpiles), so errors that would fail a production CI run can sit invisibly in a passing `npm run dev` session. Run it before declaring a task complete. **Before handoff, re-check package hygiene.** `@metabase/embedding-sdk-react` should use the expected data-app SDK source/tag for the target environment. No date picker dependency should be installed when the app only needs date ranges — not `react-datepicker`, `react-day-picker`, `flatpickr`, or a UI suite's picker (`@mui/x-date-pickers`, `antd`, `rsuite`, …); that is `DateRangePopover` from `@metabase/embedding-sdk-react/data-app`. `@types/react-datepicker` should not be installed unless the chosen `react-datepicker` version actually needs it.
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub