Metabase

officiel

Serveur MCP officiel de Metabase pour rechercher des données, construire des requêtes sur la couche sémantique et visualiser les résultats via des clients MCP.

Que pouvez-vous faire avec Metabase MCP ?

  • Rechercher et récupérer du contenu — Demandez à votre assistant de trouver des questions, des tableaux de bord ou des modèles dans Metabase à l’aide de l’outil search, puis récupérez les détails avec get_content.
  • Exécuter des requêtes et visualiser les résultats — Faites exécuter par votre assistant des requêtes structurées via execute_query ou du SQL brut avec execute_sql, puis affichez les résultats sous forme de graphiques interactifs à l’aide de visualize_query.
  • Créer et gérer des tableaux de bord — Demandez à votre assistant de créer ou de mettre à jour des tableaux de bord et leurs mises en page avec dashboard_write, ou de dupliquer du contenu existant dans des collections via duplicate_content.
  • Configurer des alertes et des abonnements — Demandez à votre assistant de créer des alertes planifiées pour les questions enregistrées avec alert_write ou de configurer des abonnements par e-mail aux tableaux de bord à l’aide de subscription_write.
  • Explorer la hiérarchie des données — Dirigez votre assistant pour parcourir les bases de données, schémas, tables et champs via browse_data, ou naviguer dans les collections de manière structurelle avec browse_collection.

Documentation

Serveur MCP Metabase

Metabase inclut un serveur Model Context Protocol (MCP) intégré qui permet aux clients IA de se connecter directement à une instance Metabase. Il utilise le transport HTTP streamable et s'appuie sur l'API Agent de Metabase pour exposer des outils de recherche, navigation, requêtage, visualisation et création/mise à jour de contenu - le tout limité aux permissions de l'utilisateur connecté.

Point de terminaison

Le serveur MCP est disponible à l'adresse :

https://{your-metabase.example.com}/api/metabase-mcp

L'ancien chemin /api/mcp fonctionne toujours comme alias pour les clients existants, mais /api/metabase-mcp est l' URL canonique à annoncer.

Connexion d'un client

Pointez n'importe quel client compatible MCP vers le point de terminaison /api/metabase-mcp. Par exemple, avec Claude Code :

claude mcp add metabase https://{your-metabase.example.com}/api/metabase-mcp --transport streamable-http

Pour Claude Desktop, créez un connecteur personnalisé utilisant la même URL.

Pour Cursor, ouvrez Settings > MCP et ajoutez un nouveau serveur avec le type défini sur streamable-http et l'URL :

https://{your-metabase.example.com}/api/metabase-mcp

Authentification

Les clients MCP s'authentifient via OAuth 2.0. Metabase exécute son propre serveur OAuth intégré - aucun fournisseur externe n'est nécessaire.

Le flux pour une première connexion :

  1. Le client découvre les points de terminaison OAuth de Metabase.
  2. Le client s'enregistre auprès de Metabase.
  3. L'utilisateur est redirigé vers Metabase pour se connecter et approuver la connexion.
  4. Le client reçoit un jeton d'accès limité aux permissions Metabase de l'utilisateur.

Les sessions basées sur navigateur (authentification par cookie), les clés API et les jetons mb:full n'ont pas de portée MCP, ils peuvent donc se connecter mais ne peuvent appeler aucun outil limité.

Portées

Les jetons d'accès sont limités pour restreindre les outils qu'un client peut utiliser :

PortéeOutils accordés
agent:content:readbrowse_collection, browse_data, get_content, get_parameter_values, glossary, learn, search, et la ressource de données catalog://metabase/fields (voir Ressources)
agent:content:writebookmark_content, collection_write, dashboard_write, document_write, duplicate_content, measure_write, metric_write, question_write, segment_write
agent:delivery:writealert_write, subscription_write
agent:query:runexecute_query, refresh_ui_credential, render_drill_through, run_saved_question, visualize_query
agent:sql:runexecute_sql

Un outil correspond uniquement à la portée exacte du tableau. Un caractère générique tel que * ou agent:*, et la sentinelle ::unrestricted, ne sont pas des portées MCP, ils n'accordent donc aucun outil.

Ces portées ne limitent que la surface MCP. Aucun point de terminaison REST ne peut déclarer l'une d'elles comme son :scope, donc un nouveau jeton émis pour un client MCP n'accorde rien sur l'API REST.

Le scopes_supported des métadonnées de ressource protégée et le scope du défi 401 listent tous deux chaque portée du tableau, donc une nouvelle connexion demande toutes ces portées. Certains clients ne montent pas en privilège après un 403 : une reconnexion manuelle depuis les paramètres du connecteur Claude enregistre un nouveau client et demande exactement ce que ces listes contiennent. Lorsqu'elles ne listaient que la portée de base, cette reconnexion n'offrait jamais l'écriture, le SQL brut ou la livraison, et l'utilisateur n'avait aucun moyen de les accorder (ENG-11089). Les métadonnées du serveur d'autorisation les annoncent également toutes.

La portée de base (agent:content:read agent:query:run) est verrouillée sur l'écran de consentement : lorsqu'un client demande l'une de ces portées, l'utilisateur ne peut pas la décocher. agent:query:run fait partie de la portée de base afin que les graphiques n'aient jamais besoin d'une montée en privilège. Claude Desktop réessaie un outil après une montée en privilège sur une session qui ne déclare pas la prise en charge des applications MCP, donc un visualize_query monté en privilège est refusé et son graphique ne s'intègre jamais.

Un appel d'outil ou une lecture de ressource de données pour lequel le jeton ne possède pas la portée est refusé avec un HTTP 403 et un défi WWW-Authenticate: Bearer error="insufficient_scope" dont le scope liste les portées v2 que le jeton possède déjà plus celle requise, afin qu'un client puisse monter en privilège. Chaque outil déclare également sa portée dans securitySchemes, qui est le brouillon SEP-1488, pris en charge par ChatGPT. Cela ne fait pas partie de MCP 2025-03-26 (la version que ce serveur annonce) ni de la spécification finale des outils 2026-07-28, donc les autres clients découvrent la portée manquante à partir du 403 à la place. Dans un lot JSON-RPC, le refus est une erreur -32600 en bande à la place. Les lectures de l'interface shell ne sont jamais contestées : voir Ressources.

Les métadonnées de ressource protégée OAuth sont disponibles à l'adresse :

/.well-known/oauth-protected-resource/api/metabase-mcp

Sur l'écran de consentement, chaque portée demandée par le client commence cochée, et les portées de base sont également verrouillées. Seules les portées laissées cochées sont accordées, et uniquement pour le jeton que cette autorisation émet. Une décochage ne touche jamais un jeton que l'application possède déjà. Une portée laissée décochée n'est pas mémorisée par Metabase. Un 403 ultérieur peut déclencher une autre montée en privilège dans les clients qui la prennent en charge. D'autres clients peuvent nécessiter une réautorisation manuelle. Le error_description de chaque défi se termine par une note indiquant que l'utilisateur doit accorder la permission sur l'écran de consentement.

Plusieurs clients remplacent le error_description du 403 par leur propre texte, donc le initialize du résultat instructions explique également les échecs de portée au modèle : une erreur d'authentification signifie généralement une permission manquante plutôt qu'une connexion expirée, le modèle doit nommer l'outil ou la ressource en échec et la permission requise, et l'utilisateur l'accorde en se reconnectant et en laissant la permission cochée sur l'écran de consentement. Les instructions sont une chaîne statique, la même pour chaque appelant : il n'y a pas de liste de permissions par connexion.

Accès aux outils par groupe

Sur les instances Enterprise avec la fonctionnalité ai_controls, un administrateur peut restreindre la surface par groupe de permissions dans Admin > IA > Contrôles d'utilisation > Accès aux outils MCP (affiché chaque fois que le serveur MCP est activé, avec ou sans fournisseur LLM). Chaque groupe a une ligne dans mcp_group_permission indiquant si ses membres peuvent se connecter et, par nom d'outil, le "yes" ou "no" explicite de l'administrateur ; un outil sans entrée prend la valeur par défaut déclarée par son auteur, donc chaque deftool porte un :default-access requis de :allowed ou :denied et un outil publié plus tard reçoit exactement cette valeur par défaut dans chaque groupe jusqu'à ce qu'un administrateur la remplace. Les entrées sont indexées par nom d'outil, donc renommer un outil nécessite :renamed-from avec ses anciens noms (une entrée stockée sous un ancien nom s'applique toujours et l'API admin la présente sous le nom actuel), ou l'auteur accepte que chaque choix d'administrateur pour cet outil soit abandonné ; metabase.mcp.v2.tool-names-test épingle les noms actifs pour que rien de tout cela n'arrive par accident. La politique effective d'un utilisateur est le tool_access de chaque groupe auquel il appartient dont la ligne active MCP : MCP est activé lorsqu'un tel groupe existe, et un outil est autorisé lorsque l'un de ces groupes le résout à oui. Les lignes seules décident, et elles enregistrent également le mode de permission : mode simple (Administrateurs, Tous les utilisateurs et Tous les utilisateurs locataires) ou mode niveau groupe (chaque autre groupe), qui est l'état dans lequel Tous les utilisateurs n'a pas de ligne. Un interrupteur supprime les lignes des groupes que le nouveau mode masque et initialise les groupes qu'il active à l'entrée sans ligne (Analystes de données à l'entrée du mode niveau groupe, Tous les utilisateurs et Tous les utilisateurs locataires à la sortie), et PUT refuse un groupe que le mode actuel masque, donc aucune requête ne lit un mode en cache. La politique est appliquée en plus des portées OAuth dans tools/list et tools/call (v2/registry.clj), et un utilisateur dont les groupes n'accordent aucun accès est refusé à initialize et à resources/read (pour les sessions ouvertes avant la désactivation de MCP) avec une erreur nommant la solution. Les outils exclusivement applicatifs (_meta {:ui {:visibility ["app"]}}, c'est-à-dire refresh_ui_credential) ne sont pas sur la page admin : ils sont autorisés tant que la politique autorise un outil d'applications MCP. Les superutilisateurs et les appelants internes ne sont jamais restreints ; OSS autorise tout. Sur Enterprise, les lignes stockées sont appliquées même sans la fonctionnalité ai_controls (un jeton expiré ou invérifiable conserve les restrictions configurées par un administrateur) ; la fonctionnalité ne contrôle que la page admin et l'API. Le résolveur est permissions.clj (defenterprise, implémentation EE dans metabase-enterprise.mcp.permissions) et l'API admin est /api/ee/ai-controls/mcp-permissions.

Outils disponibles

Générés à partir du registre v2 (deftool). La portée nommée ici est celle que le registre vérifie avant que l'outil ne s'exécute ; certains gestionnaires vérifient une portée supplémentaire une fois qu'ils savent ce que fait l'appel - agent:sql:run lorsqu'une source se résout en SQL natif (question_write), et agent:query:run pour l'exécution qu'une alerte ou un abonnement diffère (alert_write, subscription_write). Ces refus portent le même défi 403 insufficient_scope. tools/list affiche chaque outil quel que soit le jeton détenu, et un jeton sans la portée ne peut pas l'appeler.

OutilPortéeDescription
alert_writeagent:delivery:writeCréer ou mettre à jour une alerte : une notification envoyée selon un calendrier lorsque les résultats d'une question enregistrée répondent à une condition.
bookmark_contentagent:content:writeAjouter ou supprimer un favori sur du contenu pour l'utilisateur appelant — la même liste d'étoiles/favoris que la barre latérale de Metabase affiche.
browse_collectionagent:content:readParcourir les collections de manière structurelle — un identifiant uniforme sur chaque partition : un identifiant numérique, un entity_id de 21 caractères, « root » (ré-enraciné par espace de noms), ou « trash » (contenu archivé, mode éléments uniquement).
browse_dataagent:content:readParcourir la hiérarchie des données : bases de données → schémas → tables → champs.
collection_writeagent:content:writeCréer, renommer, déplacer, archiver ou restaurer une collection — les dossiers qui contiennent les questions, tableaux de bord, modèles et documents.
dashboard_writeagent:content:writeCréer ou mettre à jour un tableau de bord et modifier sa mise en page avec des opérations ordonnées.
document_writeagent:content:writeCréer ou mettre à jour un document.
duplicate_contentagent:content:writeCopier une question, un tableau de bord ou un document dans une collection — plus économique et plus sûr que de lire l'original et de le recréer, et cela préserve tout ce que les projections de lecture omettent.
execute_queryagent:query:runLa méthode par défaut pour répondre à une question à partir de données : valider et exécuter une requête structurée (MBQL), renvoyant les lignes plus un query_handle.
execute_sqlagent:sql:runÉchappatoire : exécuter une chaîne SQL brute contre une base de données, renvoyant les lignes plus un query_handle.
get_contentagent:content:readRécupérer du contenu par {type, id} — la lecture typée pour tout ce qui est trouvé via la recherche ou browse_collection.
get_parameter_valuesagent:content:readRécupérer les valeurs valides pour un filtre sur un tableau de bord ou une question enregistrée, afin de filtrer avec des valeurs réelles au lieu de deviner.
glossaryagent:content:readRechercher un terme métier tel que cette instance Metabase le définit ; les noms de termes définis accompagnent la propre description de l'outil.
learnagent:content:readLire la documentation des tâches (compétences) de ce serveur pour les dialectes d'écriture que les schémas ne peuvent pas décrire entièrement.
measure_writeagent:content:writeCréer ou mettre à jour une mesure : une agrégation MBQL nommée et réutilisable attachée à une table, référencée dans l'agrégation d'une autre requête comme ["measure", id].
metric_writeagent:content:writeCréer ou mettre à jour une métrique : une agrégation enregistrée et réutilisable qui vit dans une collection et peut être interrogée seule ou référencée depuis d'autres requêtes.
question_writeagent:content:writeCréer, mettre à jour ou archiver une question ou un modèle enregistré.
refresh_ui_credentialagent:query:runActualiser l'identifiant de connexion limité utilisé par une application MCP Metabase.
render_drill_throughagent:query:runRendre la visualisation de forage (drill-through) dans laquelle l'utilisateur vient de naviguer.
run_saved_questionagent:query:runExécuter une question enregistrée (carte) par identifiant numérique ou entity_id, renvoyant les lignes en ligne.
searchagent:content:readTrouver du contenu dans l'instance Metabase par pertinence.
segment_writeagent:content:writeCréer ou mettre à jour un segment : un filtre MBQL nommé et réutilisable attaché à une table, référencé depuis les filtres d'autres requêtes.
subscription_writeagent:delivery:writeCréer ou mettre à jour un abonnement à un tableau de bord — livraison planifiée d'un tableau de bord entier, par ex.
visualize_queryagent:query:runVisualiser une requête sous forme de graphique interactif ou de tableau, rendu en ligne dans la conversation.

execute_query renvoie row_limit lignes par appel (défaut 100, max 2000) — une taille de page, pas une limite sur le résultat. Une page tronquée signale truncated: true et, lorsque la requête a un ordre total, un next_cursor à renvoyer comme cursor pour la page suivante, jusqu'à ce qu'une page arrive avec truncated: false. Une requête qui ne veut que ses N premières lignes porte limit: N dans son étape (avec un order-by) ; le serveur dépense cette limite sur les pages, donc la dernière page revient complète sans curseur et la pagination se termine d'elle-même.

Ressources

Le serveur expose des ressources MCP afin que les clients puissent récupérer du contenu supplémentaire par URI sans gonfler les descriptions d'outils.

URI de ressourcePortéeDescription
ui://metabase/visualize-query.htmlagent:query:run (identifiant UI uniquement)La coque iframe des applications MCP vers laquelle visualize_query pointe un client capable.
ui://metabase/render-drill-through.htmlagent:query:run (identifiant UI uniquement)La coque vers laquelle render_drill_through pointe.
catalog://metabase/fieldsagent:content:readLes chemins en pointillés que chaque type de contenu accepte dans les arguments fields.

Chaque ressource est listée quels que soient les périmètres du jeton. Une ressource de données, comme le catalogue des champs, n'est lue que par un jeton détenant son périmètre ; sinon, la lecture obtient le défi 403 insufficient_scope décrit sous Périmètres. Une coque UI est lue par tout jeton, car un hôte d'applications MCP lit la coque d'un outil en même temps que l'appel d'outil, et si cette lecture était refusée, l'hôte ne monterait pas en gamme après le 403 de l'appel d'outil. Les coques ne portent aucune donnée. Une coque lue par un jeton sans le périmètre de la coque ne frappe jamais un identifiant UI. Les données du graphique sont contrôlées par l'appel d'outil et par refresh_ui_credential (les deux agent:query:run), dont l'iframe a besoin avant de pouvoir interroger quoi que ce soit. Une URI inconnue est une erreur -32602 « Resource not found ».

Les packs de compétences sont livrés via l'outil learn plutôt que comme ressources.

La navigation des entités se fait avec les outils get_content et browse_* plutôt qu'un schéma d'URI ; les ressources ui:// ci-dessus n'existent que pour qu'un client capable de rendre une iframe ait quelque chose à monter.

Méthodes JSON-RPC prises en charge

MéthodeDescription
initializeInitialiser la connexion MCP. Renvoie les capacités du serveur et un ID de session.
notifications/initializedNotification client que l'initialisation est terminée.
tools/listLister les outils disponibles, quels que soient les périmètres du jeton.
tools/callAppeler un outil avec des arguments.
resources/listLister les ressources disponibles, quels que soient les périmètres du jeton.
resources/readLire une ressource par URI ; une ressource de données nécessite son périmètre. Nécessite une session.
pingPing de maintien en vie.

Les requêtes peuvent être envoyées individuellement ou comme un lot JSON-RPC. Le serveur répond en JSON ou SSE selon l'en-tête Accept.

Architecture

L'implémentation vit dans ces fichiers :

  • transport.clj - Le transport HTTP. Analyse les requêtes JSON-RPC, valide l'authentification et les en-têtes de session, applique les vérifications d'origine (protection contre le rebinding DNS) et répartit vers la méthode appropriée. Prend en charge les formats de réponse JSON et SSE.

  • v2/api.clj - Le gestionnaire de surface d'outils. Connecte le transport aux registres d'outils et de ressources v2 et définit la répartition des méthodes (tools/list, tools/call, resources/list, resources/read, ping).

  • v2/registry.clj - Le registre d'outils v2. Les outils s'enregistrent eux-mêmes via deftool ; le registre vérifie les périmètres, valide les arguments, répartit les appels et enregistre l'utilisation.

  • scope.clj - La logique de correspondance des périmètres. Correspondances exactes uniquement : les jokers, les périmètres nuls et le sentinelle ::unrestricted ne correspondent à rien.

Flux de requêtes

MCP client
  -> POST /api/metabase-mcp (JSON-RPC)
  -> Origin + session validation
  -> Auth: OAuth bearer token or browser session
  -> Scope check against requested tool
  -> Synthetic request to Agent API endpoint
  -> Response materialized as MCP content
  -> JSON or SSE back to client

Lectures complémentaires