# Metabase Managed MCP Server

> For the complete documentation index, see [llms.txt](https://docs.redpanda.com/llms.txt). Component-specific: [agentic-data-plane-full.txt](https://docs.redpanda.com/agentic-data-plane-full.txt)

---
title: Metabase Managed MCP Server
page-beta-text: This is a beta feature. Beta features are available for testing and feedback. They are not supported by Redpanda and should not be used in production environments.
latest-operator-version: v26.1.5
latest-console-tag: v3.7.4
latest-connect-version: 4.96.1
latest-redpanda-tag: v26.1.10
docname: managed/metabase
page-component-name: agentic-data-plane
page-version: master
page-component-version: master
page-component-title: Agentic Data Plane
page-relative-src-path: managed/metabase.adoc
page-edit-url: https://github.com/redpanda-data/adp-docs/edit/main/modules/connect/pages/managed/metabase.adoc
# Beta release status
page-beta: "true"
description: Give an LLM agent read-and-query access to a Metabase analytics instance through the semantic layer (search, get_table, query metrics) and SQL escape hatches (run_native_query, run_card).
page-topic-type: how-to
personas: agent_builder, platform_engineer
learning-objective-1: Configure the Metabase managed MCP server with a v0.49+ API key bound to the right Metabase group
learning-objective-2: Identify when to use semantic-layer tools (<code>search</code>, <code>get_metric</code>, <code>query</code>) versus the native-SQL escape hatches (<code>run_native_query</code>, <code>run_card</code>)
learning-objective-3: Run a metric-first analysis against your Metabase instance from the Inspector or an agent
page-git-created-date: "2026-05-28"
page-git-modified-date: "2026-06-10"
release-status: beta - This is a beta feature. Beta features are available for testing and feedback. They are not supported by Redpanda and should not be used in production environments.
---

<!-- Source: https://docs.redpanda.com/agentic-data-plane/connect/managed/metabase.md -->

The **Metabase** managed MCP server lets agents read and query a Metabase analytics instance. The tool surface is a hybrid of Metabase’s semantic-layer primitives (tables, metrics, fields, dimensions) and SQL-first escape hatches (native queries and saved questions), wrapping the Metabase REST API with a long-lived API key.

After reading this page, you will be able to:

-   Configure the Metabase managed MCP server with a v0.49+ API key bound to the right Metabase group

-   Identify when to use semantic-layer tools (`search`, `get_metric`, `query`) versus the native-SQL escape hatches (`run_native_query`, `run_card`)

-   Run a metric-first analysis against your Metabase instance from the Inspector or an agent


## [](#what-this-mcp-server-does)What this MCP server does

Metabase is a self-service analytics platform that exposes data through a **semantic layer** (tables, metrics, fields) plus a SQL editor for ad-hoc native queries. This MCP wraps the Metabase REST API so an LLM can:

-   Search the semantic layer for relevant tables, metrics, saved questions, and dashboards.

-   Retrieve a table or metric’s structure (fields, dimensions, related tables).

-   Run a structured query against a metric or table with filters, group-by, and limits.

-   Run a saved question (card) or a hand-written native SQL query.


The agent’s preferred path is **metric-first**: discover what already exists, fetch the metric’s definition, and call `query` against it so the aggregation logic stays human-vetted. The SQL escape hatches (`run_native_query`, `run_card`) are available when the semantic layer cannot express the question, but a metric is strictly safer because it forecloses common analytics mistakes (averaging averages, double-counting joined rows).

It is **not** a Metabase admin tool. Creating dashboards, editing cards, configuring data sources, and managing users live in the Metabase admin UI.

## [](#authentication-model)Authentication model

Metabase API keys (v0.49+) inherit the **permissions of the group they’re bound to**. The MCP sends the resolved key in the `X-API-Key` header on every upstream request.

The Metabase MCP uses one API key per MCP instance, applied to every tool call, with no per-user delegation. To give different agents different access, mount multiple MCP instances bound to different Metabase groups.

Auth is implemented as `oneof auth { StaticKeyAuth api_key }` (single variant today, future-proof for adding session or OAuth modes without a wire-format break).

## [](#prerequisites)Prerequisites

Before you create the server, make sure you have:

-   A Metabase instance on v0.49 or later (API keys are not available on older versions).

-   Admin access to the Metabase instance to mint an API key.

-   A Metabase **group** with the right permissions for the data you want the agent to access (typically `Native query editing` on the relevant databases plus `View` on the relevant collections).

-   One Redpanda ADP secret-store entry for the API key. Secret references must be `UPPER_SNAKE_CASE`, for example `METABASE_API_KEY`.


## [](#get-a-metabase-api-key)Get a Metabase API key

Mint the key in the Metabase admin UI:

1.  Sign in to your Metabase instance as an admin.

2.  Open **Settings > Authentication > API keys**.

3.  Click **Create API key**.

4.  Give the key a descriptive name, for example `redpanda-mcp`.

5.  Bind the key to a group whose permissions match the access you want the agent to have. For an analytics agent, an `Analytics` group with `Native query editing` on the relevant databases and `View` on the relevant collections is typical.

6.  Copy the key. Metabase shows it only once.

7.  Save the key in your ADP secret store under a name like `METABASE_API_KEY`.


> 📝 **NOTE**
>
> Permission errors surface from the upstream Metabase API as `metabase API error (status 4xx)`. Fix by adjusting the bound group’s permissions in Metabase, not by re-issuing a different key.

## [](#configure)Configure

Create a new Metabase MCP server in ADP:

1.  Open **MCP Servers > Create Server**.

2.  Pick **Metabase** from the marketplace picker.

3.  Fill in identity fields (`name`, `description`).

4.  In the Metabase configuration form:

    | Field | Notes |
    | --- | --- |
    | Base URL | Your Metabase instance URL, for example https://metabase.mycompany.com. No trailing slash. |
    | API key ref | Secret-store reference for the Metabase API key (UPPER_SNAKE_CASE). Example: METABASE_API_KEY. |

5.  Click **Create**.


### [](#configure-from-the-cli)Configure from the CLI

Run `rpk ai mcp create` with the Metabase managed-config payload:

```bash
rpk ai mcp create --name metabase --managed-config '{
  "@type": "type.googleapis.com/redpanda.mcps.metabase.v1.MetabaseMCPConfig",
  "base_url": "https://metabase.mycompany.com",
  "api_key": {
    "key_secret_ref": "METABASE_API_KEY"
  }
}'
```

The `api_key` field wraps the shared `StaticKeyAuth`; `key_secret_ref` is the bare secret-store key name (`UPPER_SNAKE_CASE`).

## [](#tools)Tools

The Metabase MCP exposes tools across a semantic-layer surface and a SQL-first surface. The agent’s preferred path is the semantic-layer surface; the SQL-first surface is an escape hatch.

### [](#semantic-layer-tools)Semantic-layer tools

These six tools cover discovery, schema lookup, and structured queries against pre-vetted metrics.

| Tool | Description |
| --- | --- |
| search | Run Metabase’s universal search across tables, metrics, saved questions, dashboards, and collections. Use this first to discover existing analyses before constructing a fresh query. |
| get_table | Retrieve a table’s fields, related tables, and metrics defined on it. The field list is what the agent uses to write correct filters and group-bys. |
| get_table_field_values | Sample values plus a small statistical summary for one field on a table. Useful before writing a filter; the agent can check actual cardinality and value shape rather than guessing. |
| get_metric | Retrieve a metric definition with its queryable dimensions. Metrics are pre-vetted aggregations; calling query against a metric is strictly preferred over hand-rolled SQL when the measure already exists. |
| get_metric_field_values | Sample values for a dimension of a metric. Same role as get_table_field_values, scoped to a metric’s queryable dimensions. |
| query | One-shot structured query against a table OR a metric, with optional group_by_field_ids, filters (MBQL JSON snippets), and limit. Returns typed columns and rows. |

### [](#sql-first-escape-hatches)SQL-first escape hatches

These five tools let an agent enumerate databases, run native SQL, and execute saved questions when the semantic layer cannot express the question.

| Tool | Description |
| --- | --- |
| list_databases | Enumerate the databases Metabase is connected to. The id returned here is what get_table, list_tables, run_native_query, and query take as the database reference. |
| list_tables | List tables in a database, optionally filtered by schema. Use this when browsing schema by name; search is the right tool when looking by keyword. |
| run_native_query | Execute a native (raw SQL) query against a database. Escape hatch for cases the semantic layer cannot express; prefer query against a metric or table when possible. |
| get_card | Retrieve a saved question’s definition (name, description, database, display type, native SQL when applicable). Use this to inspect an analysis a human curated. |
| run_card | Execute a saved question and return its result rows. The card carries its own database, query, and parameters; this RPC just runs it as-is. |

## [](#examples)Examples

The examples in this section assume `[https://aigw.<cluster-id>.clusters.rdpa.co/mcp/v1/metabase](https://aigw.\<cluster-id\>.clusters.rdpa.co/mcp/v1/metabase)` is your MCP server URL and that you’ve already authenticated to the gateway.

### [](#discover-what-exists-then-query-a-metric)Discover what exists, then query a metric

Search for revenue-related objects:

```bash
curl -sS -X POST https://aigw.<cluster-id>.clusters.rdpa.co/mcp/v1/metabase \
  -H 'Authorization: Bearer <oidc-access-token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0", "id": 1,
    "method": "tools/call",
    "params": {
      "name": "search",
      "arguments": {
        "q": "revenue",
        "models": ["metric", "card"],
        "page_size": 10
      }
    }
  }' | jq
```

If `search` returns a metric like `{ "model": "metric", "id": 200, "name": "Monthly Revenue", …​ }`, fetch its dimensions:

```bash
curl -sS -X POST https://aigw.<cluster-id>.clusters.rdpa.co/mcp/v1/metabase \
  -H 'Authorization: Bearer <oidc-access-token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0", "id": 2,
    "method": "tools/call",
    "params": {
      "name": "get_metric",
      "arguments": { "metric_id": 200 }
    }
  }' | jq
```

Then run the metric grouped by one of its dimensions (the `field_id` values come from the previous response’s `dimensions[]`):

```bash
curl -sS -X POST https://aigw.<cluster-id>.clusters.rdpa.co/mcp/v1/metabase \
  -H 'Authorization: Bearer <oidc-access-token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0", "id": 3,
    "method": "tools/call",
    "params": {
      "name": "query",
      "arguments": {
        "database_id": 5,
        "metric_id": 200,
        "group_by_field_ids": [12],
        "limit": 1000
      }
    }
  }' | jq
```

### [](#native-sql-escape-hatch)Native SQL escape hatch

When a question genuinely needs raw SQL (for example, multi-CTE windowed analysis Metabase’s MBQL doesn’t express), drop down to `run_native_query`:

```bash
curl -sS -X POST https://aigw.<cluster-id>.clusters.rdpa.co/mcp/v1/metabase \
  -H 'Authorization: Bearer <oidc-access-token>' \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0", "id": 4,
    "method": "tools/call",
    "params": {
      "name": "run_native_query",
      "arguments": {
        "database_id": 5,
        "query": "SELECT date_trunc(\"month\", created_at) AS m, count(*) FROM orders GROUP BY 1 ORDER BY 1 DESC LIMIT 12"
      }
    }
  }' | jq
```

The bound group’s permissions on the target database determine whether the call succeeds. A key bound to a group without `Native query editing` on the database returns a 403.

## [](#notes-and-limits)Notes and limits

Five behaviors are worth knowing before you point an agent at the server:

-   **Permissions follow the API key’s group.** The MCP doesn’t bypass Metabase’s permission system; permission errors surface as `metabase API error (status 4xx)`. Fix the group, not the MCP.

-   **Metric vs. saved question.** In Metabase v0.50+ both metrics and saved questions are stored as cards (`/api/card/:id`). `get_metric` validates that the card’s `type == "metric"` and surfaces queryable dimensions; `get_card` works on any card. `run_card` runs whatever is saved.

-   **Sample database.** The `list_databases` tool exposes Metabase’s bundled Sample Database with an `is_sample` flag set. Agents should usually exclude it from real analyses.

-   **Body cap.** Query results are capped at 25 MiB on the gateway-side wrapper. Queries that exceed the cap return an error rather than blowing up the agent’s context. Lower the `limit` argument on `query` or `run_native_query` if you hit it.

-   **Response curation.** The MCP drops Metabase’s internal indexing metadata (search ranking signals, fingerprint blobs, embedded user objects, per-column metadata blobs) so each response is 60–85% smaller than the raw REST API would return. Agents see clean, action-relevant fields.


## [](#troubleshooting)Troubleshooting

Common symptoms and fixes:

| Symptom | What to check |
| --- | --- |
| metabase API error (status 401) | API key is invalid or has been revoked. Mint a new key in Settings > Authentication > API keys and update the secret in the ADP secret store under the same name. |
| metabase API error (status 403) | The API key’s group lacks the required permission. Check the group’s database and collection permissions in Admin > Permissions. |
| metabase API error (status 404) on get_table or get_metric | The table_id or metric_id doesn’t exist in the bound database. Confirm the ID with search or list_tables first. |
| Query returns an error about exceeding the body cap | Result set exceeds 25 MiB. Lower limit on the query, or pre-aggregate by calling query against a metric. |
| Agent keeps calling run_native_query instead of query | Confirm the relevant metric exists and has queryable dimensions. The agent prefers metrics when search or get_table surfaces them. |

## [](#limitations)Limitations

This page does not cover:

-   **Metabase administration.** Creating dashboards, editing cards, configuring data sources, and managing users live in the Metabase admin UI or its `/api/setup` endpoints.

-   **Per-user delegation.** The MCP uses one shared API key per instance. To give different agents different access, mount multiple MCP instances bound to different Metabase groups.

-   **Metabase MBQL reference.** See Metabase’s own documentation for the full MBQL filter shape; the MCP passes `filters` arguments through verbatim.


## [](#next-steps)Next steps

-   [Create an MCP Server](https://docs.redpanda.com/agentic-data-plane/connect/create-server/)

-   [Test a server’s tools](https://docs.redpanda.com/agentic-data-plane/connect/test-tools/)

-   [Plug in an App, Database, or Tool](https://docs.redpanda.com/agentic-data-plane/connect/managed/managed-catalog/)