CursorPool
← 返回首页

Metabase

Query and explore your Metabase data

cursor.directory·2
MCP

metabase

MCP server: metabase

{
  "url": "{METABASE_INSTANCE_PLACEHOLDER}/api/mcp"
}
Skill

metabase-react-sdk-setup

First-time setup for the Metabase React SDK — instance detection, API key, dashboard discovery, JWT auth, SDK installation, and initial embedding code.

Use this skill for any task involving `@metabase/embedding-sdk-react` — whether that's initial setup, embedding dashboards, theming, or plugins.

**Communication style**: Be concise. Do one step at a time. When asking the user for input, output only the question — do not explain upcoming steps, implementation details, or what you plan to do next. The user does not need a roadmap.

> **CRITICAL — YOU MUST GET AN API KEY BEFORE DOING ANYTHING ELSE**
>
> Step 1 asks the user for a Metabase URL and API key. You CANNOT proceed without both.
> Do NOT detect the Metabase version, fetch `llms.txt`, install packages, or write ANY code until the user has given you an API key.
> Do NOT attempt to call any Metabase API endpoint without an API key — it will return 401 and you will be guessing.
> If any Metabase API call returns 401, STOP everything and ask the user for an API key.

## Step 1 — Get the Metabase URL and API key

You need a Metabase instance URL and an admin API key before anything else.

**`.env.metabase` is only for admin tasks within this skill** (API calls to Metabase). It is NOT the app's runtime config. Never import, read, or reference `.env.metabase` from the user's application code or build config. The app's instance URL goes in the user's own `.env` file (e.g., `VITE_METABASE_URL`, `NEXT_PUBLIC_METABASE_URL`) — set that up in Step 4.

Check if `.env.metabase` exists in the project root and already has both `METABASE_INSTANCE_URL` (non-empty) and `METABASE_ADMIN_API_KEY` (non-empty). If so, skip to Step 2.

Otherwise, create the file and gitignore it:

```bash
grep -qxF '.env.metabase' .gitignore 2>/dev/null || echo '.env.metabase' >> .gitignore
printf 'METABASE_INSTANCE_URL=\nMETABASE_ADMIN_API_KEY=\n' > .env.metabase
```

Then output **only this message** — no preamble, no explanation of what comes next, no implementation details:

> I created `.env.metabase` in the project root. Please fill in both values:
>
> 1. Set `METABASE_INSTANCE_URL` to your Metabase URL (e.g. `http://localhost:3000`)
> 2. Open `{your URL}/admin/settings/authentication/api-keys`, create a new API key
> 3. Set `METABASE_ADMIN_API_KEY` to that key
> 4. Let me know when you're done

Do **not** guess or assume the instance URL. Do not pre-fill `localhost:3000`. Do not ask the user to paste the key in the chat — it should only go in `.env.metabase`. Wait for the user to confirm, then proceed to Step 2.

## Step 2 — Detect version and discover dashboards

Now that you have an API key, detect the version and find dashboards.

### 2a — Detect version

```bash
source .env.metabase && \
  curl -s "$METABASE_INSTANCE_URL/api/session/properties" \
    -H "X-API-Key: $METABASE_ADMIN_API_KEY" | grep -o '"tag":"[^"]*"'
```

Parse both the **edition** and the **major version** from the tag:

| Tag format | Edition         | Example                     |
| ---------- | --------------- | --------------------------- |
| `v0.X.Y`   | OSS (Community) | `v0.60.1` → major `60`, OSS |
| `v1.X.Y`   | Enterprise (EE) | `v1.60.1` → major `60`, EE  |

If major version < 49, tell the user the Embedding SDK requires Metabase 49+ and stop.

**If the tag starts with `v1.`, the instance is Enterprise Edition — use full JWT SSO embedding.** Do not fall back to guest embedding or any OSS-only auth path.

Remember the major version number — you will need it in Step 3.

### 2b — Enable the Embedding SDK

Automatically enable the SDK so the user doesn't have to toggle it manually in the admin panel:

```bash
source .env.metabase && \
  curl -s -X PUT "$METABASE_INSTANCE_URL/api/setting/enable-embedding-sdk" \
    -H "X-API-Key: $METABASE_ADMIN_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"value": true}'
```

If this returns an error (e.g., 403), tell the user to enable it manually at **<INSTANCE_URL>/admin/settings/embedding** and move on.

**Do NOT fetch `llms.txt` yet.** You need dashboard IDs first.

### 2c — Find dashboards and table candidates

Run both of these:

```bash
source .env.metabase && \
  curl -s "$METABASE_INSTANCE_URL/api/search?models=dashboard&archived=false" \
    -H "X-API-Key: $METABASE_ADMIN_API_KEY"
```

```bash
source .env.metabase && \
  curl -s "$METABASE_INSTANCE_URL/api/automagic-dashboards/database/1/candidates" \
    -H "X-API-Key: $METABASE_ADMIN_API_KEY"
```

Filter and prioritize the results:

- **Exclude** any dashboards from the "Usage analytics" collection — those are internal Metabase admin dashboards, not user content.
- **Deprioritize** anything from the "Sample Database" — prefer the user's own databases and dashboards.
- Pick the **top 5** most relevant to what the user asked for from each category. Do not dump every result.

Format like this:

> **Existing dashboards:**
>
> 1. Sales Overview (ID 3)
> 2. Customer Analysis (ID 7)
>    ...
>
> **Or I can create a new dashboard from your data:**
> A. Orders table
> B. Products table
> ...
>
> Which ones should I embed? (e.g. "1 and 3" or "A")

Wait for the user to pick. If they choose a table, generate and save the X-ray dashboard:

```bash
source .env.metabase && \
  DASHBOARD=$(curl -s "$METABASE_INSTANCE_URL/api/automagic-dashboards/table/<TABLE_ID>" \
    -H "X-API-Key: $METABASE_ADMIN_API_KEY")

source .env.metabase && \
  curl -s "$METABASE_INSTANCE_URL/api/dashboard/save" \
    -H "X-API-Key: $METABASE_ADMIN_API_KEY" \
    -H "Content-Type: application/json" \
    -d "$DASHBOARD"
```

The save response contains the persisted dashboard with a real `id` field. Use these IDs going forward.

## Step 3 — Fetch docs, set up auth, and install SDK

You MUST have real dashboard IDs before reaching this step. If you don't, go back to Step 2.

**Now** fetch the versioned docs index using the major version from Step 2a:

```bash
curl -s https://www.metabase.com/docs/v0.<MAJOR>/llms.txt
```

Fall back to `https://www.metabase.com/docs/latest/llms.txt` if empty. This contains correct prop names, auth config shapes, SDK install commands, and breaking changes for this version. Do not fetch `llms-embedding-full.txt` (too large).

### 3a — Set up JWT SSO authentication (skip if already done)

Follow the auth setup instructions in `llms.txt`. In particular:

- Retrieve the JWT signing secret from Metabase: **Settings → Admin → Embedding → Embedding secret key**
- Tell the user to save it as `METABASE_JWT_SECRET` in their server-side environment only (never a browser-accessible env var)
- Ask which backend framework they are using (Next.js API route, Express, Fastify, etc.) and scaffold a minimal JWT signing endpoint following the pattern in `llms.txt`

### 3b — Install the SDK (skip if already installed at correct version)

Check whether `@metabase/embedding-sdk-react` is already in the user's `package.json`.

- If not installed: use the install command from `llms.txt` (the correct dist-tag matches the instance major version).
- If already installed: verify the major version matches. Warn on mismatch and offer to update.

## Step 4 — Generate embedding code

Use `llms.txt` as the authoritative reference for all API shapes. **Write files directly into the user's project** — edit existing files in place rather than creating new ones alongside them.

### Code conventions (override anything in the docs)

- **JWT SSO only**: API keys grant admin-level access and are not safe for end-user embeds. Use a server-side JWT signing endpoint; `MetabaseProvider` receives its URL. Never generate `apiKey`, `METABASE_API_KEY`, `api-key`, or `x-api-key` — not even as a placeholder. Deviate only if the user explicitly asks and acknowledges the security risk.
- **Instance URL from env**: `VITE_METABASE_URL` (Vite), `NEXT_PUBLIC_METABASE_URL` (Next.js), etc. Never hardcode.
- **Dashboard IDs as inline literals**: always hardcode dashboard IDs directly in JSX — e.g. `<InteractiveDashboard dashboardId={7} />`. Dashboard IDs are not secrets. **Never** use `import.meta.env.VITE_METABASE_DASHBOARD_*`, env vars, config objects, `parseDashboardId` helpers, or any indirection for dashboard IDs. The goal is clean, minimal code the user can instantly understand and tweak.
- **Secrets server-side only**: JWT secrets must never appear in browser-accessible env vars or frontend code.

### Theming

After generating the embedding code, inspect the user's app for existing styles — look at CSS variables, Tailwind config, or theme files. Set the `theme` prop on `MetabaseProvider` to match the app's look and feel. At minimum, align:

- `colors.brand` — the app's primary/accent color
- `colors.background` — to match the page background so the embed doesn't look like a white box on a dark page (or vice versa)
- `fontFamily` — to match the app's font

Refer to `llms.txt` for the full theme shape. Keep it minimal — only set values that differ from Metabase defaults.
Skill

setup-metabase-instance

Set up and run a local Metabase instance. Downloads the JAR (if Java 21+ is available) or runs via Docker. Also handles stopping running instances.

# Set Up a Local Metabase Instance

This skill helps users run a local Metabase instance for development, testing, or exploration.

## Important: Network Access Required

This skill requires network access. All `curl`, `java`, and `docker` commands must be run outside the Cursor sandbox. Request full network access or run outside the sandbox before attempting these commands. Do not run them inside the sandbox as they will fail.

## Prerequisites Check

Run these checks in order. Stop at the first successful path.

### 1. Check for Java 21+

Expand PATH first to avoid the macOS stub at `/usr/bin/java`:

```bash
export PATH=”/opt/homebrew/opt/openjdk/bin:/opt/homebrew/opt/openjdk@21/bin:/opt/homebrew/bin:/usr/local/opt/openjdk/bin:/usr/local/opt/openjdk@21/bin:/usr/local/bin:$PATH”
java -version 2>&1 | head -1
```

If the output shows Java 21 or higher → use Section A (JAR). Keep this `PATH` export for all subsequent commands.
If not found or version < 21 → check Docker (step 2).

### 2. Check for Docker

```bash
docker --version 2>&1
```

**If Docker is available**: Use the Docker method (Section B).

**If neither Java 21+ nor Docker is available**: Direct the user to install Docker:

- macOS/Windows: [Docker Desktop](https://www.docker.com/products/docker-desktop/)
- Linux: [Docker Engine](https://docs.docker.com/engine/install/)

Tell them to re-run this skill after installing Docker.

---

## Section A: JAR Method (Java 21+)

### A1. Check for existing Metabase directory

```bash
ls -la ./metabase 2>/dev/null
```

If `./metabase` exists and contains files, ask the user:

- "A `./metabase` directory already exists. Should I use it (preserving existing data) or remove it and start fresh?"

If the user wants to start fresh:

```bash
rm -rf ./metabase
```

### A2. Create directory and download JAR

```bash
mkdir -p ./metabase
```

Get the latest OSS release URL and download:

```bash
curl -sL -o ./metabase/metabase.jar https://downloads.metabase.com/latest/metabase.jar
```

Tell the user this may take a minute (the JAR is ~400MB).

### A3. Check if port 3000 is in use

```bash
lsof -i :3000 2>/dev/null | grep LISTEN
```

If the port is in use, ask the user:

- "Port 3000 is already in use. Would you like to use a different port?"
- Suggest port 3001, 3002, etc.

Store the chosen port as `$PORT` (default: 3000).

### A4. Start Metabase in the background

Use the same `PATH` as in the Java prerequisite step when the agent uses a fresh shell (prepend the macOS Homebrew line again if unsure). Optionally set `JAVA_CMD=$(command -v java)` after that export so you invoke the same binary you version-checked.

```bash
export PATH="/opt/homebrew/opt/openjdk/bin:/opt/homebrew/opt/openjdk@21/bin:/opt/homebrew/bin:/usr/local/opt/openjdk/bin:/usr/local/opt/openjdk@21/bin:/usr/local/bin:$PATH"
cd ./metabase && \
  MB_DB_FILE=./metabase.db \
  MB_JETTY_PORT=$PORT \
  nohup java -jar metabase.jar > metabase.log 2>&1 &
echo $! > metabase.pid
```

Tell the user: "Metabase is starting in the background. I'll check when it's ready..."

Also mention:

- "View logs: `tail -f ./metabase/metabase.log`"
- "The process ID is saved in `./metabase/metabase.pid`"

### A5. Wait for Metabase to be ready

Poll the health endpoint every 5 seconds until it returns `{"status":"ok"}`:

```bash
curl -s http://localhost:$PORT/api/health
```

Keep polling until the response is `{"status":"ok"}`. Metabase usually starts within 30-60 seconds.

If the health check keeps failing after 2 minutes, check if the process is still running:

```bash
ps -p $(cat ./metabase/metabase.pid 2>/dev/null) > /dev/null 2>&1 && echo "Running" || echo "Not running"
tail -50 ./metabase/metabase.log
```

Once healthy, tell the user: "Metabase is ready at `http://localhost:$PORT`"

---

## Section B: Docker Method

### B1. Check for existing Metabase directory

```bash
ls -la ./metabase 2>/dev/null
```

If `./metabase` exists and contains files, ask the user:

- "A `./metabase` directory already exists. Should I use it (preserving existing data) or remove it and start fresh?"

If the user wants to start fresh:

```bash
rm -rf ./metabase
```

### B2. Create directory for data persistence

```bash
mkdir -p ./metabase
```

### B3. Check if port 3000 is in use

```bash
lsof -i :3000 2>/dev/null | grep LISTEN
```

If the port is in use, ask the user for an alternative port. Store as `$PORT` (default: 3000).

### B4. Check for existing Metabase container

```bash
docker ps -a --filter "name=metabase-local" --format "{{.Names}} {{.Status}}"
```

If a container named `metabase-local` exists:

- If running: Ask if they want to stop it and start fresh, or keep using it
- If stopped: Ask if they want to remove it and start fresh, or restart it

To remove an existing container:

```bash
docker rm -f metabase-local 2>/dev/null
```

### B5. Get the latest Metabase version

The `latest` tag on Docker Hub is often outdated. Get the actual latest version from GitHub:

```bash
curl -s https://api.github.com/repos/metabase/metabase/releases/latest | grep '"tag_name"' | head -1
```

This returns something like `"tag_name": "v0.52.5"`. Extract the version (e.g., `v0.52.5`).

Verify the Docker image exists:

```bash
docker manifest inspect metabase/metabase:$VERSION 2>&1 | head -5
```

If it doesn't exist, fall back to `latest`.

### B6. Start Metabase container

```bash
docker run -d \
  --name metabase-local \
  -p $PORT:3000 \
  -v "$(pwd)/metabase:/metabase.db" \
  -e MB_DB_FILE=/metabase.db/metabase.db \
  -e MB_JETTY_HOST=0.0.0.0 \
  -e MB_ENABLE_EMBEDDING_SDK=true \
  -e MB_ENABLE_EMBEDDING_SIMPLE=true \
  metabase/metabase:$VERSION
```

Tell the user: "Metabase is starting via Docker. I'll check when it's ready..."

### B7. Wait for Metabase to be ready

Poll the health endpoint every 5 seconds until it returns `{"status":"ok"}`:

```bash
curl -s http://localhost:$PORT/api/health
```

Keep polling until the response is `{"status":"ok"}`. Metabase usually starts within 30-60 seconds.

If the health check keeps failing after 2 minutes, check the container status and logs:

```bash
docker ps --filter "name=metabase-local" --format "{{.Status}}"
docker logs metabase-local 2>&1 | tail -50
```

Once healthy, tell the user:

- "Metabase is ready at `http://localhost:$PORT`"
- "View logs: `docker logs -f metabase-local`"

---

## Next Steps: MCP Setup

Once the health check passes and Metabase is ready, ask the user:

"Would you like to set up the Metabase MCP so you can query your data directly from the IDE?"

- If they agree, invoke the `setup-metabase-mcp` skill. The MCP setup will:
  - Configure the MCP server with the local instance URL (`http://localhost:$PORT`)
  - Guide them through authentication
  - Enable querying tables, metrics, and dashboards from the IDE

- If they decline, let them know they can set up the MCP later by asking for "Metabase MCP setup".

---

## Stopping Metabase

When the user asks to stop Metabase, determine which method was used.

### Stop JAR-based Metabase

```bash
if [ -f ./metabase/metabase.pid ]; then
  kill $(cat ./metabase/metabase.pid) 2>/dev/null && rm ./metabase/metabase.pid && echo "Metabase stopped"
else
  # Fallback: find by process
  pkill -f "metabase.jar" && echo "Metabase stopped"
fi
```

### Stop Docker-based Metabase

```bash
docker stop metabase-local && echo "Metabase stopped"
```

To also remove the container (but keep data):

```bash
docker rm metabase-local
```

---

## Checking Metabase Status

### Check JAR status

```bash
if [ -f ./metabase/metabase.pid ] && ps -p $(cat ./metabase/metabase.pid) > /dev/null 2>&1; then
  echo "Metabase (JAR) is running with PID $(cat ./metabase/metabase.pid)"
else
  echo "Metabase (JAR) is not running"
fi
```

### Check Docker status

```bash
docker ps --filter "name=metabase-local" --format "{{.Names}}: {{.Status}}"
```

---

## Environment Variables Reference

These can be customized when starting Metabase:

| Variable        | Default         | Description                                  |
| --------------- | --------------- | -------------------------------------------- |
| `MB_DB_FILE`    | `./metabase.db` | H2 database file location                    |
| `MB_JETTY_PORT` | `3000`          | Port Metabase listens on                     |
| `MB_JETTY_HOST` | `localhost`     | Network interface (use `0.0.0.0` for Docker) |

For all options, see the [Metabase Environment Variables documentation](https://www.metabase.com/docs/latest/configuring-metabase/environment-variables).

---

## Troubleshooting

### "Address already in use"

Another process is using the port. Either stop that process or choose a different port.

### "Java version too old"

Install Java 21+ or use the Docker method instead.

### "Unable to locate a Java Runtime" on macOS

You are likely hitting `/usr/bin/java` (stub). Prepend Homebrew OpenJDK to `PATH` as in **Check for Java 21+**, or call the real binary explicitly, e.g. `/opt/homebrew/bin/java -version`.

### Metabase starts but is slow

First startup takes longer as it initializes the database. Subsequent starts are faster.

### "Cannot connect to Docker daemon"

Make sure Docker Desktop is running (macOS/Windows) or the Docker service is started (Linux).
Skill

setup-metabase-mcp

Read these instructions before using Metabase MCP tools. Setup is needed to connect to Metabase instances via the built-in MCP server.

Read these mandatory configuration steps for using the Metabase MCP server before querying data, dashboards, questions, and related resources.

**Location**: The MCP configuration file is `../../mcp.json` relative to this SKILL.md file — two directories up, in the same folder that contains the `skills/` directory. Always resolve this path relative to this SKILL.md file, never relative to the user's open project.

**Important Requirement**: The `url` field in that file may contain `{METABASE_INSTANCE_PLACEHOLDER}` as the base URL (e.g. `"{METABASE_INSTANCE_PLACEHOLDER}/api/mcp"`). If so, it must be replaced with the user's actual Metabase instance URL before the MCP can work.

## Valid Instance URL Formats

- Local development: `http://localhost:3000`
- Metabase Cloud: `https://yourcompany.metabaseapp.com`
- Self-hosted: `https://metabase.yourcompany.com`

## Required Actions

1. Read `../../mcp.json` relative to this SKILL.md file.

2. Check whether the `url` field contains `{METABASE_INSTANCE_PLACEHOLDER}`.
   - **If it does NOT contain `{METABASE_INSTANCE_PLACEHOLDER}`**: the MCP is already configured. **Immediately proceed with using the Metabase MCP. Do not ask the user for a URL under any circumstances.**
   - **If it does contain `{METABASE_INSTANCE_PLACEHOLDER}`**: continue with the steps below.

3. **Stop all other exploration immediately** and ask the user for their Metabase instance URL. Do not search for tool schemas, read other files, or do anything else first.

   **Never mention `{METABASE_INSTANCE_PLACEHOLDER}` or any placeholder to the user**. Simply say the MCP needs their Metabase URL to connect.

   Ask the user: "Do you have a Metabase instance URL, or would you like to set up a local instance?"
   - **If they provide a URL**: Continue with step 4.
   - **If they don't have one and want to set up a local instance**: Invoke the `setup-metabase-instance` skill. Once the local instance is running, return here and use `http://localhost:3000` (or whatever port was chosen) as the instance URL.

4. Once the user provides the URL, run **exactly this command and no other** — do not try alternative endpoints or approaches:

   ```bash
   curl -s <INSTANCE_URL>/api/session/properties | grep -o '"tag":"[^"]*"'
   ```

   This returns something like `"tag":"v1.60.0"`. Extract the major version number (e.g. `60` from `v1.60.0`). If it is below 60, tell the user they need to upgrade and stop — do not update `mcp.json`.

5. Replace the placeholder in `../../mcp.json` relative to this SKILL.md with the user's instance URL. Strip any trailing slash before saving — do not mention this to the user.

6. Only after updating the file, tell the user they need to reload the window.

   Use the user's current OS to show the right shortcut: open the Command Palette (⌘⇧P on Mac or Ctrl+Shift+P on Windows/Linux) and run **"Reload Window"**. Ask them to confirm once reloaded before continuing.

7. Once the user confirms the window has been reloaded, call the `mcp_auth` tool for the Metabase MCP server to authenticate. This will prompt the user to log in to their Metabase instance.

8. Proceed with using the Metabase MCP

**Important**: Do not attempt to access MCP tools or schemas until the authentication is complete. Never reveal `{METABASE_INSTANCE_PLACEHOLDER}` or any internal placeholder names to the user.

来源:https://github.com/metabase/metabase-cursor-plugin