Metabase
io.github.metabasePublished by GitHub account metabasev0.1.0Updated Oct 24, 2026
Lets AI clients search, explore, query, and visualize data in a Metabase instance.
Overview
Metabase's built-in remote MCP server lets an AI client search, browse, query and visualize data, and create or update Metabase content within the user's…
- What you can do
- Search and browse content: search the instance, browse collections and the database/schema/table/field hierarchy, fetch content by type and id, look up glossary terms and filter values
- Query data: run structured MBQL queries or saved questions, execute raw SQL, and render results as inline charts or tables
- Create and edit content: write questions, models, dashboards, documents, collections, metrics, measures and segments, duplicate content, and manage bookmarks
- Schedule delivery: create or update alerts and dashboard subscriptions
- Read task docs and field-path catalogs through the learn tool and MCP resources
- When to use it
- Use it when the assistant should answer questions from data held in a Metabase instance, explore dashboards and saved questions, or build and maintain Metabase content such as questions, dashboards and alerts without leaving the chat.
- Requirements
- A reachable Metabase instance exposing the MCP endpoint at its /api/metabase-mcp path (the legacy /api/mcp alias also works). The client connects over Streamable HTTP and authenticates with OAuth 2.0 against Metabase's embedded authorization server; session cookies and API keys are not accepted on MCP paths. The user must log in and approve the requested scopes on the consent screen.
Installation
In SourceWeft
- Open Metabase in the dashboard and add it to a workspace.
- Enable the server for the chats that should use its tools.
Web executable via Streamable HTTP. Remote servers run from the web runtime once configured in a workspace.
Other MCP clients
Follow the launch instructions in the repository.
README
Metabase MCP Server
Metabase includes a built-in Model Context Protocol (MCP) server that lets AI clients connect directly to a Metabase instance. It uses the Streamable HTTP transport and builds on Metabase's Agent API to expose tools for searching, navigating, querying, visualizing, and creating/updating content - all scoped to the connecting user's permissions.
Endpoint
The MCP server is available at:
The legacy /api/mcp path still works as an alias for existing clients, but /api/metabase-mcp is the
canonical URL to advertise.
Connecting a client
Point any MCP-compatible client at the /api/metabase-mcp endpoint. For example, with Claude Code:
For Claude Desktop, create a custom connector using the same URL.
For Cursor, open Settings > MCP and add a new server with the type set to streamable-http and the URL:
Authentication
MCP clients authenticate via OAuth 2.0. Metabase runs its own embedded OAuth server - no external provider is needed.
The flow for a first-time connection:
- The client discovers Metabase's OAuth endpoints.
- The client registers itself with Metabase.
- The user is redirected to Metabase to log in and approve the connection.
- The client receives an access token scoped to the user's Metabase permissions.
MCP accepts only an OAuth access token. On the MCP paths the session middleware ignores session cookies and API
keys, so such a request gets the same 401 discovery challenge as a request with no credential. An mb:full token
holds no MCP scope, so it can connect but call no scoped tool.
Scopes
Access tokens are scoped to limit what tools a client can use:
A tool matches only the exact scope in the table. A wildcard such as * or agent:*, and the ::unrestricted
sentinel, are not MCP scopes, so they grant no tool.
These scopes gate the MCP surface only. No REST endpoint may declare one as its :scope, so a new token issued to an
MCP client grants nothing on the REST API.
The protected-resource metadata's scopes_supported and the scope of the 401 challenge both list every scope in
the table, so a fresh connection asks for all of them. Some clients do not step up after a 403: a manual reconnect
from Claude's connector settings registers a new client and asks for exactly what these list. When they listed only
the baseline, that reconnect never offered writes, raw SQL, or delivery, and the user had no way to grant them
(ENG-11089). The authorization server metadata advertises all of them too.
The baseline (agent:content:read agent:query:run) is locked on the consent screen: when a client requests one of
these, the user cannot untick it. agent:query:run is in the baseline so that charts never need a step-up. Claude
Desktop retries a tool after a step-up over a session that doesn't declare MCP Apps support, so a stepped-up
visualize_query is refused and its chart never embeds.
A tool call or data resource read the token lacks a scope for is refused with HTTP 403 and a
WWW-Authenticate: Bearer error="insufficient_scope" challenge whose scope lists the v2 scopes the token already
holds plus the one required, so a client can step up. Each tool also declares its scope in securitySchemes, which is
draft SEP-1488, supported by ChatGPT. It is
not part of MCP 2025-03-26 (the version this server reports) or the final 2026-07-28 tools spec, so other clients
discover the missing scope from the 403 instead. Inside a JSON-RPC batch the refusal is an in-band -32600 error
instead. UI shell reads are never challenged: see Resources.
OAuth protected resource metadata is available at:
On the consent screen, every scope the client requested starts ticked, and the baseline scopes are also locked. Only
the scopes left ticked are granted, and only for the token this authorization mints. An untick never touches a token
the app already has. A scope left unticked is not remembered by Metabase. A later 403 can trigger another step-up in
clients that support it. Other clients may require manual reauthorization. Each challenge's error_description ends
with a note that the user must grant the permission on the consent screen.
Several clients replace the 403's error_description with their own text, so the initialize result's instructions
explain scope failures to the model too: an auth error usually means a missing permission rather than an expired
login, the model should name the failed tool or resource and the permission it requires, and the user grants it by
reconnecting and leaving the permission ticked on the consent screen. The instructions are one static string, the same
for every caller: there is no per-connection permission list.
Per-group tool access
On Enterprise instances with the ai_controls feature, an admin can narrow the surface per permissions group on Admin > AI > Usage controls > MCP tools access (shown whenever the MCP server is on, with or without an LLM provider). Each group has a row in mcp_group_permission saying whether its members may connect at all and, per tool name, the admin's explicit "yes" or "no"; a tool with no entry takes the default its author declared, so every deftool carries a required :default-access of :allowed or :denied and a tool released later gets exactly that default in every group until an admin overrides it. Entries are keyed by tool name, so renaming a tool needs :renamed-from with its former names (a stored entry under a former name still applies and the admin API presents it under the current name), or the author accepts that every admin's choice for it is discarded; metabase.mcp.v2.tool-names-test pins the live names so neither happens by accident. A user's effective policy is the tool_access of each group of theirs whose row enables MCP: MCP is on when any such group exists, and a tool is allowed when any of those groups resolves it to yes. The rows alone decide it, and they also record the permission mode: simple mode (Administrators, All Users, and All tenant users) or group-level mode (every other group), which is the state in which All Users has no row. A switch deletes the rows of the groups the new mode hides and seeds the groups it enables on entry that have no row (Data Analysts on entering group-level mode, All Users and All tenant users on leaving it), and PUT refuses a group the current mode hides, so no request reads a cached mode. The policy is applied on top of OAuth scopes in tools/list and tools/call (v2/registry.clj), and a user whose groups grant no access is refused at initialize and at resources/read (for sessions opened before MCP was turned off) with an error naming the fix. App-only tools (_meta {:ui {:visibility ["app"]}}, i.e. refresh_ui_credential) are not on the admin page: they are allowed while the policy allows any MCP Apps tool. Superusers and internal callers are never narrowed; OSS allows everything. On Enterprise the stored rows are enforced even without the ai_controls feature (an expired or unverifiable token keeps the restrictions an admin configured); the feature only gates the admin page and API. The resolver is permissions.clj (defenterprise, EE implementation in metabase-enterprise.mcp.permissions) and the admin API is /api/ee/ai-controls/mcp-permissions.
Available tools
Generated from the v2 registry (deftool). The scope named here is what the registry checks before the tool
runs; some handlers check a further scope once they know what the call does - agent:sql:run when a source
resolves to native SQL (question_write), and agent:query:run for the execution an alert or subscription defers
(alert_write, subscription_write). Those refusals carry the same 403 insufficient_scope challenge. tools/list
shows every tool whatever the token holds, and a token missing the scope may not call it.
execute_query returns row_limit rows per call (default 100, max 2000) — a page size, not a bound on the result.
A truncated page reports truncated: true and, when the query has a total order, a next_cursor to pass back as
cursor for the next page, until a page arrives with truncated: false. A query that wants only its first N rows
carries limit: N in its stage (with an order-by); the server spends that limit down across pages, so the last
page comes back complete with no cursor and pagination ends by itself.
Resources
The server exposes MCP resources so clients can fetch supplementary content by URI without inflating tool descriptions.
Every resource is listed whatever the token's scopes. A data resource, such as the fields catalog, is read only by a
token holding its scope; otherwise the read gets the 403 insufficient_scope challenge described under
Scopes. A UI shell is read by any token, because an MCP Apps host reads a tool's shell
alongside the tool call, and if that read were refused, the host would not step up after the tool call's 403. The
shells carry no data. A shell read by a token without the shell's scope never mints a UI credential. The chart data is
gated by the tool call and by refresh_ui_credential (both agent:query:run), which the iframe needs before it can
query anything. An unknown URI is a -32602 "Resource not found" error.
Skill packs are delivered through the learn tool rather than as resources.
Entity navigation is done with the get_content and browse_* tools rather than a URI scheme; the ui://
resources above exist only so a client that can render an iframe has something to mount.
Supported JSON-RPC methods
Requests can be sent individually or as a JSON-RPC batch. The server responds with JSON or SSE depending on the
Accept header.
Architecture
The implementation lives in these files:
-
transport.clj- The HTTP transport. Parses JSON-RPC requests, validates authentication and session headers, enforces origin checks (DNS rebinding protection), and dispatches to the appropriate method. Supports both JSON and SSE response formats. -
v2/api.clj- The tool surface handler. Wires the transport to the v2 tool + resource registries and defines method dispatch (tools/list,tools/call,resources/list,resources/read,ping). -
v2/registry.clj- The v2 tool registry. Tools self-register viadeftool; the registry checks scopes, validates arguments, dispatches calls, and records usage. -
scope.clj- Scope matching logic. Exact matches only: wildcards, nil scopes, and the::unrestrictedsentinel match nothing.
Request flow
Further reading
Source: src/metabase/mcp/README.md at commit ffebfce
Tools
0Version history
1- v0.1.0LatestSep 16, 2026
Score
40% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
- Reached in the last 30 daysRequired25 pts
Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.
- Auth declaredRequired10 pts
Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.
- Tools listed15 pts
Answer tools/list without a credential, or ship a package that starts in a sandbox without one.
- DescriptionRequired15 pts
- Logo10 pts
- Documentation15 pts
- License10 pts
Declare a license in the repository or the registry entry.