src/templates/claude/skills/bonnard-metabase-migrate/SKILL.md
Guide migration from an existing Metabase instance to a Bonnard semantic layer. Use when user says "migrate from metabase", "import metabase", "metabase to semantic layer", or has Metabase data they want to model.
npx skillsauth add meal-inc/bonnard-cli bonnard-metabase-migrateInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill guides you through analyzing an existing Metabase instance and building a semantic layer that replicates its most important metrics. Walk through each phase in order, confirming progress before moving on.
Set up a connection to the Metabase instance:
bon metabase connect
This prompts for the Metabase URL and API key. The API key should be created in Metabase under Admin > Settings > Authentication > API Keys. An admin-level key gives the richest analysis (permissions, schema access).
Generate an intelligence report that maps the entire Metabase instance:
bon metabase analyze
This writes a report to .bon/metabase-analysis.md. Read it carefully — it
drives every decision in the remaining phases.
| Report Section | What It Tells You | Action |
|----------------|-------------------|--------|
| Most Referenced Tables | Tables used most in SQL queries | Create cubes for these first — they are the core of the data model |
| Top Cards by Activity | Most-viewed questions/models | analytical cards (GROUP BY + aggregation) map to measures; lookup cards indicate key filter dimensions; display cards can be skipped |
| Common Filter Variables | Template vars ({{var}}) used across 3+ cards | These must be dimensions on relevant cubes |
| Foreign Key Relationships | FK links between tables | Define joins between cubes using these relationships |
| Collection Structure | How users organize content by business area | Map each top-level collection to a view (one view per business domain) |
| Dashboard Parameters | Shared filters across dashboards | The most important shared dimensions — ensure they exist on relevant cubes |
| Table Inventory | Field counts and classification per table | Field classification (dims/measures/time) guides each cube definition; tables with 0 refs can be deprioritized |
| Schema Access | Which schemas non-admin groups can query | Focus on user-facing schemas — skip admin-only/staging schemas |
Add a datasource pointing to the same database that Metabase queries. The database connection details can often be found in Metabase under Admin > Databases, or in the analysis report header.
# Non-interactive (preferred for agents)
bon datasource add --name my_warehouse --type postgres \
--host db.example.com --port 5432 --database mydb --schema public \
--user myuser --password mypassword
# Import from dbt if available
bon datasource add --from-dbt
# Interactive setup (in user's terminal)
bon datasource add
Supported types: postgres, redshift, snowflake, bigquery, databricks, duckdb.
The connection will be tested automatically during bon deploy.
Before writing cubes, drill into the most important tables and cards identified in Phase 2. Use the explore commands to understand field types and existing SQL patterns:
# View table fields with type classification
bon metabase explore table <id>
# View card SQL and columns
bon metabase explore card <id>
# View schemas and tables in a database
bon metabase explore database <id>
# View cards in a collection
bon metabase explore collection <id>
| Explore Field | Cube Mapping |
|---------------|-------------|
| Field class pk | Set primary_key: true on dimension |
| Field class fk | Join candidate — note the target table |
| Field class time | Dimension with type: time |
| Field class measure | Measure candidate — check card SQL for aggregation type |
| Field class dim | Dimension with type: string or type: number |
Look at the SQL in analytical cards to determine measure types:
| Card SQL Pattern | Cube Measure |
|-----------------|-------------|
| SUM(amount) | type: sum, sql: amount |
| COUNT(*) | type: count |
| COUNT(DISTINCT user_id) | type: count_distinct, sql: user_id |
| AVG(price) | type: avg, sql: price |
| MIN(date) / MAX(date) | type: min / type: max, sql: date |
Use bon docs cubes.measures.types for all 12 measure types.
Create cubes for the most-referenced tables (from Phase 2). Start with the
highest-referenced table and work down. Create one file per cube in
bonnard/cubes/.
For each cube:
sql_table to the full schema.table pathdata_source to the datasource name from Phase 3primary_key dimension — must be unique. If no column is naturally unique, use sql with ROW_NUMBER() instead of sql_table and add a synthetic keydescription to every measure and dimension — descriptions should say what's included and excluded. Dimension descriptions should include example values for categorical fields.Example — bonnard/cubes/orders.yaml:
cubes:
- name: orders
sql_table: public.orders
data_source: my_warehouse
description: Order transactions
measures:
- name: count
type: count
description: Total number of orders
- name: total_revenue
type: sum
sql: amount
description: Sum of order amounts
dimensions:
- name: id
type: number
sql: id
primary_key: true
- name: created_at
type: time
sql: created_at
description: Order creation timestamp
- name: status
type: string
sql: status
description: Order status (pending, completed, cancelled)
Use FK relationships from the analysis report to define joins between cubes:
joins:
- name: customers
sql: "{CUBE}.customer_id = {customers.id}"
relationship: many_to_one
Use bon docs cubes.joins for the full reference.
Map Metabase collections to views. Each top-level collection (business domain)
from the analysis report becomes a view that composes the relevant cubes.
Name views by what they answer (e.g., sales_pipeline), not by what
table they wrap (e.g., orders_view).
Create one file per view in bonnard/views/.
The view description is critical — it's how AI agents decide which view to query. It should answer: what's in here, when to use it, and when to use something else instead. Cross-reference related views to prevent agents from picking the wrong one.
Example — bonnard/views/sales_analytics.yaml:
views:
- name: sales_analytics
description: >-
Sales team view — order revenue, counts, and status breakdowns with
customer region. Default view for revenue and order questions. Use the
status dimension (values: pending, completed, cancelled) to filter.
For customer-level analysis, use customer_insights instead.
cubes:
- join_path: orders
includes:
- count
- total_revenue
- created_at
- status
- join_path: orders.customers
prefix: true
includes:
- name
- region
Use bon docs views for the full reference. See /bonnard-design-guide
for principles on view naming, descriptions, and structure.
Validate the semantic layer:
bon validate
Fix any errors. Common issues:
primary_key dimensiondata_sourceThen deploy:
bon login
bon deploy -m "Migrate semantic layer from Metabase"
Compare results from the semantic layer against Metabase card outputs.
Pick 3-5 important analytical cards from the analysis report and run
equivalent queries:
# Run a semantic layer query
bon query '{"measures": ["sales_analytics.total_revenue"], "dimensions": ["sales_analytics.status"]}'
# SQL format
bon query --sql "SELECT status, MEASURE(total_revenue) FROM sales_analytics GROUP BY 1"
Compare the numbers with the corresponding Metabase card. If they don't match:
bon metabase explore card <id>) for filters or transformationsTest with natural language too. Set up MCP (bon mcp) and ask an agent
the same questions your Metabase dashboards answer. Check whether it picks
the right view and measure — most failures are description problems,
not data problems. See /bonnard-design-guide Principle 6.
After the core migration is working:
/bonnard-design-guide)bon docs cubes.measures.calculated)bon mcp)bon deployments and bon diff <id>documentation
Guide a user through setting up their first semantic layer after bon init. Use when user says "get started", "what next", "help me set up", or has just run bon init.
development
Design principles for building semantic layers that work well for AI agents and business users. Use when building views, writing descriptions, or improving agent accuracy.
development
Guide a user through building and deploying a markdown dashboard. Use when user says "build a dashboard", "create a chart", "visualize data", or wants to create a dashboard.
documentation
Fetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: /gh-issues [owner/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352]