add-malli-schemas
Efficiently add Malli schemas to API endpoints in the Metabase codebase with proper patterns, validation timing, and error handling
The Metabase-flavored Markdown grammar for document_write — the CommonMark subset, {% card %} embeds, {% entity %} links, ::: layout containers (flex / supporting / resize) and nesting rules, surgical `edits` and their limits, card clones, comments. Triggers — "create a document
$ npx -y skills add metabase/metabase --skill documents --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/documentsContext preview
The summary Claude sees to decide when to auto-load this skill.
The Metabase-flavored Markdown grammar for document_write — the CommonMark subset, {% card %} embeds, {% entity %} links, ::: layout containers (flex / supporting / resize) and nesting rules, surgical `edits` and their limits, card clones, comments. Triggers — "create a document
name: documents
description: The Metabase-flavored Markdown grammar for document_write — the CommonMark subset, {% card %} embeds, {% entity %} links, ::: layout containers (flex / supporting / resize) and nesting rules, surgical `edits` and their limits, card clones, comments. Triggers — "create a document / report", "put prose beside a chart", "embed a question in a document", "edit a document", "document comments".A document is a rich-text page mixing prose with embedded saved questions. `document_write` takes **Metabase-flavored Markdown**: CommonMark plus tokens. Charts must exist first (`question_write`), embedded by id.
document_write {"method": "create", "name": "Weekly report",
"content_markdown": "# Weekly report\n\nOrders trended up this week.\n\n{% card id=118 %}\n"}**CommonMark subset:** headings, paragraphs, bold/italic/inline code, links (bare URLs autolink), lists, blockquotes, fenced code, horizontal rules, images. **No tables, strikethrough, or task lists** — the editor has no nodes for them; show tabular data by embedding a table-display question.
`::: <name> {attrs}` on its own line opens a container; a bare `:::` closes the **innermost** open one (never `::: end`) — every opener needs its own closer, in order.
Prose beside a chart:
::: flex {columns=[60,40]}
::: supporting
### What happened
Revenue climbed through the quarter, led by the Gadget category.
:::
{% card id=118 %}
:::(First `:::` closes `supporting`, last closes `flex`.) To pin the row's height, wrap it in `::: resize {height=400}` … `:::`.
A card the document doesn't already own is **cloned into it** on write and its id rewritten in the stored body — the markdown stored is not the markdown you sent, so always take the returned `content_markdown` as current, and edit against that, never your own earlier text. The clone is what renders: to change an embedded chart (display, settings, query), `question_write` the **clone id** shown in `content_markdown`; the master card does nothing for the document. E.g. you embedded 148, the body now reads `{% card id=155 %}` — `question_write {"method": "update", "id": 155, "display": "row"}` changes the document, updating 148 does not.
Pass `id` and exactly **one** of:
Writes are last-write-wins; a stale `old_str` failing to match is the only staleness signal.
**What `edits` can do:** `new_str` is plain text, stored literally — `**bold**`, `` `code` ``, `- item`, `## Heading`, fences, and `{% card %}` inside it appear as those characters, not formatting. A blank line (`\n\n`) does split a paragraph. So edits cover rewording any paragraph, heading, or list item, and adding a plain paragraph; a new heading, bullet, code block, embed, or container needs `content_markdown`.
Add a paragraph — extend the end of the block before it:
"edits": [{"old_str": "led by the Gadget category.",
"new_str": "led by the Gadget category.\n\nGizmos were flat for the third quarter running."}]Edit one bullet — match its text only, never the `- ` marker (a marker in `new_str` is escaped and merges two items into one):
"edits": [{"old_str": "Churn fell to 4%", "new_str": "Churn fell to 3.8%"}]`get_content {"type": "document", "id": 12, "include": ["comments"]}` returns threads grouped by anchored block, each with `anchor: {start, end, text}` — the exact character slice of the returned `content_markdown` — and `thread` messages (`id`, `creator`, `text`, `is_resolved`, `created_at`). A thread whose block was rewritten or deleted appears under `orphaned_comments`: the discussion still exists but points at no text, and only the UI can re-anchor it. MCP can read comments but not reply, resolve, or re-anchor — tell the user when a thread needs that.
`get_content {"type": "document", "id": 12}` returns the stored `content_markdown` (clone ids included); add `"inclu
Metabase is the easy, open-source way for everyone in your company to ask questions and learn from data.
Repo: metabase/metabase
Efficiently add Malli schemas to API endpoints in the Metabase codebase with proper patterns, validation timing, and error handling
Add OpenTelemetry tracing spans to Clojure code following Metabase tracing conventions. Use when instrumenting backend code with trace coverage.
Add product analytics events to track user interactions in the Metabase frontend
Evaluate Clojure code via nREPL using clj-nrepl-eval. Use this when you need to test code, check if edited files compile, verify function behavior, or interact…
Review Clojure and ClojureScript code changes for compliance with Metabase coding standards, style violations, and code quality issues. Use when reviewing pull…
Guide Clojure and ClojureScript development using REPL-driven workflow, coding conventions, and best practices. Use when writing, developing, or refactoring…