SuperAPI AI Directory

metabase

Data & analytics

View repository

Updated 10-17 01:46

About this skill

Metabase Skill

End-to-end BI development tool for Metabase: design dashboards, write and test SQL, keep dashboards as files, publish them, look at the result, and share or schedule it.

All scripts are in ${CLAUDE_SKILL_DIR}/scripts/ and need only Node.js (no npm install; rendering needs Node 22+ and Google Chrome). Every command below is node ${CLAUDE_SKILL_DIR}/scripts/metabase.mjs <command>.

First-Time Setup

Run instances to see which Metabase connections exist (it never prints keys or tokens). Never read ~/.metabase-skill/config.json or anything under ~/.metabase-skill/tokens/ — they can hold secrets.

  • Instances listed: use them (ask which one when there are several). status shows who you're signed in as, how, the Metabase version, and whether it's the free edition.
  • None configured: ask the user to sign in, in their terminal (it opens a browser; you can't do it for them):
node ${CLAUDE_SKILL_DIR}/scripts/metabase.mjs login --instance <name> --url https://metabase.example.com

Browser sign-in (Metabase v63+) uses the person's own Metabase account and permissions; tokens go to the macOS Keychain (a private file elsewhere) and renew automatically. API keys still work for scripts and CI: setup → env or direct mode. If login says app registration is turned off, an admin disabled Admin > AI > MCP (which also turns off automatic registration): re-enable it, or register an app and pass --client-id.

Quick Reference

Global options (every command):

  • --instance <name> — use a connection other than the default
  • --json — JSON output (list/discovery commands print text tables without it; get/create/update always print JSON)
  • --fields <a,b.c> — only these dot-paths of the JSON result (per item on lists), e.g. card 402 --fields name,dataset_query.stages.0.native
  • --max-bytes <n> — cap printed JSON (default 24576, 0 = none). Lists are trimmed with a notice; an oversized object is refused (exit 2) — narrow it with --fields or use --out
  • --skip-preflight — try a Pro/Enterprise-only command on an instance without the feature
  • --theme light|dark — colour scheme for Chrome-rendered pictures (default light; Metabase otherwise follows the OS)

Exit codes: 0 ok, 1 operation failed, 2 input/pre-flight problem (fix the input; the message says what).

Sign-in

Command What it does
login --instance <n> --url <u> [--client-id <id>] Browser sign-in (v63+); creates the connection
logout [--instance <n>] Revoke and delete the stored sign-in
status [--instance <n>] User, auth method, token expiry, Metabase version, edition
setup / instances Wizard (browser or API key) / list connections

Projects — a dashboard as YAML + SQL files (see specs/project-format.md)

Command What it does
project new <dir> --name <n> --database <id> [--collection <id>] Empty project folder for a new dashboard
project pull <dir> [--dashboard <id> [--loose-cards] | --collection <id>] [--force] Metabase → files (first pull needs --dashboard or --collection; --loose-cards also keeps the collection's cards that are on no dashboard; refuses to overwrite local edits)
project validate <dir> [--json] Structure, references, 24-column grid, filter wiring, SQL tags, chart settings
project lint <dir> [--strict] [--offline] Design checks (descriptions, number formats, full rows, unwired filters, …)
project diff <dir> [--json] Exactly what push would change, field by field and placement by placement
project status <dir>... [--json] Every project at or under the folders: in sync, changed in Metabase (pull), or not pushed yet (push). Read-only
project push <dir> [--dry-run] [--yes] Files → Metabase (snippets, cards, dashboard, subscriptions); warns on drift
project test <dir> [--card <key>] [--images] Run every card (as in the files) with the dashboard's default filters; --images → PNGs
project render <dir> [--pdf] [--tab <name>] Picture / PDF of the published dashboard → <dir>/.mbsync/render/

Pictures and PDFs (see specs/rendering.md)

Command What it does
card image <id> [--out f.png|f.pdf] [--params <json>] [--width N --height N] The saved question's chart (card-level settings, no dashboard filters)
dashcard image <dashboard-id> <index|name> [--id] [--out ..] [--params <json>] One tile as it looks on the dashboard: its own title, value, formatting, and the dashboard's filters
dashboard render <id> [--out f.png|f.pdf] [--tab <name|id>] [--params <json>] Every tab (PNG each, or one PDF); reports cards showing errors
dashboard render <id> --server [--out f.pdf] [--paper a4|letter] PDF drawn by Metabase itself (v63+, subscription style); no Chrome needed

Discovery

Command What it does
databases / tables --database <id> Databases / tables and fields
collections [--tree] / collection-items <id> [--models card,dashboard] Collections (★ = official) / contents
search <query> [--models ...] Find anything by name

Cards (questions)

Command What it does
card <id> Summary: result columns, snippets, template tags
card <id> --full --out <file> / --layout --out <file> Full payload / writable fields, to a file
card create --from <file> [--skip-validate] Create (checked first: tags, field filters, compile, chart settings)
card update <id> --patch <file> [--skip-validate] Fetch → merge → check → save
card delete <id> [--permanent --yes] / card restore <id> Archive (restorable) / hard delete / un-archive
card copy <id> [--collection <id>] Duplicate
card query <id> [--params '{"tag": value}'] [--out <file>] Run it (20 rows, or all rows to a file)
card query <id> --export csv|json|xlsx --out <file> [--params ..] Download results
card history <id> / card revert <id> --to <revision-id> [--yes] Who changed what / restore a version
pivot build --table <id> --rows <f[:unit],..> [--columns ..] --values <count|sum:F,..> (--out f.json | --project <dir> --key <k>) Write a correct structured pivot question (run once to check it)

Dashboards

Command What it does
dashboard <id> Summary: tabs, filters, placements, repeated_card_ids, per-placement overrides
dashboard <id> --layout --out <file> / --full --out <file> Layout only (~3K tokens for 30 cards) / full payload
dashboard create --from <file> Create a shell
dashboard put <id> --from <file> [--skip-validate] Save a whole layout (every referenced card is checked first)
dashboard update <id> --patch <file> Fetch → merge → save
dashcard <dashboard-id> <index|name> [--id] One placement's settings and filter wiring
dashcard update <dashboard-id> <index|name> [--id] --patch <file> Change one placement; the rest untouched
dashboard delete <id> [--permanent --yes] / dashboard restore <id> Archive / hard delete / un-archive
dashboard copy <id> [--collection <id>] Duplicate
dashboard history <id> / dashboard revert <id> --to <revision-id> Who changed what / restore a version

Alerts and subscriptions (see specs/notifications-api-spec.md)

Command What it does
alerts [--card <id>] / alert <id> List / show card alerts
alert create --from <file> / alert update <id> --patch <file> Alert on a card (condition + Quartz cron + recipients)
alert archive <id> / alert restore <id> / alert send <id> [--yes] Stop / resume / deliver now to real recipients
subscriptions [--dashboard <id>] / subscription <id> List / show dashboard subscriptions
subscription create --from <file> / subscription update <id> --patch <file> Email/Slack a dashboard on a schedule
subscription archive <id> / subscription restore <id> Stop / resume

Snippets, collections, metadata

Command What it does
snippets / snippet <id> List / show SQL
snippet create --name <n> --content <sql> [--collection <id>] [--description <d>] Create
snippet update <id> [--name] [--content] [--description] [--archived true|false] Update or archive (no hard delete)
collection create --name <n> [--parent <id>] [--description <d>] Create
collection update <id> [--name] [--parent] [--description] [--archived true|false] Rename, move, archive
database sync-schema <id> / field rescan-values <id> / field discard-values <id> Pick up new columns / refresh dropdown values

Users, groups, permissions

users [--query --status --admins --group <ids> --groups-all <ids>], user <id>|create|update|deactivate, groups, group <id>|create|delete|add-user|remove-user, permissions [--database] [--group] [--native-sql] [--collections], permissions summary, permissions set … [--dry-run] [--yes], permissions set-collection ….

Pro/Enterprise only (refused early on the free edition): usage-analytics …, sandboxes, sandbox create|delete, permissions app [set], permissions set-snippets, collection … --authority-level official.

--help prints every command and flag.

Context Safety Rules

Not optional:

  • Never load full dashboard/card payloads into context. Use the summaries (card <id>, dashboard <id>), --fields for one part, or --full --out <file>.
  • Never print or read secrets. API keys and tokens never appear in output; don't read config.json or tokens/.
  • Use files for anything you send. --from / --patch take file paths, in the project directory — never /tmp.
  • Look at pictures, not payloads, to judge a chart. card image / dashboard render + Read the PNG.

When to Ask the User

You are the builder; the user decides. Ask (AskUserQuestion) when choosing: the instance; the dashboard's purpose and audience; metrics and dimensions; layout (tabs, arrangement, chart types); filters (which, defaults, cascading); SQL vs MBQL when unclear; visual choices with several valid options; before creating or changing anything in Metabase (show the plan or project diff); before any delivery to real recipients (alert send, new subscriptions — confirm addresses); and whenever something fails unexpectedly (report it; don't silently retry).

Task Identification

Mode When Workflow
NEW Build a dashboard Design → project files → validate/lint/test → push → visual check
EDIT (project) Change a dashboard kept as a project folder pull → edit files → diff → push → visual check
EDIT (direct) Quick change to a dashboard not kept as files STUDY → change via card/dashcard commands → visual check
STUDY Understand a dashboard (describe, audit, plan changes) Summary → render → per-card inspection → override audit
SHARE & SCHEDULE PDF/PNG for someone, alerts, subscriptions render / alert / subscription
EXPLORE / REORGANIZE Browse, search, move, rename Discovery and collection commands
HISTORY "Who changed this", "put it back" history → confirm → revert
USAGE-ANALYTICS Adoption, stale content, query performance (Pro/Enterprise) MBQL against analytics models
ADMIN Users, groups, permissions Permission commands

A dashboard that already has a project folder (it has project.yaml and ids.json) is always edited through the project: direct edits in Metabase make the files stale (push would warn about drift).

Reference Loading Strategy

After choosing the mode, read every spec in its bundle before starting. Deferred reads lead to broken payloads.

Mode Specs (${CLAUDE_SKILL_DIR}/specs/…)
NEW / EDIT / STUDY project-format.md, rendering.md, dashboard-api-spec.md, card-api-spec.md, visualization-cookbook.md, sql-style-guide.md, snippet-api-spec.md, discovery-api-spec.md
SHARE & SCHEDULE rendering.md, notifications-api-spec.md
EXPLORE / REORGANIZE discovery-api-spec.md, collection-api-spec.md, card-api-spec.md
Standalone SQL / card / snippet card-api-spec.md, sql-style-guide.md, snippet-api-spec.md, visualization-cookbook.md
USAGE-ANALYTICS usage-analytics-spec.md
ADMIN permissions-api-spec.md, permissions-guide.md

Assets (${CLAUDE_SKILL_DIR}/assets/templates/): mockup-text-template.md, mockup-html-template.html, design-doc.md — load at the design stage that needs them.

Spec Content
project-format.md Project folder format, what's derived, push/pull behaviour, lint rules
rendering.md How card/dashboard pictures and PDFs are made, options, output, limits
notifications-api-spec.md Alerts vs subscriptions, bodies, schedules, traps
dashboard-api-spec.md Dashboard CRUD, layout, parameters, mapping targets, PUT rules
card-api-spec.md Card CRUD, MBQL, native SQL, template tags, what v63 returns
visualization-cookbook.md Every display's required settings and sizes; copy-pasteable examples
sql-style-guide.md SQL conventions, sample-database dialects
snippet-api-spec.md / collection-api-spec.md / discovery-api-spec.md Snippets / collections / metadata and search
permissions-api-spec.md / permissions-guide.md Users, groups, permission graph / recipes and safety
usage-analytics-spec.md Analytics models, MBQL patterns (Enterprise)

NEW Dashboard Workflow

Before you start

Ask: design first (recommended for 5+ cards, tabs or complex filters) or build directly (simple requests)? Then agree the project folder (e.g. <repo>/dwh/metabase/<dashboard>/); never /tmp.

Phase 1: Design — Define → Mockup → Approve

Get explicit approval at each stage; never skip one silently.

Stage 1: requirements. The business question, the audience (exec: tiles and trends; analyst: tables and drill-downs), the data (databases, tables --database <id>). Then define and present for approval:

Metric Calculation Display Card
Total Revenue SUM(total) scalar Revenue KPI
Dimension Source Used in
Month orders.created_at (month) Revenue trend
Filter Type Default Applied to
Date date/all-options past30days~ all cards

Don't continue until the user confirms — a wrong data model makes everything built on it wrong.

Stage 2: progressive mockup (approve each level):

  1. Text mockup (assets/templates/mockup-text-template.md) → <project>/design/mockup.md: tile/chart/table arrangement on the 24-column grid.
  2. HTML mockup (assets/templates/mockup-html-template.html) → <project>/design/mockup.html for the browser.
  3. Metabase mockup: write the project with simple sample SQL (SELECT 'Gadgets' AS "Category", 42000 AS "Revenue" or real queries with LIMIT), project push, then project render <dir> and show the user the PNG — they see the real layout, filters and formatting without clicking through Metabase.

Stage 3: design document (assets/templates/design-doc.md) → <project>/design/design-doc.md.

Phase 2: The project files

project new <dir> --name "Store Analytics" --database 3 --collection 17

Then write (format: specs/project-format.md):

  1. snippets/<name>.sql — one feeder snippet per data source: the joins, the filters ([[AND {{date}}]]), the time grouping ({{time_perspective}} :: DATE AS period on Postgres/Redshift; DATE({{time_perspective}}) on SQLite). Cards wrap it: WITH base AS ({{snippet: name}}) SELECT …. Never put {{…}} in SQL comments (Metabase expands them).
  2. filters.yaml — the template tags the snippets use: field filters (type: dimension, dimension: [field, {base-type: type/Date}, <field id>], alias: table_alias.column, widget-type) and the temporal-unit tag. Shared tags live here once; the example is in specs/project-format.md.
  3. cards/<key>.sql + cards/<key>.yaml — one per card. SQL follows specs/sql-style-guide.md; the .yaml has name, description, display and chart settings. Chart cards always set graph.dimensions, graph.metrics (and graph.x_axis.scale: timeseries for dates) — see the table at the top of visualization-cookbook.md.
  4. dashboard.yaml — filters (parameters), tabs, placements with at: [col, row, width, height], per-tile viz overrides, and filter wiring filters: {p_date: date} on every card that has the tag.

SQL vs MBQL: prefer native SQL for anything reviewed, versioned or reused (CTEs, window functions, custom date logic, snippets); MBQL for simple aggregations and reusable metrics. Structured questions work in projects too (their query: is kept as-is).

Parameterized SQL — two different mechanisms:

  • Basic variables (text, number, date): plain substitution, you write the operator — WHERE status = {{status}}, optional with [[AND category = {{category}}]].
  • Field filters (dimension): Metabase writes the condition — WHERE {{date_range}} [[AND {{category}}]]. Never WHERE col = {{tag}} (the pre-flight rejects it). With table aliases, alias must be alias.column.

Phase 3: Check, publish, look

project validate <dir>           # must be clean
project lint <dir>               # fix, or record deliberate choices under lint.ignore in project.yaml
project test <dir>               # every card runs with the default filters, and chart settings are checked
                                 # against the columns each query really returns — must be all ok
project diff <dir>               # show the user what will be created
project push <dir>               # after the user approves
project lint <dir>               # again: column-based rules (number formats, …) only run once cards exist in Metabase

Then run the visual check (below) — mandatory. Metabase may draw a sensible chart even when a setting is wrong (it falls back to another column), so a good-looking picture doesn't replace a clean project test.

Phase 4: Test and hand off

  1. project test <dir> --images — every card ran; PNG per card and per tab in <dir>/.mbsync/render/.
  2. Spot-check 2–3 key numbers against known values or a direct SQL run.
  3. Filters: render with other values (dashboard render <id> --params '{"category": "Gizmo"}') and confirm cards change.
  4. Edge cases: empty results, nulls, an empty filter.
  5. Hand off with project render <dir> --pdf so the user sees the final result without opening Metabase, and commit the project folder.

The Visual Check (after any card or layout change)

Metabase will happily save a chart that is wrong. Look at it:

  1. After creating or changing a card: for a card on a dashboard, dashcard image <dashboard-id> <tile title> (what viewers see: the tile's own title, displayed value and formatting, with the dashboard's default filters — a card placed three times needs three checks); for a standalone question, card image <id>. Read the PNG. Check:
    • the chart type fits the question; axes use the intended columns
    • labels readable and not overlapping; sensible sort order
    • number formats (currency, %, decimals — no 44,383.9); colours consistent with the dashboard
    • not empty and no error (the JSON's error field); title and description present
  2. Fix and re-render — at most 3 rounds per card. After that, show the user the image and ask.
  3. After any layout change: dashboard render <id> (or project render <dir>) → Read each tab's PNG. Check:
    • rows fill the 24 columns; KPI tiles align and read at a glance
    • headings separate sections; charts aren't cramped
    • the filter bar has every filter; reading order makes sense
    • card_errors in the JSON is empty for every tab
  4. At hand-off, attach a --pdf render.

Rendering shows what Metabase has now — push project edits first.


STUDY Workflow

Run this before claiming you understand a dashboard. JSON-only reading routinely misdescribes dashboards whose cards are placed several times with different settings.

  1. Summary — dashboard <id>: collection_authority_level (official = curated), card_count vs unique_card_count, repeated_card_ids, per-placement overrides (title, scalar_field, graph_metrics, …).
  2. Look at it — dashboard render <id> → Read every tab's PNG. This replaces asking for a screenshot; ask only if rendering fails. card_errors lists broken cards.
  3. Each unique card — card <id> (result columns, snippets, template tags); --full --out only for the SQL.
  4. Each repeated placement — dashcard <dashboard-id> <index|name> for the full settings.
  5. Shared snippets — snippet <id> for every referenced snippet.
  6. Produced vs displayed columns — compare result_columns with each placement's scalar_field / graph_metrics.
  7. Parameters — type, default, and which tags/fields each maps to.
  8. Data shape — card query <id> [--out <file>] on the most complex card.

(If the dashboard is a project folder, project pull then reading dashboard.yaml covers steps 1, 4 and 7.)


EDIT Workflow

Dashboards kept as a project folder

project pull <dir>               # bring in anything changed in Metabase (refuses to overwrite local edits)
# edit the YAML / SQL
project validate <dir> && project lint <dir>
project test <dir> --card <key>  # the cards you touched
project diff <dir>               # show the user
project push <dir>               # then the visual check
  • The files are the source of truth. Someone changed it in the Metabase UI? project pull and commit (ids.json too: it records when each item was synced, and that is what the drift check compares against).
  • If push reports drift ("changed in Metabase since these files were last synced"), stop and ask: keep the Metabase change (pull --force after saving your edits elsewhere) or overwrite it (push --yes).
  • To stop managing a card without archiving it, remove it from cards/ and from ids.json.

Dashboards not kept as files

  1. STUDY first — it catches per-placement overrides you'd otherwise overwrite.
  2. The live card is the source of truth, not any repo .sql file: start from card <id> --full --out.
  3. Layout: dashcard update <dash> <index|name> --patch f.json for one placement; for bigger changes dashboard <id> --layout --out f.json, edit, dashboard put <id> --from f.json.
  4. Card query/settings: card update <id> --patch f.json (checked before saving).
  5. The visual check.
  6. Offer to turn it into a project: project pull <dir> --dashboard <id> (no-op in Metabase; gives files, diff, history in git).

Don't read a repo's old build/deploy scripts to "understand how things fit together" — the CLI is sufficient. Read them only for a convention invisible in the live payload (e.g. a field-id registry) or when asked to change them.


SHARE & SCHEDULE

  • A picture or PDF for someone: dashboard render <id> --server --out <dir>/name.pdf (Metabase's own print layout, A4/letter, active filters listed — the best choice for sharing), dashboard render <id> --out name.pdf (screenshots of the live UI, one page per tab) or card image <id> --out name.png. Filter values: --params (dashboard: filter slugs; card: tag names).
  • Alert (one card, on a condition): write the body per specs/notifications-api-spec.md, confirm recipients, then alert create --from f.json. goal_above/below needs a goal line on the card; otherwise shape the card so has_result expresses the condition. Rehearse with yourself as the only recipient before alert send.
  • Subscription (a dashboard on a schedule): subscription create --from f.json; leave cards out to include every card. In a project, put it under subscriptions: in dashboard.yaml and push.
  • Both warn when email/Slack isn't configured — tell the user an admin must set it up; don't create deliveries that silently go nowhere. Report schedules in the instance's report timezone (printed on create).

HISTORY

card history <id> / dashboard history <id> list revisions (who, when, what). Confirm the target with the user, then card revert <id> --to <revision-id>. For a project folder, project pull afterwards.

EXPLORE Workflow

  1. databases → tables --database <id> → collections --tree (★ = official) → collection-items <id> → search <query> → card <id> / dashboard <id>.
  2. Official collections (authority_level: "official") show a medal badge; promoting one is Pro/Enterprise.

Shortcut: when the intent is clear ("show me Q3 sales by region"), skip discovery and go to SQL.

USAGE-ANALYTICS Workflow (Pro/Enterprise)

Adoption, stale content, query performance, who uses what. Refused early on the free edition.

  1. usage-analytics — models and their card ids (ids vary per instance; never hardcode).
  2. Prefer the 8 built-in analytics dashboards when one answers the question.
  3. Otherwise MBQL (native SQL is blocked on the audit database): write the query to a file, usage-analytics query --from <file>; browsing: usage-analytics query --card <id> --limit N.

Rules: always date-filter View/Query/Activity logs (7–30 days); query_source separates human (dashboard, ad-hoc, question, collection) from automated (pulse, dashboard-subscription, cache-refresh); use the Content model's question_is_native for SQL-vs-MBQL counts; "External User" = embedded views.


Defensive Guardrails

  • Every SQL query must aggregate or LIMIT. No unbounded SELECT * on large tables. Queries over 2 minutes suggest missing date filters on sort keys, pre-aggregation or materialized views.
  • Test SQL before publishing (project test, card query, or a database skill the project uses).
  • Delete means archive. card delete / dashboard delete archive (restore with restore); --permanent --yes only when the user asks for a hard delete. Collections and snippets can only be archived.
  • Pre-flight checks exist for a reason. Fix what they report; use --skip-validate only when you know the check is wrong, and say so.
  • Deliveries reach real people. Confirm recipients before creating alerts/subscriptions or running alert send.

Common Gotchas

Metabase v63 payloads (verified on v0.63.18):

  • Template tags are returned as an array ([{name: …}]) even when written as a map; read either shape.
  • Stored field refs carry random lib/uuid stamps, and structured queries get fresh lib/uuids on every read — not changes. (Project diff/pull already ignore them.)
  • A native card's parameters stay []; set them only for static-list dropdowns.
  • The Sample Database is SQLite on v63 (H2 before): no DATE_TRUNC — strftime('%Y-%m-01', col); check the engine with databases. Metabase types such a month column as a number; keep it in graph.dimensions with graph.x_axis.scale: timeseries.

Projects (config as code):

  • Commit ids.json with every pull and push: its updated_at values are what protects a teammate's push from overwriting changes made in the Metabase UI. A folder with no recorded timestamps makes push warn on every item.
  • One owner per snippet. If cards in two projects use the same snippet, both pulls write it and the last push wins. Keep it in one project and list it under external_snippets in the others.
  • --loose-cards on one project per collection, never on two.
  • Never pull personal collections into a shared repo.
  • Prefer native SQL cards with snippets for anything edited as files; structured (MBQL) questions round-trip exactly but are awkward to edit by hand.

Dashboard PUT and layout:

  • tabs must accompany dashcards; every dashcard needs card_id (null for virtual cards); negative ids create new tabs/placements; omitted placements are deleted.
  • Placement ids change when a layout is re-created — subscriptions point at them. project push reuses existing ids; hand-written layouts should too (GET fresh state first).
  • The 24-column grid: full width is size_x: 24; keep col + size_x ≤ 24; Metabase stores overlaps as sent.
  • Placement visualization_settings override the card's for that placement only; column_settings on a placement replace the card's whole column_settings.

Dashboard summary interpretation:

  • card_count counts placements; unique_card_count distinct cards; repeated_card_ids shows cards placed several times — almost always with different titles/metrics. Run dashcard on each before describing the dashboard.
  • overrides is a curated subset (title, scalar_field, graph_metrics, graph_dimensions, series_titles). For combined visualizations the settings sit under visualization.settings with COLUMN_N names; the summary resolves them.

Filter wiring:

  • A filter only works where it's mapped. Native SQL cards need the template tag defined before a mapping works.
  • Targets: field filters / time grouping ["dimension", ["template-tag", t], {"stage-number": 0}]; plain variables exactly ["variable", ["template-tag", t]] (a third item is rejected); text cards ["text-tag", t].
  • Field refs in mappings need {"base-type": "type/Text"}-style options, not null.
  • Tags used only inside a snippet must still be declared on every card using the snippet (projects do this for you), and nested snippets must all be declared as snippet tags on the card.
  • Field filters can't target derived columns (CASE, computed buckets) — use a basic text variable with a static-list dashboard filter.
  • Relative date defaults: past7days excludes today; past7days~ includes it.
  • A static-list string/= filter needs "values_query_type": "list" to render as a dropdown.
  • Text cards can show filter values: {{d}} in the text + a mapping with target ["text-tag", "d"], card_id: null.

Display types:

  • pivot is structured-query only; native SQL → display: "table" + "table.pivot": true, table.pivot_column, table.cell_column. Build pivots with pivot build (never hand-write the MBQL); card query on a pivot card returns the grid users see and exports are pivoted by default (--flat for rows). Details: cookbook "Pivot Table".
  • Never create a chart card with visualization_settings: {} — set graph.dimensions, graph.metrics, and graph.x_axis.scale: timeseries for dates. Scalars and tables can start empty.
  • Headings: plain text; text cards: markdown. Heading recipe: {"dashcard.background": false, "virtual_card": {"name": null, "display": "heading", "visualization_settings": {}, "archived": false}, "text": "Section"}.

SQL:

  • pMBQL only for native cards: {"lib/type": "mbql/query", "database": <id>, "stages": [{"lib/type": "mbql.stage/native", "native": "<SQL>", "template-tags": {...}}]} — the legacy format silently drops the database.
  • Read SQL from dataset_query.stages[0].native, tags from stages[0].template-tags.
  • Column aliases are human-readable and quoted: AS "Revenue". Follow specs/sql-style-guide.md from the start.
  • Never write {{snippet: x}} inside a SQL comment — Metabase expands it there too.

Sign-in and editions:

  • Browser sign-in needs v63+ and the app-registration switch that rides on Admin > AI > MCP.
  • Chrome rendering needs Chrome and Node 22+; --server PDFs need neither. Both show what Metabase has now. There is no server-side PNG for single cards on v63 (the download menu's PNG is drawn in the browser).
  • Pro/Enterprise-only commands are refused on the free edition with a clear message (--skip-preflight to try).

CLI: --patch / --from take file paths only. Batch updates: write all patch files first, then loop.


Example Prompts

"Build me a store performance dashboard: weekly installs, conversion and ratings as KPIs, a trend line, a breakdown by country, with date and country filters."

NEW → design (metrics/dimensions/filters → text → HTML mockup) → project new → snippets, filters.yaml, cards, dashboard.yaml → validate / lint / test → diff → push → visual check (card images, dashboard render) → test --images → PDF hand-off.

"Show me what dashboard 134 looks like — is anything broken?"

STUDY step 2: dashboard render 134 → Read each tab → report layout and card_errors.

"The date filter on dashboard 22 doesn't affect the Revenue by State card."

Project folder? project pull, check that placement's filters: and the card's tags, fix, diff, push, render. Otherwise dashcard 22 "Revenue by State", add the tag to the card if missing (card update), wire it with dashcard update, then dashboard render 22 --params '{"date": "past30days~"}' to confirm.

"Send me a PDF of the store dashboard." — dashboard render 134 --out <dir>/store-analytics.pdf.

"Email the exec dashboard to the leadership list every Monday at 8." — confirm addresses → subscription (weekly, mon, 8) → report the timezone. In a project: add it under subscriptions: and push.

"Alert me when yesterday's signups drop below 50." — shape a card that returns a row only when signups < 50 → alert with has_result, daily cron.

"Who changed card 402 last week? Put it back." — card history 402 → confirm the revision → card revert.

"Tidy up the Commercial collection." — REORGANIZE: collections --tree, collection-items, propose a structure, then collection create and move items via card update / dashboard update with collection_id.

"Am I signed in? Which version is it?" — status.