@metabase/cli 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -115
- package/dist/add-collection-C9BdVBs2.mjs +11 -0
- package/dist/{add-collection-C_iovi9i.mjs → add-collection-DQjTlDNF.mjs} +12 -5
- package/dist/archive-B3qiL-kK.mjs +40 -0
- package/dist/{archive-DdaP94H3.mjs → archive-BXzghEQX.mjs} +10 -9
- package/dist/{archive-WaEW85NB.mjs → archive-CBGKzEAl.mjs} +9 -8
- package/dist/{archive-BKPO8lEO.mjs → archive-CuVk8iwN.mjs} +10 -9
- package/dist/archive-DTN9tLGT.mjs +40 -0
- package/dist/{archive-Dvzrmdbk.mjs → archive-De8jzzq7.mjs} +10 -9
- package/dist/auth-D9eAyVoG.mjs +19 -0
- package/dist/{body-XtR7-uCO.mjs → body-tcURGnGh.mjs} +4 -4
- package/dist/{branches-XUY4JY-X.mjs → branches-CIGkjXIk.mjs} +11 -7
- package/dist/{cancel-BrUVO_ax.mjs → cancel-pPsvgJ0Z.mjs} +9 -8
- package/dist/{cancel-task-oXheTOB6.mjs → cancel-task-BLGE4UlL.mjs} +12 -8
- package/dist/capabilities-7e9MgquN.mjs +29 -0
- package/dist/card-DDDrWcDU.mjs +20 -0
- package/dist/{card-CQxvHeyP.mjs → card-ezYiriML.mjs} +1 -1
- package/dist/{cards-CONTTAG9.mjs → cards-Dq3nx_9n.mjs} +8 -7
- package/dist/cli.mjs +264 -44
- package/dist/collection-DkEvCDar.mjs +20 -0
- package/dist/{predicates-CGO17Q15.mjs → command-augment-BH9qgQ5u.mjs} +66 -14
- package/dist/{create-Cs2xntFG.mjs → create-9DBTkbMq.mjs} +18 -16
- package/dist/{create-kYpjobrq.mjs → create-B1dyuL9Y.mjs} +16 -10
- package/dist/create-BIphz0kO.mjs +52 -0
- package/dist/create-BcgoukG4.mjs +50 -0
- package/dist/create-BdPoSk_7.mjs +50 -0
- package/dist/{create-DZxUeqdf.mjs → create-BrUqxreg.mjs} +11 -10
- package/dist/create-DHscDhRd.mjs +50 -0
- package/dist/{create-Dq25vsMu.mjs → create-aPaUEGdr.mjs} +11 -10
- package/dist/{create-branch-D5u14AxL.mjs → create-branch-DGoc9CUU.mjs} +12 -8
- package/dist/{create-Ca9lIDwP.mjs → create-w3mQg9n4.mjs} +11 -10
- package/dist/{credentials-BIQ1cEzM.mjs → credentials-qryRLUed.mjs} +12 -11
- package/dist/current-task-DZM28rnr.mjs +40 -0
- package/dist/{dashboard-CnMD04PQ.mjs → dashboard-B4bn3z6t.mjs} +2 -1
- package/dist/dashboard-BLf1RZlk.mjs +21 -0
- package/dist/{database-vvig8k4x.mjs → database-BXiue1in.mjs} +1 -1
- package/dist/{database-BSvzYlRe.mjs → database-Ce1gOJF7.mjs} +3 -3
- package/dist/db-CWTFe_FZ.mjs +22 -0
- package/dist/{delete-VTAS9EUt.mjs → delete-BPaFdHZP.mjs} +11 -8
- package/dist/{delete-CUx6RT9e.mjs → delete-BrJOotpW.mjs} +11 -8
- package/dist/{remove-C6bS0Z6w.mjs → delete-FFj1xQWO.mjs} +25 -20
- package/dist/{delete-runtime-DfFMWJJ6.mjs → delete-runtime-uuYbd4k2.mjs} +9 -7
- package/dist/{delete-table-DzUneMKe.mjs → delete-table-CNupWUO0.mjs} +11 -8
- package/dist/{deprovision-CpJfGgCt.mjs → deprovision-BNr9fPDY.mjs} +18 -12
- package/dist/{dirty-nkAOXxgC.mjs → dirty-BCkNOY8c.mjs} +11 -7
- package/dist/{docker-D5FTIoD0.mjs → docker-Ds252Mwc.mjs} +4 -4
- package/dist/{translate-Cqsd0Px5.mjs → eid-CLY5X0Uw.mjs} +43 -23
- package/dist/error-ZsFeevV2.mjs +191 -0
- package/dist/{export-BWvY7X_G.mjs → export-CgHgWW3I.mjs} +20 -18
- package/dist/{field-B3gvaqpK.mjs → field-E0IBy4Uw.mjs} +12 -3
- package/dist/field-LL6W_c-c.mjs +18 -0
- package/dist/{fields-dH16G5UV.mjs → fields-RkRWU-u9.mjs} +9 -8
- package/dist/{get-D96QEU49.mjs → get-42tJ7BNp.mjs} +8 -7
- package/dist/{get-C6SR3A9t.mjs → get-B8l4t4Pz.mjs} +10 -9
- package/dist/{get-BnBRKHr7.mjs → get-B9kwSs6U.mjs} +8 -7
- package/dist/{get-7macOPAI.mjs → get-Bo4Cpd_c.mjs} +7 -7
- package/dist/{get-BcqxMVC1.mjs → get-C9O_aEGo.mjs} +8 -7
- package/dist/{get-CKxlhMy1.mjs → get-CRvbChoX.mjs} +8 -7
- package/dist/{get-B7i_nYJB.mjs → get-CTDqioaj.mjs} +8 -7
- package/dist/{get-D8e_RzZ0.mjs → get-CiZrZJLt.mjs} +10 -9
- package/dist/{get-B08K82JV.mjs → get-CvmqPN30.mjs} +8 -7
- package/dist/{get-CACaBFLt.mjs → get-DmzgSgrl.mjs} +9 -8
- package/dist/{get-R7OaVL_t.mjs → get-DsqGHNHN.mjs} +8 -7
- package/dist/get-run-CBwcRc8E.mjs +38 -0
- package/dist/{get-DNN1X2gN.mjs → get-sMpa-X4E.mjs} +9 -8
- package/dist/{get-DAWofnzK.mjs → get-y17zJMnU.mjs} +8 -7
- package/dist/git-sync-CrWTo3YX.mjs +28 -0
- package/dist/{has-remote-changes-BAnIXQXU.mjs → has-remote-changes-CfRidwXT.mjs} +13 -8
- package/dist/{import-CfdPEMng.mjs → import-BZV0Z2KR.mjs} +21 -18
- package/dist/{input-BQ-BZA8h.mjs → input-cMSEqISy.mjs} +7 -4
- package/dist/is-dirty-CPzOnnH6.mjs +10 -0
- package/dist/{is-dirty-CZWcG0vj.mjs → is-dirty-hKcB4OH9.mjs} +9 -4
- package/dist/{items-DqwahOKf.mjs → items-C94eW2Yd.mjs} +10 -9
- package/dist/key-vkNkH82H.mjs +17 -0
- package/dist/license-B37055sr.mjs +17 -0
- package/dist/{list-L63TpX1t.mjs → list-B0V7FeL2.mjs} +7 -7
- package/dist/{list-Bkd7Nbds.mjs → list-BFlzLGlw.mjs} +7 -6
- package/dist/{list-yxVAE1S7.mjs → list-BJXaGk-z.mjs} +7 -6
- package/dist/{list-BqNMpIXy.mjs → list-BS_Bxejg.mjs} +9 -8
- package/dist/{list-J277Qtki.mjs → list-BmHoYJr7.mjs} +7 -6
- package/dist/list-C-oZe1_p.mjs +173 -0
- package/dist/{list-ViT2KWhv.mjs → list-CF1pMN4S.mjs} +7 -6
- package/dist/{list-BpNU1neq.mjs → list-CU6sOfI-.mjs} +9 -7
- package/dist/{list-oftHLFbE.mjs → list-CqN4gvCk.mjs} +9 -7
- package/dist/{list-DBOYoJtA.mjs → list-DUXdt0XI.mjs} +10 -6
- package/dist/{list-D41gfkKb.mjs → list-DfDZr55C.mjs} +10 -8
- package/dist/{list-vF4EneaE.mjs → list-DrINpVLM.mjs} +7 -6
- package/dist/{list-DJcGwJ4W.mjs → list-DuSoEk_J.mjs} +9 -8
- package/dist/{list-CQkDqphl.mjs → list-HS15y_WN.mjs} +7 -6
- package/dist/login-enh9Yimb.mjs +181 -0
- package/dist/{logout-DD4q5whi.mjs → logout-BWLPLDh8.mjs} +13 -11
- package/dist/{logs-Ci3mJE2z.mjs → logs-Cu3QtvPs.mjs} +9 -8
- package/dist/{manifest-CGM7XNLC.mjs → manifest-BNh0Lw6p.mjs} +15 -15
- package/dist/{measure-BEQfnLdN.mjs → measure-Bt3InQsA.mjs} +2 -2
- package/dist/measure-CDlEPFtB.mjs +19 -0
- package/dist/{metadata-BDat-jN9.mjs → metadata-BTTEBWdS.mjs} +10 -9
- package/dist/{metadata-29_qlqbz.mjs → metadata-D2TxboMm.mjs} +9 -8
- package/dist/parse-enum-CrEWOhuY.mjs +11 -0
- package/dist/{parse-id-CysSaCbf.mjs → parse-id-0_tOPvfI.mjs} +1 -1
- package/dist/{parse-ref-D1yeDOn8.mjs → parse-ref-DKag6a6I.mjs} +1 -1
- package/dist/{parse-schemas-B10n01ez.mjs → parse-schemas-D-qVLl4z.mjs} +2 -2
- package/dist/{path-DLByFMMA.mjs → path-C8GrBdgT.mjs} +7 -7
- package/dist/{poll-p9Y7-JEQ.mjs → poll-4eoh5J0r.mjs} +2 -2
- package/dist/{poll-task-BQe0NvJZ.mjs → poll-task-51WRdugU.mjs} +19 -20
- package/dist/{preflight-CvFu0Cct.mjs → preflight-BhsErYz3.mjs} +4 -4
- package/dist/{process-zJeVJZTM.mjs → process-CM7Uu5q_.mjs} +1 -1
- package/dist/{prompt-DgDNy_Pc.mjs → prompt-CFKoys7k.mjs} +3 -1
- package/dist/provision-Chf86BF0.mjs +83 -0
- package/dist/{ps-BxQdpkr5.mjs → ps-CEYtsKBj.mjs} +5 -3
- package/dist/ps-CIDwaubS.mjs +11 -0
- package/dist/{query-C7zTlFJA.mjs → query-BBCAF-tG.mjs} +17 -14
- package/dist/{query-CFH4nBlK.mjs → query-DYVBnu9d.mjs} +12 -9
- package/dist/query-result-ABPLz6I4.mjs +19 -0
- package/dist/{remove-BuWxx3hY.mjs → remove-2yInufA6.mjs} +14 -12
- package/dist/remove-collection-CBAHz0Dk.mjs +44 -0
- package/dist/{render-DuoDUTVL.mjs → render-0_GsapXa.mjs} +51 -23
- package/dist/render-khznBlla.mjs +32 -0
- package/dist/{rescan-values-DabyRYQ_.mjs → rescan-values-cfTSNQZo.mjs} +12 -11
- package/dist/{run-Cl-9RtC4.mjs → run-qgdEJv-I.mjs} +17 -15
- package/dist/{runs-BH6s1Zao.mjs → runs-BFIIH4GL.mjs} +9 -8
- package/dist/{runtime-CDu6fykq.mjs → runtime-Duawf5lE.mjs} +653 -428
- package/dist/{schema-tables-i58wp_p3.mjs → schema-tables-C2xM3dho.mjs} +9 -8
- package/dist/{schemas-_m8RYRl9.mjs → schemas-BP7xiktH.mjs} +7 -6
- package/dist/{search-DObOsjbP.mjs → search-DYP3lOlq.mjs} +12 -16
- package/dist/segment-B6HnNGDs.mjs +19 -0
- package/dist/{set-CwVWeAsi.mjs → set-DpRQqdo7.mjs} +13 -11
- package/dist/{set-CJA9dpK6.mjs → set-Tt-ioa4L.mjs} +14 -13
- package/dist/setting-DUa96KF3.mjs +17 -0
- package/dist/{setup-DqBOe3HZ.mjs → setup-BPlllnim.mjs} +10 -9
- package/dist/{skills-CHU7uuDU.mjs → skills-BkregMyb.mjs} +2 -2
- package/dist/{skills-C2rTVj0n.mjs → skills-SqbPo0BI.mjs} +3 -3
- package/dist/snippet-dJ68tGsl.mjs +19 -0
- package/dist/{start-CfruN4wF.mjs → start-DJZA67WF.mjs} +70 -38
- package/dist/{stash-CWuXKSZq.mjs → stash-C89zNKxo.mjs} +21 -18
- package/dist/{status-D-RYZB9G.mjs → status-B1EJ_jv0.mjs} +16 -9
- package/dist/status-BNvFPemM.mjs +100 -0
- package/dist/status-D5wSqYV_.mjs +34 -0
- package/dist/{stop-D8Hr4cKX.mjs → stop-5rCLmkCQ.mjs} +16 -11
- package/dist/{summary-Lt2XLBK9.mjs → summary-Cihbx0Qs.mjs} +8 -7
- package/dist/{sync-schema-BDElSynU.mjs → sync-schema-C3odu0ZH.mjs} +12 -11
- package/dist/table-J2f0STnB.mjs +19 -0
- package/dist/{table-B-PYcgGb.mjs → table-qDD2kApF.mjs} +1 -1
- package/dist/{transform-job-BrhOLO4M.mjs → transform-job-DjhoJbiV.mjs} +1 -1
- package/dist/transform-job-OW4SDhsQ.mjs +19 -0
- package/dist/transform-q1LYWQtW.mjs +24 -0
- package/dist/{tree-DfvjDjmk.mjs → tree-mvq9gM9w.mjs} +7 -6
- package/dist/{update-DzgXF082.mjs → update-BoIiuC70.mjs} +16 -14
- package/dist/{update-qnFY5IuC.mjs → update-C0jP0AKT.mjs} +12 -11
- package/dist/{update-zp7pCBZH.mjs → update-CbBnHz42.mjs} +16 -14
- package/dist/{update-B0bjPqKC.mjs → update-CtOo3LsX.mjs} +13 -12
- package/dist/{update-CVxOxmt6.mjs → update-DCrOQ1PW.mjs} +17 -15
- package/dist/{update-BYduslhn.mjs → update-DEZayTb4.mjs} +20 -15
- package/dist/{update-BgcroYkF.mjs → update-DwRxdflw.mjs} +12 -11
- package/dist/{update-D9Z8cL7h.mjs → update-Rr4usmCo.mjs} +12 -11
- package/dist/{update-DuA8-cCq.mjs → update-VvKMnwsM.mjs} +16 -14
- package/dist/{update-dashcard-CQ3kmmss.mjs → update-dashcard-DFvIz8Qj.mjs} +12 -11
- package/dist/{update-CqnDMNtZ.mjs → update-tRparnUs.mjs} +18 -16
- package/dist/{upgrade-CIgTr2CG.mjs → upgrade-D-Rl_fH9.mjs} +16 -29
- package/dist/{url-B5MgZXzg.mjs → url-BB6jeNQj.mjs} +11 -10
- package/dist/{uuid-CJz9TmHI.mjs → uuid-BSVUk8u2.mjs} +8 -6
- package/dist/{validate-CB0bu50i.mjs → validate-dPEOnOf8.mjs} +2 -1
- package/dist/{validate-query-CavIA0Q2.mjs → validate-query-CYvOP8Ld.mjs} +3 -3
- package/dist/values-D1RJE4H6.mjs +45 -0
- package/dist/verify-A7BWfBPZ.mjs +79 -0
- package/dist/{wait-BFqBlg0y.mjs → wait-B17I_pWy.mjs} +2 -2
- package/dist/{wait-tDp9ZOou.mjs → wait-DK5QDZ8n.mjs} +14 -10
- package/dist/{wait-flags-CN-e9zNq.mjs → wait-flags-DlfbIXHw.mjs} +20 -9
- package/dist/{workspace-credentials-4lIxxz4g.mjs → workspace-credentials-8CBMQJFz.mjs} +2 -2
- package/dist/workspace-ri6r3zWo.mjs +25 -0
- package/dist/{yaml-ECiog374.mjs → yaml-Gv6wRFMF.mjs} +1 -1
- package/package.json +2 -1
- package/skill-data/core/SKILL.md +55 -453
- package/skill-data/git-sync/SKILL.md +1 -1
- package/skill-data/mbql/SKILL.md +156 -0
- package/skill-data/mbql/references/operators.md +253 -0
- package/skill-data/transform/SKILL.md +2 -40
- package/skill-data/viz/SKILL.md +137 -0
- package/skill-data/viz/references/settings.md +312 -0
- package/skill-data/workspace/SKILL.md +45 -63
- package/skills/metabase-cli/SKILL.md +5 -26
- package/dist/add-collection-ucsyAMkV.mjs +0 -11
- package/dist/api-key-BENHbTbV.mjs +0 -13
- package/dist/auth-DICRtJDy.mjs +0 -19
- package/dist/card-l-UmrUIo.mjs +0 -20
- package/dist/collection-oV0olVY-.mjs +0 -19
- package/dist/command-augment-D9pI9Vbh.mjs +0 -11
- package/dist/create-CrUq6sib.mjs +0 -125
- package/dist/create-D3Z878yr.mjs +0 -50
- package/dist/create-Le3Bqn7b.mjs +0 -48
- package/dist/create-V-q2rU0T.mjs +0 -48
- package/dist/create-swbIXdo5.mjs +0 -48
- package/dist/current-task-DCq7rk9V.mjs +0 -36
- package/dist/dashboard-hbKDd36X.mjs +0 -20
- package/dist/db-qVK6NsdB.mjs +0 -22
- package/dist/eid-CDFXX_6H.mjs +0 -13
- package/dist/field-C0LE7RQI.mjs +0 -18
- package/dist/flag-pair-Fmcdkrfx.mjs +0 -17
- package/dist/get-run-CwFuR4Uw.mjs +0 -36
- package/dist/git-sync-DV7YjniX.mjs +0 -28
- package/dist/is-dirty-LxVbm2C5.mjs +0 -10
- package/dist/key-CCJdVWKc.mjs +0 -12
- package/dist/license-Cb6ewEJO.mjs +0 -17
- package/dist/list-DV6CONhp.mjs +0 -55
- package/dist/login-D1nZwgKv.mjs +0 -192
- package/dist/measure-XhJuL77y.mjs +0 -19
- package/dist/package-DFUprkSZ.mjs +0 -85
- package/dist/provision-BP-b4Are.mjs +0 -77
- package/dist/ps-Bk6unzaX.mjs +0 -11
- package/dist/remove-collection-Bc4roCq0.mjs +0 -38
- package/dist/segment-DfxZdJmR.mjs +0 -19
- package/dist/setting-Czy4ws6h.mjs +0 -18
- package/dist/snippet-BCY4KHBU.mjs +0 -19
- package/dist/status-1oUnw803.mjs +0 -56
- package/dist/status-J9HIDcA5.mjs +0 -32
- package/dist/table-BwX3Ib5f.mjs +0 -19
- package/dist/transform-iaAi37V0.mjs +0 -24
- package/dist/transform-job-Bemonf82.mjs +0 -19
- package/dist/values-BXN6tx1i.mjs +0 -36
- package/dist/workspace-BBsT0H0g.mjs +0 -24
- /package/dist/{body-flags-BK7J6Daz.mjs → body-flags-D7q87Btw.mjs} +0 -0
- /package/dist/{collection-B3sPXRLs.mjs → collection-Bcy8cWYH.mjs} +0 -0
- /package/dist/{paginate-CTSfuYiF.mjs → paginate-BexjkjbY.mjs} +0 -0
- /package/dist/{revision-message-flag-oyq2xrDU.mjs → revision-message-flag-DY29-cgz.mjs} +0 -0
- /package/dist/{segment-BMrUBz94.mjs → segment-DhBmcr_E.mjs} +0 -0
- /package/dist/{setting-CTaAeMci.mjs → setting-BzCng1Ub.mjs} +0 -0
- /package/dist/{snippet-CSWqkslB.mjs → snippet-bi_0XbNT.mjs} +0 -0
- /package/dist/{transform-DR4ejuPM.mjs → transform-BKahefz_.mjs} +0 -0
- /package/dist/{workspace-DUfqhPm5.mjs → workspace-D8HtUN0y.mjs} +0 -0
package/skill-data/core/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: core
|
|
3
|
-
description: Drive a Metabase instance from the terminal via the `mb` CLI
|
|
3
|
+
description: Drive a Metabase instance from the terminal via the `mb` CLI — auth, databases, cards, dashboards, collections, transforms, queries, search, git-sync, Enterprise workspaces. Use for any `mb <verb>` task.
|
|
4
4
|
allowed-tools: Read, Write, Edit, Bash, AskUserQuestion
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -11,55 +11,43 @@ The official Metabase CLI (`mb`) drives a Metabase instance over its REST API. I
|
|
|
11
11
|
Top-level command groups (run `mb <group> --help` to discover verbs):
|
|
12
12
|
|
|
13
13
|
```
|
|
14
|
-
auth |
|
|
15
|
-
setting | search | git-sync | workspace | setup |
|
|
14
|
+
auth | db | table | field | query | card | dashboard | snippet | segment | measure | collection
|
|
15
|
+
transform | transform-job | setting | search | git-sync | workspace | setup | eid | uuid | upgrade | skills
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
The
|
|
18
|
+
The patterns below — auth, flag conventions, output flags, body input — apply across **every** group. Per-command flags, examples, and output schemas live in `mb __manifest` (see below). A few flows have their own specialized skills; load them on demand (see "Specialized skills"). Authoring any query body (cards, transforms, measures, segments, ad-hoc `mb query`) is one — load `mbql` whenever you build MBQL by hand.
|
|
19
19
|
|
|
20
20
|
## Auth & profiles
|
|
21
21
|
|
|
22
|
-
**The agent does not log in for the user.** Authentication is the human's job — they pick the base URL, paste credentials, and store them as a named profile
|
|
22
|
+
**The agent does not log in for the user.** Authentication is the human's job — they pick the base URL, paste credentials, and store them as a named profile. The agent's role is to _check_ what profiles exist, _ask_ which to use, and pass `--profile <name>` through every command.
|
|
23
23
|
|
|
24
|
-
**The one exception** is a freshly bootstrapped workspace child
|
|
25
|
-
|
|
26
|
-
For everything else (parent profile, staging, prod, anything pointing at a Metabase the user has direct credentials for), follow the flow below.
|
|
24
|
+
**The one exception** is a freshly bootstrapped workspace child: its API key is minted by the parent the human already authorized, so the agent reads it via `mb workspace credentials <ws-id>` and saves it with `auth login` — piping the key on **stdin** (`printf '%s' "$KEY" | mb auth login …`), never on an `--api-key` flag (the CLI rejects the flag form). See the `workspace` skill, step 4.
|
|
27
25
|
|
|
28
26
|
### Discover what's already configured
|
|
29
27
|
|
|
30
28
|
```bash
|
|
31
|
-
mb auth list --json # → {data: [{profile, url,
|
|
29
|
+
mb auth list --json # → {data: [{profile, url, authenticated, status, …}], returned, total}
|
|
32
30
|
mb auth status --json # → {profile, present, url} for the default profile
|
|
33
31
|
mb auth status --profile <name> --json # → status of a specific profile
|
|
34
32
|
```
|
|
35
33
|
|
|
36
|
-
`auth list` is the primary enumeration path — one call returns every configured profile with sanitized URL and `
|
|
37
|
-
|
|
38
|
-
If `auth list` returns an empty `data: []` or the user has no profile set up, **stop and ask them to log in themselves**:
|
|
39
|
-
|
|
40
|
-
> Please run, yourself, `mb auth login --url <your-base-url> --profile <name>`. Tell me the profile name when you're done.
|
|
41
|
-
|
|
42
|
-
Don't suggest a base URL, paste an API key, or run `auth login` on their behalf. Profile names are arbitrary local labels — `prod`, `staging`, the workspace name — let the user pick.
|
|
34
|
+
`auth list` is the primary enumeration path — one call returns every configured profile with sanitized URL, an `authenticated` flag, and a probe `status` (`ok` / `auth-failed` / `network-error` / `server-error` / `not-probed`). Use it before asking the user which profile to pick. If it returns an empty `data: []`, ask the user to run `mb auth login` themselves (see the policy above) and tell you the profile name. `auth status` is a single-profile health probe when you already know the name.
|
|
43
35
|
|
|
44
36
|
### Pick the profile to use
|
|
45
37
|
|
|
46
|
-
|
|
38
|
+
If exactly one profile is configured and the user's intent doesn't disambiguate, use it. If multiple profiles exist and the user hasn't named one, ask via `AskUserQuestion`, presenting the names from `auth list`. Once a name is established, pass `--profile <name>` to **every** subsequent command. Profile names are arbitrary local labels — `prod`, `staging`, the workspace name — let the user pick.
|
|
47
39
|
|
|
48
40
|
### Other secrets (license, warehouse passwords)
|
|
49
41
|
|
|
50
42
|
Same rule: the human runs the storing command. To check whether a license is present:
|
|
51
43
|
|
|
52
44
|
```bash
|
|
53
|
-
mb license status --
|
|
45
|
+
mb workspace license status --json # → {present: bool}
|
|
54
46
|
```
|
|
55
47
|
|
|
56
|
-
If `present: false`, ask
|
|
57
|
-
|
|
58
|
-
> Please run `echo "<your-token>" | mb license set --profile <name>` from your terminal — don't paste the token in chat.
|
|
59
|
-
|
|
60
|
-
## Flag conventions (read once, internalize)
|
|
48
|
+
If `present: false`, ask the user to run `echo "<your-token>" | mb workspace license set` from their terminal — don't paste the token in chat.
|
|
61
49
|
|
|
62
|
-
|
|
50
|
+
## Flag conventions
|
|
63
51
|
|
|
64
52
|
### `--profile` is per-subcommand, not global
|
|
65
53
|
|
|
@@ -70,27 +58,16 @@ These trip up every fresh run.
|
|
|
70
58
|
|
|
71
59
|
`--profile` attaches **after** the full verb chain (`table list`, `card get`, `workspace start`).
|
|
72
60
|
|
|
73
|
-
### When you do call `auth login` (workspace child only), pipe the key on stdin
|
|
74
|
-
|
|
75
|
-
The agent normally doesn't run `auth login` (see "Auth & profiles" — the human does). The one place it _does_ — saving a workspace child's API key after `workspace credentials` — must use stdin, not a flag value:
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
✅ printf '%s' "$KEY" | mb auth login --url <url> --api-key-stdin --profile <n> --json
|
|
79
|
-
❌ mb auth login --api-key "$KEY" … # → warns + rejects
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Reason: shell history and process listings leak the value. The CLI rejects the flag form on purpose.
|
|
83
|
-
|
|
84
61
|
### `--wait` for async operations
|
|
85
62
|
|
|
86
|
-
`workspace start`, `workspace database provision`, `transform run`, and similar async verbs return immediately by default. Pass `--wait` for any interactive flow where the next step depends on completion. Without
|
|
63
|
+
`workspace start`, `workspace database provision`, `transform run`, and similar async verbs return immediately by default. Pass `--wait` for any interactive flow where the next step depends on completion. Without it you'll race the operation and see "not ready" / `state: starting` / transient connection refusals.
|
|
87
64
|
|
|
88
65
|
### Some outputs are JSON envelopes, not bare strings
|
|
89
66
|
|
|
90
67
|
A handful of "lookup" verbs return a JSON object even when you only want a single field. `mb workspace url <id>` returns `{"workspace_id": ..., "url": "http://..."}`, not `"http://..."`. Don't drop them raw into another flag — extract:
|
|
91
68
|
|
|
92
69
|
```bash
|
|
93
|
-
WS_URL=$(mb workspace url <id> --
|
|
70
|
+
WS_URL=$(mb workspace url <id> --json | jq -r '.url')
|
|
94
71
|
```
|
|
95
72
|
|
|
96
73
|
If you find yourself writing `--url $(mb ...)` and the receiving command rejects it with "URL must start with http://", this is what happened.
|
|
@@ -99,12 +76,10 @@ If you find yourself writing `--url $(mb ...)` and the receiving command rejects
|
|
|
99
76
|
|
|
100
77
|
Every list/get verb supports the same output flags:
|
|
101
78
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
| `--fields a,b.c.d` | Project specific dot-paths. Mutually exclusive with `--full`. |
|
|
107
|
-
| `--max-bytes <n>` | Cap **list** output size (drops trailing items, sets `truncated`). Default 65 536; `0` disables. Single-item commands (`get`, `metadata`) never truncate — they only emit a stderr advisory when the body is over the cap. |
|
|
79
|
+
- `--json` — emit the full JSON envelope, safe for `jq`. Default is human-readable text.
|
|
80
|
+
- `--full` — include every field (compact projection is the default for list/get).
|
|
81
|
+
- `--fields a,b.c.d` — project specific dot-paths. Mutually exclusive with `--full`.
|
|
82
|
+
- `--max-bytes <n>` — cap **list** output size (drops trailing items, sets `truncated`). Default 65 536; `0` disables. Single-item commands (`get`, `metadata`) never truncate — they emit a stderr advisory when over the cap.
|
|
108
83
|
|
|
109
84
|
List envelope shape:
|
|
110
85
|
|
|
@@ -120,9 +95,7 @@ List envelope shape:
|
|
|
120
95
|
}
|
|
121
96
|
```
|
|
122
97
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
`total` is best-effort and may be omitted or `null` — the server returns `null` for empty/permissions-filtered collections, and `--limit` early-stop omits it because the true total is unknown. Don't depend on it being a number; use `returned` for the count you actually got back and `data.length` for the rendered slice.
|
|
98
|
+
The compact item projection is the agent-facing contract — add `--full` for all Metabase fields. `total` is best-effort and may be `null` (empty / permissions-filtered collections, or `--limit` early-stop); use `returned` for the count you got and `data.length` for the rendered slice.
|
|
126
99
|
|
|
127
100
|
## Body input (create / update / run)
|
|
128
101
|
|
|
@@ -130,13 +103,11 @@ Verbs that take a payload accept it from one of four sources, **first non-empty
|
|
|
130
103
|
|
|
131
104
|
1. `--body '<inline JSON>'`
|
|
132
105
|
2. `--file <path>` — JSON file
|
|
133
|
-
3. stdin (auto-detected when piped, or explicit
|
|
106
|
+
3. stdin (auto-detected when piped, or explicit `--stdin` where supported)
|
|
134
107
|
4. positional argument
|
|
135
108
|
|
|
136
109
|
Picking exactly one is required; passing two of `--body` + `--file` + `--stdin` is rejected with a `ConfigError`.
|
|
137
110
|
|
|
138
|
-
Common pattern:
|
|
139
|
-
|
|
140
111
|
```bash
|
|
141
112
|
cat > /tmp/body.json <<'EOF'
|
|
142
113
|
{ ... }
|
|
@@ -144,432 +115,63 @@ EOF
|
|
|
144
115
|
mb <noun> create --file /tmp/body.json --profile <n> --json
|
|
145
116
|
```
|
|
146
117
|
|
|
147
|
-
|
|
118
|
+
Single-quoted `'EOF'` prevents the shell from interpolating `$vars` inside the JSON.
|
|
148
119
|
|
|
149
120
|
## Discover the full surface: `mb __manifest`
|
|
150
121
|
|
|
151
|
-
For the canonical, machine-readable inventory of every command — name, description, examples, every flag with type and default, and the output JSON Schema — run:
|
|
122
|
+
For the canonical, machine-readable inventory of every command — name, description, per-command `details`, examples, every flag with type and default, and the output JSON Schema — run:
|
|
152
123
|
|
|
153
124
|
```bash
|
|
154
125
|
mb __manifest
|
|
155
126
|
```
|
|
156
127
|
|
|
157
|
-
The leading `__`
|
|
158
|
-
|
|
159
|
-
```bash
|
|
160
|
-
# Every command name:
|
|
161
|
-
mb __manifest | jq -r '.commands[].command'
|
|
162
|
-
|
|
163
|
-
# Every verb under "transform":
|
|
164
|
-
mb __manifest | jq -r '.commands[] | select(.command | startswith("transform")) | .command'
|
|
165
|
-
|
|
166
|
-
# Flags + types for `card query`:
|
|
167
|
-
mb __manifest | jq '.commands[] | select(.command == "card query") | .args'
|
|
168
|
-
|
|
169
|
-
# Output schema for `card list` (so you know what to parse):
|
|
170
|
-
mb __manifest | jq '.commands[] | select(.command == "card list") | .outputSchema'
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
Use it to (a) enumerate verbs you don't know by heart, (b) validate flag names before constructing a command, (c) read an output schema before parsing. Cheaper and more reliable than scraping `--help` text.
|
|
174
|
-
|
|
175
|
-
## Resources at a glance
|
|
176
|
-
|
|
177
|
-
The CLI exposes the Metabase REST API in 13 command groups beyond `auth` / `license`. Each follows the same shape (list/get/create/…); flags + output schemas are in `__manifest`. Only the deviations and quirks worth memorizing are below.
|
|
178
|
-
|
|
179
|
-
### `db` (alias `database`) — list and inspect databases
|
|
180
|
-
|
|
181
|
-
**Default agent traversal (granular, scales to real warehouses):**
|
|
182
|
-
|
|
183
|
-
```bash
|
|
184
|
-
mb database list --profile <n> --json # discover db ids
|
|
185
|
-
mb database schemas <db-id> --profile <n> --json # list schema names in one db
|
|
186
|
-
mb database schema-tables <db-id> <schema> --profile <n> --json # tables in ONE schema (compact)
|
|
187
|
-
mb table get <table-id> --include fields --profile <n> --json # fields for ONE table (see `table` section)
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
This is the path to use. A production Metabase typically has dozens of schemas, hundreds of tables, and dozens of fields per table — walking three levels and pulling one table's fields at a time keeps each response in the kilobytes. The rollup endpoints below pull megabytes and will blow the context window on any real warehouse.
|
|
191
|
-
|
|
192
|
-
**Other commands:**
|
|
193
|
-
|
|
194
|
-
```bash
|
|
195
|
-
mb database list --saved --profile <n> --json # include the Saved Questions virtual db (id -1337)
|
|
196
|
-
mb database get <db-id> --profile <n> --json # db record only (no tables)
|
|
197
|
-
mb database sync-schema <db-id> --profile <n> # POST /sync_schema; queues async work, returns {status:"ok"}
|
|
198
|
-
mb database rescan-values <db-id> --profile <n> # POST /rescan_values; queues async work, returns {status:"ok"}
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
**Rollup commands — only on small/dev warehouses:**
|
|
202
|
-
|
|
203
|
-
```bash
|
|
204
|
-
mb database list --include tables --profile <n> --full --json # every db with its full table list
|
|
205
|
-
mb database get <db-id> --include tables.fields --profile <n> --full --json # one db, every table, every field
|
|
206
|
-
mb database metadata <db-id> --profile <n> --full --json # alias for the above, server-rolled
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
Reach for these only when you know the db is small (a seeded dev instance, a sample db, a freshly-bootstrapped test fixture) or when you genuinely need every column of every table in one shot. On a real warehouse the response will exceed the agent context — use the granular traversal instead.
|
|
210
|
-
|
|
211
|
-
`sync-schema` / `rescan-values` are the two manual triggers admins reach for after warehouse-side changes; both queue work and return immediately.
|
|
212
|
-
|
|
213
|
-
### `table` — list and inspect tables
|
|
214
|
-
|
|
215
|
-
```bash
|
|
216
|
-
mb table list --db-id <db-id> --profile <n> --json # all tables in a db (compact, no fields)
|
|
217
|
-
mb table get <table-id> --profile <n> --json # table-level metadata only
|
|
218
|
-
mb table get <table-id> --include fields --profile <n> --json # bundles compact-projected fields ← default for field-listing
|
|
219
|
-
mb table fields <table-id> --profile <n> --json # just the fields, as a list envelope
|
|
220
|
-
mb table metadata <table-id> --profile <n> --json # fields + FKs + dimensions hydrated (heavier)
|
|
221
|
-
mb table update <table-id> --body '{"display_name":"Customers"}' --profile <n> --json
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
`table get` hits `/api/table/:id` and never returns fields on its own — `--full` only widens the projection over the already-fetched object. Pass `--include fields` for the field shape needed to author a card, transform, or measure; the hydrated path goes through `/api/table/:id/query_metadata`. Use `table fields` when you want just the field array (no surrounding table metadata) and `table metadata` only when you also need FKs and dimensions hydrated.
|
|
225
|
-
|
|
226
|
-
`table list --db-id <db-id>` returns every table across every schema as a flat compact list. On a real warehouse with hundreds of tables this is still smaller than `database get --include tables.fields`, but `database schema-tables <db-id> <schema>` is the right starting point when you know which schema you want.
|
|
227
|
-
|
|
228
|
-
`table update <id>` patches table-level metadata only — `display_name`, `description`, `caveats`, `points_of_interest`, `entity_type`, `visibility_type` (`normal`/`hidden`/`details-only`/`technical`/`cruft`), `field_order` (`alphabetical`/`custom`/`database`/`smart`), `show_in_getting_started`. Only the keys you send are touched. The underlying physical schema (the columns themselves) is not editable here — that's the warehouse's responsibility.
|
|
229
|
-
|
|
230
|
-
### `field` — inspect a single field, edit metadata, peek at distinct values
|
|
231
|
-
|
|
232
|
-
```bash
|
|
233
|
-
mb field get <field-id> --profile <n> --full --json
|
|
234
|
-
mb field values <field-id> --profile <n> --json # cached distinct values (FieldValues)
|
|
235
|
-
mb field summary <field-id> --profile <n> --json # {field_id, count, distincts} — live from the warehouse
|
|
236
|
-
mb field update <field-id> --body '{"semantic_type":"type/Email"}' --profile <n> --json
|
|
237
|
-
mb field update <field-id> --body '{"fk_target_field_id":<other-id>}' --profile <n> --json
|
|
238
|
-
mb field update <field-id> --body '{"description":"customer email"}' --profile <n> --json
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
No `list` — fields are per-table, so use `table get <table-id> --include fields` (compact) or `table fields <table-id>` (list envelope). Never try to enumerate fields across an entire database — that's what blows up the context.
|
|
242
|
-
|
|
243
|
-
`field update` patches metadata only — `display_name`, `description`, `caveats`, `points_of_interest`, `semantic_type` (Metabase type hierarchy: `type/Email`, `type/Category`, `type/PK`, `type/FK`, …), `coercion_strategy`, `fk_target_field_id` (the foreign-key target field), `visibility_type` (`normal`/`hidden`/`details-only`/`sensitive`/`retired`), `has_field_values` (`list`/`search`/`none`/`auto-list`), `settings`, `nfc_path`, `json_unfolding`. Only the keys you send are touched. `base_type` is not editable — that's the column's type as the warehouse reports it.
|
|
244
|
-
|
|
245
|
-
`field values` returns the _cached_ distinct values populated by the most recent field-values scan (`mb db rescan-values <db-id>` triggers a refresh). Useful when authoring a filter and you need the closed set of categorical values. Returns `{values, field_id, has_more_values, has_field_values}` — `has_more_values: true` means the cache was truncated; consider widening the cap server-side rather than treating the snapshot as exhaustive.
|
|
246
|
-
|
|
247
|
-
`field summary` returns `{field_id, count, distincts}` — cardinality straight from the warehouse, not the cache. Cheap pre-flight when deciding whether a column makes sense as a `list`-widget filter (low cardinality) or a `search` widget (high cardinality), and a quick way to spot a field that's effectively constant before you build a card around it.
|
|
248
|
-
|
|
249
|
-
### `query` — run ad-hoc MBQL with pre-flight validation
|
|
250
|
-
|
|
251
|
-
```bash
|
|
252
|
-
mb query --print-schema --profile <n> > /tmp/mbql.json # fetch the JSON Schema
|
|
253
|
-
mb query --file q.json --dry-run --profile <n> # validate, no network
|
|
254
|
-
mb query --file q.json --profile <n> --json # validate + run
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
The canonical agent-side path for ad-hoc MBQL. Three modes:
|
|
258
|
-
|
|
259
|
-
- `--print-schema` — emits `{ schema, defs }` where `defs` carries `id.yaml` / `parameter.yaml` / `ref.yaml` / `temporal_bucketing.yaml` keyed by the path used in the schema's `$ref`s. Use this **first** when authoring a non-trivial query — it's cheaper than guess-and-fail.
|
|
260
|
-
- `--dry-run` — validates and emits `{ ok, errors: [{path, message}] }`. Exit 0 if valid, 2 if not. No request sent.
|
|
261
|
-
- run (no flag) — validates, then on success runs the query. On validation failure: same envelope on stdout, exit 2, **never sends** the request.
|
|
262
|
-
|
|
263
|
-
MBQL 5 bodies use numeric IDs (`database: 1`, `source-table: 7`) and POST to `/api/dataset`. The bundled schema's `id.yaml` is overridden to require positive integers for every ID `$def`.
|
|
264
|
-
|
|
265
|
-
Validation error envelope (same shape across `query`, `card create`, `transform create/update`):
|
|
266
|
-
|
|
267
|
-
```json
|
|
268
|
-
{ "ok": false, "errors": [{ "path": "/stages/0/aggregation/0", "message": "must be array" }] }
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
`path` is a JSON Pointer into the body, `message` is the validator error string. Iterate against `--dry-run` until `ok: true`, then drop `--dry-run` to run.
|
|
272
|
-
|
|
273
|
-
Exit codes: `0` valid + ran, `2` validation failed / malformed body, `1` server-side error after a valid pre-flight.
|
|
274
|
-
|
|
275
|
-
**Any non-MBQL 5 body skips pre-flight automatically.** Legacy MBQL 4 (`{type:"query", database:N, query:{source-table:T, …}}`), legacy native (`{type:"native", database:N, native:{query:"…"}}`), and any other shape that doesn't carry `lib/type:"mbql/query"` are accepted by `/api/dataset` as-is and normalized server-side by `lib-be/normalize-query` (the same normalizer that backs `card create` / `transform create`, so behavior is symmetric across endpoints). The bundled schema only models MBQL 5; the CLI skips validation for the rest. Just `mb query --file probe.json` works for ad-hoc native SQL or legacy MBQL 4 probes; no `--skip-validate` needed. `--dry-run` on a non-MBQL 5 body returns `{ ok: true, errors: [] }`. The double-wrap footgun (`{type:"query", query:{lib/type:"mbql/query",…}}`) is still rejected with a `ConfigError` before send.
|
|
276
|
-
|
|
277
|
-
**`--skip-validate`** is the escape hatch for MBQL 5 bodies: bypasses the pre-flight and sends the body as-is. Use only when the bundled schema disagrees with what the server actually accepts (drift, false negative). Mutually exclusive with `--dry-run`. Same flag works on `mb card create` and `mb transform create / update`.
|
|
278
|
-
|
|
279
|
-
**MBQL 5 clause shape — opts always second.** Every clause is `[op, {options}, ...args]`: options object is the **second** element, not the third. Field refs are `["field", {options}, fieldId]` (id third), not the legacy MBQL 4 shape `["field", id, opts]`. The same `[op, {options}, …]` rule applies to aggregations (`["count", {options}]`, `["sum", {options}, <expr>]`), filters (`["=", {options}, <a>, <b>]`), order-by (`["asc", {options}, <expr>]`), and every other clause. Slot-1 violations surface from `--dry-run` as `must be the field options object` / `must be the clause options object` at `/stages/0/<verb>/<n>/1`.
|
|
280
|
-
|
|
281
|
-
### `uuid` — mint UUID v4 strings for `lib/uuid` slots
|
|
282
|
-
|
|
283
|
-
```bash
|
|
284
|
-
mb uuid # one UUID, v4 from crypto.randomUUID
|
|
285
|
-
mb uuid --count 5 # five UUIDs (one per line in TTY, JSON when piped)
|
|
286
|
-
mb uuid --count 5 --json # ["uuid1", "uuid2", …]
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
**Hard rule for agents: never generate, invent, hard-code, or reuse UUID values.** Always call `mb uuid` for fresh UUIDs at the moment you need them. Do not copy UUIDs from documentation examples, prior conversations, prior queries you authored, or anywhere else — every `lib/uuid` slot gets a freshly-minted value. The bundled schema enforces RFC 4122 format strictly, so placeholder strings (`"a1"`, `"uuid-1"`, `"agg-uuid-001"`, …) fail pre-flight with `must be a UUID v4 (RFC 4122) — run \`mb uuid\` …`. The same rule applies to native template-tag `id`fields, parameter ids, and any other`format: "uuid"` slot.
|
|
290
|
-
|
|
291
|
-
Workflow when assembling an MBQL 5 body:
|
|
292
|
-
|
|
293
|
-
1. Count the `lib/uuid` slots you need (one per clause options object, plus aggregation-ref ↔ aggregation pairings — those two share the same string).
|
|
294
|
-
2. `mb uuid --count <N> --json` — mint exactly that many in one call.
|
|
295
|
-
3. Substitute each minted value into its slot as you build the JSON.
|
|
296
|
-
|
|
297
|
-
Aggregation-ref pairing: the `["aggregation", {options}, "<uuid>"]` ref's third arg must equal the target aggregation's own `lib/uuid` (string equality). Mint the aggregation's `lib/uuid` once, then reuse that _same minted value_ for the ref — that's the only legitimate "reuse" pattern, and it's intra-body, not across bodies or sessions.
|
|
298
|
-
|
|
299
|
-
### `card` — questions, models, metrics
|
|
300
|
-
|
|
301
|
-
```bash
|
|
302
|
-
mb card list --profile <n> --json
|
|
303
|
-
mb card get <id> --profile <n> --full --json
|
|
304
|
-
mb card query <id> --profile <n> --json --limit 50
|
|
305
|
-
mb card query <id> --profile <n> --export-format csv > /tmp/results.csv
|
|
306
|
-
mb card query <id> --profile <n> --export-format xlsx > /tmp/results.xlsx
|
|
307
|
-
mb card query <id> --profile <n> --parameters '[{"type":"category","value":"A","target":["variable",["template-tag","c"]]}]'
|
|
308
|
-
mb card create --file body.json --profile <n> --json
|
|
309
|
-
mb card update <id> --body '{"name":"renamed"}' --profile <n> --json
|
|
310
|
-
mb card update <id> --body '{"display":"bar"}' --profile <n> --json
|
|
311
|
-
mb card update <id> --body '{"archived":false}' --profile <n> --json # unarchive
|
|
312
|
-
mb card archive <id> --profile <n> # soft-delete; not undoable from the CLI
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
`--export-format csv|xlsx` bypasses the JSON envelope and streams the raw export — pipe to a file. There is no permanent-delete; `archive` is the only delete verb (and `update --body '{"archived":false}'` is the unarchive path).
|
|
316
|
-
|
|
317
|
-
**`card update <id>`** patches a partial subset of the create shape (`name`, `display`, `dataset_query`, `visualization_settings`, `description`, `archived`, `collection_id`, `dashboard_id`, `cache_ttl`, `parameters`, `parameter_mappings`, …). Only the keys you send are touched. If `dataset_query` is MBQL 5 (`lib/type: "mbql/query"`) it goes through the same pre-flight validation as `card create` and `mb query`; pass `--skip-validate` to bypass.
|
|
318
|
-
|
|
319
|
-
**MBQL 5 `dataset_query` is a _flat_ `mbql/query`, not a legacy envelope.** This is the most common authoring mistake — the legacy MBQL4 shape `{type:"query", database:N, query:{...}}` looks similar but the server _will silently double-wrap_ an MBQL5 body submitted that way (you'll see the second-level `stages` nested inside an outer empty stage on `card get`), and queries fail with `"Initial MBQL stage must have either :source-table or :source-card"`. The right shape:
|
|
320
|
-
|
|
321
|
-
```json
|
|
322
|
-
{
|
|
323
|
-
"name": "Total shipments",
|
|
324
|
-
"display": "scalar",
|
|
325
|
-
"collection_id": 8,
|
|
326
|
-
"dataset_query": {
|
|
327
|
-
"lib/type": "mbql/query",
|
|
328
|
-
"database": 2,
|
|
329
|
-
"stages": [
|
|
330
|
-
{
|
|
331
|
-
"lib/type": "mbql.stage/mbql",
|
|
332
|
-
"source-table": 190,
|
|
333
|
-
"aggregation": [["count", { "lib/uuid": "<mint via `mb uuid`>" }]]
|
|
334
|
-
}
|
|
335
|
-
]
|
|
336
|
-
},
|
|
337
|
-
"visualization_settings": {}
|
|
338
|
-
}
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
`dataset_query` is the mbql/query value itself — no `type:"query"` envelope, no `query:` nesting.
|
|
342
|
-
|
|
343
|
-
**MBQL 5 pre-flight on `card create` / `card update`:** when `dataset_query` has `lib/type: "mbql/query"`, the body is validated against the same schema as `mb query` before sending. On failure, exit 2 with the standard `{ ok, errors }` envelope on stdout. Legacy `dataset_query` shapes (MBQL 4, native) skip pre-flight. The pre-flight also rejects the double-wrap mistake above (MBQL 5 nested inside a legacy `{type:"query", query:…}` envelope) with a `ConfigError` pointing at the right shape — no `--skip-validate` will get that past pre-flight. Author MBQL 5 by fetching the schema via `mb query --print-schema` and iterating with `mb query --dry-run`. Pass `--skip-validate` to bypass the pre-flight on schema-shape disagreements and let the server be the authority.
|
|
344
|
-
|
|
345
|
-
**Visualization settings.** The valid keys for `visualization_settings` are scoped by the card's `display` value (`scalar`, `bar`, `line`, `area`, `combo`, `pie`, `table`, `pivot`, `row`, `waterfall`, `scatter`, `boxplot`, …). The CLI does not validate this object client-side — the schema lives in the **`metabase-representation-format`** skill, `spec.md` "Visualization Settings" section (graph / series / table / pivot / pie / scalar subsections, plus common `column_settings`). Load that skill if it isn't active when authoring viz keys. Common keys you'll reach for:
|
|
346
|
-
|
|
347
|
-
- `bar` / `line` / `area` / `combo` / `scatter` / `waterfall` / `row` / `boxplot`: `graph.dimensions`, `graph.metrics`, `graph.show_values`, `graph.x_axis.title_text`, `graph.y_axis.title_text`, `graph.show_goal`, `graph.goal_value`, `stackable.stack_type`, plus per-series settings (`series_settings`).
|
|
348
|
-
- `pie`: `pie.dimension`, `pie.metric`, `pie.show_total`, `pie.percent_visibility`, `pie.show_legend`.
|
|
349
|
-
- `scalar`: `scalar.prefix`, `scalar.suffix`, `scalar.decimals`, plus `column_settings` for number formatting on the displayed column.
|
|
350
|
-
- `table`: `table.columns` (order + visibility), `table.column_formatting` (conditional formatting), `column_settings` for per-column display.
|
|
351
|
-
- `pivot`: `pivot_table.column_split` (rows / columns / values), `pivot.show_row_totals`, `pivot.show_column_totals`.
|
|
352
|
-
|
|
353
|
-
Empty `{}` is always valid; defaults apply.
|
|
354
|
-
|
|
355
|
-
### `dashboard` — dashboards and dashcards
|
|
356
|
-
|
|
357
|
-
```bash
|
|
358
|
-
mb dashboard list --profile <n> --json
|
|
359
|
-
mb dashboard list --filter archived --profile <n> --json
|
|
360
|
-
mb dashboard get <id> --profile <n> --full --json # --full hydrates dashcards + tabs
|
|
361
|
-
mb dashboard cards <id> --profile <n> --json # list of dashcards on the dashboard
|
|
362
|
-
mb dashboard create --file body.json --profile <n> --json
|
|
363
|
-
mb dashboard create --body '{"name":"D","dashcards":[{"id":-1,"card_id":42,"row":0,"col":0,"size_x":12,"size_y":6}]}' --profile <n> --json
|
|
364
|
-
mb dashboard update <id> --body '{"name":"renamed"}' --profile <n> --json
|
|
365
|
-
mb dashboard update-dashcard <dashboard-id> <dashcard-id> --body '{"row":4,"col":2}' --profile <n> --json
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
A "dashcard" is a card placement on a dashboard — its own id, position (`row`/`col`), and size (`size_x`/`size_y`). Dashcards are nested inside the parent dashboard's response; the API has no per-dashcard endpoint, so dashcard edits round-trip through `PUT /api/dashboard/:id`.
|
|
369
|
-
|
|
370
|
-
A dashcard's `visualization_settings` overrides the underlying card's — same key list as the `card` section above. Dashcards can additionally set `click_behavior` for cell-level navigation; see the `metabase-representation-format` skill's "Click Behavior" subsection for that schema.
|
|
371
|
-
|
|
372
|
-
**`dashboard create` accepts `dashcards` and `tabs` in the body.** The create endpoint itself only sets dashboard metadata (name, description, collection, parameters); when the body carries `dashcards` or `tabs`, the CLI chains a `PUT /api/dashboard/:id` automatically and renders the hydrated dashboard back. The compact projection includes the resulting `dashcards` and `tabs` arrays (each entry projected to id / position / size / card_id / tab_id), so the agent can confirm the placements landed without a second call. Use `--full` to also see dashboard-level metadata (width, embedding flags, parameters, …). Use a negative id (`-1`, `-2`, …) for new dashcards.
|
|
373
|
-
|
|
374
|
-
**Card-reference pre-flight on `dashboard create` / `dashboard update`.** Before either command sends anything, every positive `card_id` referenced from `dashcards` is checked against `GET /api/card/:id` in parallel (de-duplicated per id). Cards that don't exist, are archived, or aren't readable fail pre-flight: the CLI writes a `{ok:false, errors:[{path, message}]}` envelope to stdout (one entry per offending dashcard, `path` = JSON pointer like `/dashcards/3/card_id`) and exits **2** with `dashboard card-reference pre-flight failed: N error(s) — fix the dashcard card_id values listed above` on stderr. No dashboard is created or modified on a pre-flight miss — this is the contract that eliminates orphan dashboards from chained creates. The pre-flight is non-bypassable: it queries live server state (no bundled schema), so there is no `--skip-validate` escape hatch. If pre-flight rejects something you believe is valid, the input is stale — `card list --json` to confirm, then re-author.
|
|
375
|
-
|
|
376
|
-
**Chained-PUT failures call out the orphan risk explicitly.** If the chained `PUT /api/dashboard/:id` fails after the `POST /api/dashboard` already created the row (rare with pre-flight, but possible on permission / 5xx / network mid-flight), the user-facing error becomes `dashboard <id> created but follow-up PUT /api/dashboard/<id> failed: <reason>; dashcards not applied`. Recovery: `mb dashboard get <id>` to confirm the empty row, then either `dashboard update <id> --body '{"dashcards":[...]}'` to retry the dashcards, or `dashboard update <id> --body '{"archived":true}'` to archive the orphan. Split-into-two recipe for debugging: `dashboard create` with a metadata-only body, then `dashboard update <id>` with the `dashcards` array — isolates which leg of the chain is at fault.
|
|
377
|
-
|
|
378
|
-
Two ways to edit dashcards:
|
|
379
|
-
|
|
380
|
-
- **`dashboard update <id> --body { "dashcards": [...] }`** — replaces the entire dashcard set. IDs in the array are kept (and updated to the values you send); IDs **absent** are deleted server-side. Use a negative id (`-1`, `-2`, …) for cards the server should create. You must include every existing dashcard you want to preserve.
|
|
381
|
-
- **`dashboard update-dashcard <dashboard-id> <dashcard-id>`** — patches a single dashcard's layout / settings without touching the others. Internally: GET dashboard → merge patch into the targeted dashcard → PUT the whole array. Safer than hand-rolling the full-array variant if you only meant to nudge one card.
|
|
382
|
-
|
|
383
|
-
`dashboard list` is a thin filter helper (`--filter all|mine|archived`; default `all`). The list endpoint omits `dashcards` / `tabs`; `dashboard get <id>` includes them as compact projections, and `dashboard get <id> --full` (or `dashboard cards <id>`) gives the full hydrated form.
|
|
384
|
-
|
|
385
|
-
Patch fields supported by `update-dashcard`:
|
|
386
|
-
|
|
387
|
-
| Field | Type |
|
|
388
|
-
| ------------------------ | ---------------------------------- |
|
|
389
|
-
| `row`, `col` | non-negative integer |
|
|
390
|
-
| `size_x`, `size_y` | positive integer |
|
|
391
|
-
| `dashboard_tab_id` | integer or `null` |
|
|
392
|
-
| `parameter_mappings` | array of parameter-mapping objects |
|
|
393
|
-
| `inline_parameters` | array of strings |
|
|
394
|
-
| `visualization_settings` | object |
|
|
395
|
-
|
|
396
|
-
Empty-object patches are rejected client-side before any network call.
|
|
397
|
-
|
|
398
|
-
### `snippet` — native query snippets (reusable SQL fragments)
|
|
399
|
-
|
|
400
|
-
```bash
|
|
401
|
-
mb snippet list --profile <n> --json
|
|
402
|
-
mb snippet list --archived --profile <n> --json # → ONLY archived (mutually exclusive with active)
|
|
403
|
-
mb snippet get <id> --profile <n> --full --json
|
|
404
|
-
mb snippet create --body '{"name":"active","content":"WHERE active = true"}' --profile <n> --json
|
|
405
|
-
mb snippet update <id> --body '{"name":"renamed"}' --profile <n> --json
|
|
406
|
-
mb snippet update <id> --body '{"archived":false}' --profile <n> --json # unarchive
|
|
407
|
-
mb snippet archive <id> --profile <n> # soft-delete
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
Hits `/api/native-query-snippet`. A snippet is a named, reusable piece of native (SQL) query text — referenced from cards via `{{snippet: Name}}`. **`--archived` is a swap, not a union**: list returns either active (default) or archived rows, never both. Compact projection: `id`, `name`, `description`, `archived`, `collection_id`. Create body required fields: `name`, `content`. Update body is partial — `name`, `content`, `description`, `archived`, `collection_id`.
|
|
411
|
-
|
|
412
|
-
### `segment` — saved MBQL filter macros
|
|
413
|
-
|
|
414
|
-
```bash
|
|
415
|
-
mb segment list --profile <n> --json
|
|
416
|
-
mb segment get <id> --profile <n> --full --json
|
|
417
|
-
mb segment create --file segment.json --profile <n> --json
|
|
418
|
-
mb segment update <id> --body '{"name":"renamed","revision_message":"rename"}' --profile <n> --json
|
|
419
|
-
mb segment archive <id> --profile <n> # default audit message
|
|
420
|
-
mb segment archive <id> --revision-message "deprecated" --profile <n> # custom audit message
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
Hits `/api/segment`. A segment is a saved MBQL filter macro tied to a table — used in card filters to share a reusable predicate. Create body required: `name`, `table_id`, `definition` (MBQL filter object), optional `description`. **Update bodies MUST include `revision_message`** (a non-blank string captured in the audit log); the CLI does not synthesize it. The `archive` verb hardcodes `"Archived via mb CLI"` by default — override with `--revision-message`.
|
|
424
|
-
|
|
425
|
-
Compact projection: `id`, `name`, `description`, `archived`, `table_id`. The list response is bare; only the get/list responses hydrate `creator` and (list-only) `definition_description`.
|
|
426
|
-
|
|
427
|
-
### `measure` — saved MBQL aggregation macros
|
|
428
|
-
|
|
429
|
-
```bash
|
|
430
|
-
mb measure list --profile <n> --json
|
|
431
|
-
mb measure get <id> --profile <n> --full --json
|
|
432
|
-
mb measure create --file measure.json --profile <n> --json
|
|
433
|
-
mb measure update <id> --body '{"name":"renamed","revision_message":"rename"}' --profile <n> --json
|
|
434
|
-
mb measure archive <id> --profile <n>
|
|
435
|
-
mb measure archive <id> --revision-message "deprecated" --profile <n>
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
Hits `/api/measure`. A measure is a saved MBQL aggregation (a single `:aggregation` clause) tied to a table — referenced from cards and metrics to share a reusable computation. Create body required: `name`, `table_id`, `definition` (MBQL aggregation object), optional `description`. Same `revision_message` requirement on update / archive as `segment`.
|
|
439
|
-
|
|
440
|
-
Compact projection: `id`, `name`, `description`, `archived`, `table_id`. The full response on `get` adds `dimensions`, `dimension_mappings`, `result_column_name`; the list response adds `definition_description` instead.
|
|
441
|
-
|
|
442
|
-
### `collection` — folder hierarchy for cards, dashboards, sub-collections
|
|
443
|
-
|
|
444
|
-
```bash
|
|
445
|
-
mb collection list --profile <n> --json
|
|
446
|
-
mb collection list --filter archived --profile <n> --json # → just the trash collection
|
|
447
|
-
mb collection list --filter personal --profile <n> --json # → only personal collections
|
|
448
|
-
mb collection get <ref> --profile <n> --json --full
|
|
449
|
-
mb collection items <ref> --profile <n> --json
|
|
450
|
-
mb collection items <ref> --models card,dashboard --pinned-state is_pinned --profile <n> --json
|
|
451
|
-
mb collection tree --profile <n> # → JSON only, recursive
|
|
452
|
-
mb collection create --body '{"name":"My Collection","parent_id":4}' --profile <n> --json
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
`<ref>` (the positional id on `get` and `items`) accepts **four** forms — anything else is rejected client-side with a `ConfigError` before any HTTP call:
|
|
456
|
-
|
|
457
|
-
| Form | Example | Notes |
|
|
458
|
-
| ----------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
459
|
-
| Positive integer | `4` | Database id of the collection. |
|
|
460
|
-
| `root` | `mb collection get root` | The virtual "Our analytics" root. Returns a stripped-down shape — `archived`, `description`, `location`, `type`, etc. are _absent_, not `null`. |
|
|
461
|
-
| `trash` | `mb collection get trash` | The trash collection — paradoxically returns `archived: false`, `type: "trash"`. Filter via `list --filter archived` to enumerate it. |
|
|
462
|
-
| 21-char entity_id | `voo1If9y8Sld0lXej6xl0` | NanoID form (regex `^[A-Za-z0-9_-]{21}$`). Works wherever an int does — Metabase resolves it server-side via the same route. |
|
|
463
|
-
|
|
464
|
-
**`collection items` is auto-paginated.** The CLI drains all pages of `/api/collection/:id/items` by default; pass `--limit <n>` to cap the total returned. With `--limit` set, the result envelope omits `total` (true total is unknown after early-stop). Items at the root level (`collection items root`) carry `collection_id: null`.
|
|
465
|
-
|
|
466
|
-
**`collection tree` is JSON-only.** The recursive `{id, name, location, here, children, …}` structure does not render meaningfully as a key/value table; passing `--format text` is rejected with `ConfigError` so the user gets a clear signal rather than silent JSON.
|
|
467
|
-
|
|
468
|
-
**Compact projection** (default for `list` / `get`): `id`, `name`, `description`, `archived`, `location`, `parent_id`, `type`, `authority_level`, `is_personal`. Use `--full` for hydrated fields like `slug`, `entity_id`, `can_write`, `namespace`, `personal_owner_id`. The compact projection on items is even tighter: `id`, `model`, `name`, `description`, `archived`, `collection_id`.
|
|
469
|
-
|
|
470
|
-
**`collection create` body** accepts the same fields as `POST /api/collection`: `name` (required, non-empty), `description`, `parent_id` (omit or `null` for the root), `namespace`, `authority_level`. Note: the create response does _not_ hydrate `parent_id` (only `location` reflects the parent path); use `collection get <id>` if you need `parent_id` populated.
|
|
471
|
-
|
|
472
|
-
For dashboard / card / collection enumeration, prefer the dedicated `collection list` / `dashboard list` / `card list` verbs over `mb search --models collection` — search is for ranking against a query string or cross-resource lookup, not bulk enumeration.
|
|
473
|
-
|
|
474
|
-
### `transform` and `transform-job`
|
|
475
|
-
|
|
476
|
-
```bash
|
|
477
|
-
mb transform list --profile <n> --json
|
|
478
|
-
mb transform run <id> --wait --profile <n> --json
|
|
479
|
-
mb transform runs --transform-id <id> --profile <n> --json # recent runs, optionally filtered
|
|
480
|
-
mb transform get-run <run-id> --profile <n> --json # single run by RUN id (not transform id)
|
|
481
|
-
mb transform cancel <id> --profile <n> --json # cancel the in-flight run for a transform
|
|
482
|
-
mb transform-job list --profile <n> --json
|
|
483
|
-
```
|
|
484
|
-
|
|
485
|
-
**MBQL 5 pre-flight on `transform create` / `update`:** when `source.query` has `lib/type: "mbql/query"`, it's validated against the same schema as `mb query` before sending; failures exit 2 with the standard `{ ok, errors }` envelope on stdout. Legacy `source.query` shapes and Python sources skip pre-flight. Pass `--skip-validate` to bypass.
|
|
486
|
-
|
|
487
|
-
**Iterate via `transform update`, not re-`create`.** When a `transform run` fails and you want to retry with a fixed body, patch the existing transform with `transform update <id> --file new-body.json` rather than `transform delete <id>` + `transform create`. Update keeps the same row, `entity_id`, materialized table, and on-disk YAML filename — `git-sync export` produces one clean commit, and you avoid the `_2` suffix the YAML serializer mints when two same-named transforms exist on disk. See the `transform` skill, "Iterating on a failing transform".
|
|
488
|
-
|
|
489
|
-
For the body shape, run-with-wait pattern, schedule authoring, and inspection load the `transform` skill via `mb skills get transform`.
|
|
490
|
-
|
|
491
|
-
### `setting` (alias `settings`) — admin settings
|
|
128
|
+
The leading `__` hides it from `--help`, but it's stable. Reach for it instead of `--help` per command. It pairs with `jq`:
|
|
492
129
|
|
|
493
130
|
```bash
|
|
494
|
-
mb
|
|
495
|
-
mb
|
|
496
|
-
mb
|
|
131
|
+
mb __manifest | jq -r '.commands[].command' # every command name
|
|
132
|
+
mb __manifest | jq -r '.commands[] | select(.command | startswith("transform")) | .command' # verbs under "transform"
|
|
133
|
+
mb __manifest | jq '.commands[] | select(.command == "card query") | .args' # flags + types for a command
|
|
134
|
+
mb __manifest | jq '.commands[] | select(.command == "card list") | .outputSchema' # output schema before parsing
|
|
497
135
|
```
|
|
498
136
|
|
|
499
|
-
|
|
137
|
+
Use it to (a) enumerate verbs, (b) validate flag names before constructing a command, (c) read an output schema before parsing.
|
|
500
138
|
|
|
501
|
-
|
|
139
|
+
## Resource quirks worth memorizing
|
|
502
140
|
|
|
503
|
-
|
|
141
|
+
Routine verb shapes (list / get / create / update), every flag, and output JSON Schemas live in `mb __manifest` — pull them on demand. Below is only what the manifest does _not_ tell you: the footguns and non-obvious behaviors.
|
|
504
142
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
mb
|
|
518
|
-
mb
|
|
519
|
-
mb git-sync export -m "commit message" --profile <n>
|
|
520
|
-
mb git-sync branches --profile <n> --json
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
14 verbs (status / is-dirty / has-remote-changes / dirty / current-task / cancel-task / wait / import / export / stash / branches / create-branch / add-collection / remove-collection). Both `import --force` and `export --force` are **lossy** — confirm with the user before either. `add-collection <id>` / `remove-collection <id>` toggle a collection's `is_remote_synced` and cascade to descendants by location prefix; the server rejects them in the default read-only mode (`mb setting set remote-sync-type '"read-write"'` first). For the dirty-check workflow, stash semantics, and the full collection-toggle prerequisites, load the `git-sync` skill via `mb skills get git-sync`.
|
|
524
|
-
|
|
525
|
-
### `workspace` — Enterprise workspaces (parent-side + local child)
|
|
526
|
-
|
|
527
|
-
Lifecycle, provisioning, child-credential extraction, diagnose. Load the `workspace` skill via `mb skills get workspace` — it's the densest reference and assumes the conventions above.
|
|
528
|
-
|
|
529
|
-
### `api-key` — create API keys
|
|
530
|
-
|
|
531
|
-
```bash
|
|
532
|
-
mb api-key create --body '{"name":"agent-demo","group_id":<id>}' --profile <n> --json
|
|
533
|
-
```
|
|
534
|
-
|
|
535
|
-
Admin-only. The response includes the unmasked key once — capture it; the API never reveals it again.
|
|
536
|
-
|
|
537
|
-
### `eid translate` — string EID → numeric id
|
|
538
|
-
|
|
539
|
-
```bash
|
|
540
|
-
mb eid translate <eid> --profile <n> --json
|
|
541
|
-
```
|
|
542
|
-
|
|
543
|
-
Useful when an external system gives you a string entity id (like `Nd3A2qlmFIOYa5UZpQdsL`) and you need the numeric id for `card query`, `transform run`, etc.
|
|
544
|
-
|
|
545
|
-
### `setup` — initial setup wizard
|
|
546
|
-
|
|
547
|
-
```bash
|
|
548
|
-
mb setup --file /path/to/setup-spec.json
|
|
549
|
-
```
|
|
550
|
-
|
|
551
|
-
Walks the `/api/setup` endpoint with a default user. **Don't run this against an instance the user already set up** — it errors out, and even successful runs are one-shot. Mostly useful for bootstrapping a fresh local instance (e2e harnesses).
|
|
143
|
+
- **db traversal vs. rollup.** Default to granular: `database list` → `database schemas <db-id>` → `database schema-tables <db-id> <schema>` → `table get <table-id> --include fields`. The rollup endpoints (`database get --include tables.fields`, `database metadata <db-id>`) pull megabytes and blow the context window on any real warehouse — use them only on a small/dev db. `sync-schema` / `rescan-values` queue async work and return `{status:"ok"}` immediately.
|
|
144
|
+
- **table fields.** `table get` never returns fields on its own — pass `--include fields` (compact) or use `table fields <id>` (list envelope). `table metadata <id>` adds FKs + dimensions (heavier). `table update` patches table-level metadata only; physical columns aren't editable here.
|
|
145
|
+
- **field has no `list`.** Fields are per-table — get them via `table get <id> --include fields`. Never enumerate fields across a whole db (context blow-up). `field summary` is live cardinality `{field_id, count, distincts}`; `field values` is the cached distinct set (`has_more_values: true` ⇒ truncated cache). `field update` patches metadata only; `base_type` isn't editable.
|
|
146
|
+
- **card.** `dataset_query` is the **flat** `mbql/query` value, not a legacy `{type:"query",query:…}` envelope (→ `mbql` skill). `--export-format csv|xlsx` streams the raw export (pipe to a file), bypassing the JSON envelope. `archive` is the only delete; unarchive with `update --body '{"archived":false}'`. `visualization_settings` keys are scoped by `display` and aren't pre-flighted — see the `viz` skill.
|
|
147
|
+
- **dashboard.** Dashcards round-trip through `PUT /api/dashboard/:id` (no per-dashcard endpoint): `update-dashcard <dash-id> <dashcard-id>` patches one safely; `update --body '{"dashcards":[…]}'` replaces the whole set (omitted ids are deleted server-side; use negative ids for new cards). `create`/`update` pre-flight every positive `card_id` against live server state and exit **2** with `{ok:false,errors:[…]}` on a bad ref — non-bypassable (no `--skip-validate`). `dashboard get <id>` (or `--full`) hydrates dashcards/tabs; `list` omits them.
|
|
148
|
+
- **snippet `--archived` is a swap, not a union** — list returns _either_ active _or_ archived rows, never both. (Same shape for `--filter archived` on dashboard/collection.)
|
|
149
|
+
- **segment / measure** `update` and `archive` require a non-blank `revision_message` (audit-logged); the CLI does not synthesize it on `update`. `archive` defaults to `"Archived via mb CLI"` — override with `--revision-message`. `definition` is a flat MBQL clause (→ `mbql` skill): segment = a filter, measure = exactly one aggregation.
|
|
150
|
+
- **collection `<ref>`** accepts four forms only — positive int, `root`, `trash`, or a 21-char entity_id — anything else is a client-side `ConfigError`. `collection items` auto-paginates (cap with `--limit`, which then omits `total`). `collection tree` is **JSON-only** — `--format text` is rejected.
|
|
151
|
+
- **setting set** parses the value as **strict JSON**: a string is `'"value"'` (inner quotes), booleans `true`/`false`, numbers bare. Wrong quoting silently errors — confirm with `setting get <key>` after. `setting get --json` works on every value type (it wraps bare-text responses into `{key, value}`).
|
|
152
|
+
- **search vs. list.** For plain enumeration of cards/dashboards/collections use the dedicated `… list` verbs; reach for `search --models <kind>` only for ranking against a query string or a cross-resource lookup.
|
|
153
|
+
- **transform.** Iterate with `transform update <id>`, never `delete` + `create` — keeps the row, `entity_id`, materialized table, and YAML filename (avoids `_2` suffixes and noisy git history). `transform run` needs `--wait` or you get only `{run_id, final:null}`. (→ `transform` skill.)
|
|
154
|
+
- **setup is one-shot.** `mb setup` walks `/api/setup` for a **fresh** instance only — it errors against an already-configured one. Mostly for bootstrapping local / e2e instances.
|
|
155
|
+
- **eid** translates a string entity id → numeric id: `mb eid --model <model> <eid1,eid2> --json` (EIDs are a positional used with `--model`; or pass `--body '{"entity_ids":{"card":["…"]}}'`). Useful when an external system hands you an entity id and a verb needs the numeric one.
|
|
156
|
+
- **query / uuid.** `mb query` is the ad-hoc MBQL surface (`--print-schema` → `--dry-run` → run); `mb uuid --count <n>` mints the `lib/uuid` values every MBQL 5 clause needs. Both workflows live in the `mbql` skill.
|
|
552
157
|
|
|
553
158
|
## Specialized skills (load on demand)
|
|
554
159
|
|
|
555
|
-
This core file is enough for any single-command task.
|
|
556
|
-
|
|
557
|
-
| Load this skill | When the user's intent matches |
|
|
558
|
-
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
559
|
-
| `mb skills get workspace` | "spin up a workspace", "provision", "start a local Metabase against my prod", anything `mb workspace …`. **Mandatory** before running `workspace start` — it tells you to ask the user about Remote Sync (current dir / custom path / none) up front, since the bind mount can only be set at container create. |
|
|
560
|
-
| `mb skills get transform` | "create a transform", "run a transform", authoring transform body JSON, run inspection |
|
|
561
|
-
| `mb skills get git-sync` | "import the latest changes", "export to git", "git sync", "dirty check", "stash before pulling" |
|
|
160
|
+
This core file is enough for any single-command task. Load the relevant skill **proactively** when intent matches — don't wing an MBQL body, the workspace lifecycle, a transform body, or the git-sync workflow from this overview alone. Load each via `mb skills get <name>`.
|
|
562
161
|
|
|
563
|
-
|
|
162
|
+
- **`mbql`** — authoring or fixing any MBQL query body: `mb query`, a card `dataset_query`, a transform `source.query`, a measure/segment `definition`, "aggregate and group by", reading `--dry-run` errors. The query-body reference.
|
|
163
|
+
- **`viz`** — choosing a card's `display` and authoring `visualization_settings`: "make it a bar chart", "set the pie dimension/metric", "format this column as currency", "the card renders as a table instead of a chart". The presentation counterpart to `mbql`.
|
|
164
|
+
- **`workspace`** — "spin up a workspace", "provision", "start a local Metabase against my prod", anything `mb workspace …`. **Mandatory** before `workspace start` — ask the user about Remote Sync up front (the bind mount is create-time only).
|
|
165
|
+
- **`transform`** — "create a transform", "run a transform", authoring transform body JSON, run inspection.
|
|
166
|
+
- **`git-sync`** — "import the latest changes", "export to git", "git sync", "dirty check", "stash before pulling".
|
|
564
167
|
|
|
565
|
-
`mb skills list` enumerates everything
|
|
168
|
+
If a task spans more than one, load each. Specialized skills assume the conventions above and won't repeat them. `mb skills list` enumerates everything on the installed version.
|
|
566
169
|
|
|
567
170
|
## Don't
|
|
568
171
|
|
|
569
|
-
- **Don't run `mb auth login` for the user
|
|
570
|
-
- Don't paste credentials, license tokens, or warehouse passwords in chat. Have the user run the storing command
|
|
571
|
-
- Don't put `--profile` before the verb chain — the CLI parses it as a
|
|
572
|
-
- Don't pass an API key with `--api-key "$KEY"`; pipe it on stdin via `--api-key-stdin`. (Comes up only in the workspace-child case.)
|
|
172
|
+
- **Don't run `mb auth login` for the user** — authentication is theirs (see §Auth). The only exception is saving a workspace child's credentials, and even there pipe the key on stdin.
|
|
173
|
+
- Don't paste credentials, license tokens, or warehouse passwords in chat. Have the user run the storing command.
|
|
174
|
+
- Don't put `--profile` before the verb chain — the CLI parses it as a subcommand and errors out.
|
|
573
175
|
- Don't omit `--wait` on `workspace start` / `transform run` / `workspace database provision` for interactive flows; the next step will race the operation.
|
|
574
176
|
- Don't drop a JSON-envelope verb's output raw into another flag. Extract with `--json | jq -r '.<field>'`.
|
|
575
|
-
- Don't add a third-party HTTP library or shell into `curl`
|
|
177
|
+
- Don't add a third-party HTTP library or shell into `curl` against `/api/...` when a `mb <verb>` exists — that bypasses retries, schema validation, and credential redaction.
|