Metabase
официальныйОфициальный MCP-сервер Metabase для поиска данных, построения запросов на семантическом уровне и визуализации результатов через MCP-клиенты.
Что можно делать с Metabase MCP?
- Поиск и получение контента — Попросите ассистента найти вопросы, дашборды или модели в Metabase с помощью инструмента
search, а затем получить подробности черезget_content. - Выполнение запросов и визуализация результатов — Поручите ассистенту выполнять структурированные запросы через
execute_queryили необработанный SQL с помощьюexecute_sql, а затем отображать результаты в виде интерактивных графиков с использованиемvisualize_query. - Создание и управление дашбордами — Дайте указание ассистенту создавать или обновлять дашборды и их макеты с помощью
dashboard_writeили дублировать существующий контент в коллекции черезduplicate_content. - Настройка оповещений и подписок — Попросите ассистента создавать запланированные оповещения для сохраненных вопросов с помощью
alert_writeили настраивать email-подписки на дашборды черезsubscription_write. - Изучение иерархии данных — Направьте ассистента на просмотр баз данных, схем, таблиц и полей через
browse_dataили на структурную навигацию по коллекциям с помощьюbrowse_collection.
Документация
Сервер MCP Metabase
Metabase включает встроенный сервер Model Context Protocol (MCP), который позволяет AI-клиентам подключаться напрямую к экземпляру Metabase. Он использует https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http и опирается на Agent API Metabase, предоставляя инструменты для поиска, навигации, выполнения запросов, визуализации и создания/обновления контента — всё в рамках прав доступа подключающегося пользователя.
Конечная точка
Сервер MCP доступен по адресу:
https://{your-metabase.example.com}/api/metabase-mcp
Устаревший путь /api/mcp по-прежнему работает как псевдоним для существующих клиентов, но /api/metabase-mcp является
каноническим URL для публикации.
Подключение клиента
Укажите любому MCP-совместимому клиенту конечную точку /api/metabase-mcp. Например, с Claude Code:
claude mcp add metabase https://{your-metabase.example.com}/api/metabase-mcp --transport streamable-http
Для Claude Desktop создайте пользовательский коннектор, используя тот же URL.
Для Cursor откройте Settings > MCP и добавьте новый сервер с типом streamable-http и URL:
https://{your-metabase.example.com}/api/metabase-mcp
Аутентификация
MCP-клиенты аутентифицируются через OAuth 2.0. Metabase запускает собственный встроенный OAuth-сервер — внешний провайдер не требуется.
Процесс первого подключения:
- Клиент обнаруживает OAuth-конечные точки Metabase.
- Клиент регистрируется в Metabase.
- Пользователь перенаправляется в Metabase для входа и одобрения подключения.
- Клиент получает токен доступа с правами пользователя Metabase.
Сессии на основе браузера (cookie-аутентификация), API-ключи и токены mb:full не имеют MCP-области, поэтому они могут подключаться, но
не могут вызывать инструменты с ограниченной областью.
Области
Токены доступа ограничены по областям, чтобы определить, какие инструменты может использовать клиент:
| Область | Предоставляемые инструменты |
|---|---|
agent:content:read | browse_collection, browse_data, get_content, get_parameter_values, glossary, learn, search и ресурс данных catalog://metabase/fields (см. Ресурсы) |
agent:content:write | bookmark_content, collection_write, dashboard_write, document_write, duplicate_content, measure_write, metric_write, question_write, segment_write |
agent:delivery:write | alert_write, subscription_write |
agent:query:run | execute_query, refresh_ui_credential, render_drill_through, run_saved_question, visualize_query |
agent:sql:run | execute_sql |
Инструмент соответствует только точной области из таблицы. Подстановочный знак, такой как * или agent:*, и маркер ::unrestricted
не являются MCP-областями, поэтому они не предоставляют ни одного инструмента.
Эти области ограничивают только поверхность MCP. Ни одна REST-конечная точка не может объявить одну из них как свой :scope, поэтому новый токен, выданный
MCP-клиенту, не предоставляет ничего в REST API.
В метаданных защищённого ресурса scopes_supported и в scope запроса 401 перечислены все области из
таблицы, поэтому новое подключение запрашивает все из них. Некоторые клиенты не повышают права после 403: ручное переподключение
из настроек коннектора Claude регистрирует новый клиент и запрашивает ровно то, что перечислено. Когда там был указан только
базовый набор, такое переподключение никогда не предлагало запись, raw SQL или доставку, и у пользователя не было способа их предоставить
(ENG-11089). Метаданные сервера авторизации также рекламируют все из них.
Базовый набор (agent:content:read agent:query:run) заблокирован на экране согласия: когда клиент запрашивает одну из
этих областей, пользователь не может снять с неё отметку. agent:query:run входит в базовый набор, чтобы диаграммы никогда не требовали повышения прав. Claude
Desktop повторяет вызов инструмента после повышения прав через сессию, которая не объявляет поддержку MCP Apps, поэтому повышенная
область visualize_query отклоняется, и её диаграмма никогда не встраивается.
Вызов инструмента или чтение ресурса данных, для которого у токена нет области, отклоняется с HTTP 403 и
запросом WWW-Authenticate: Bearer error="insufficient_scope", чей scope перечисляет v2-области, уже имеющиеся у токена,
плюс требуемую, чтобы клиент мог повысить права. Каждый инструмент также объявляет свою область в securitySchemes, что является
черновиком SEP-1488, поддерживаемым ChatGPT. Это
не часть MCP 2025-03-26 (версии, которую сообщает этот сервер) или финальной спецификации инструментов 2026-07-28, поэтому другие клиенты
обнаруживают отсутствующую область из 403. Внутри JSON-RPC-пакета отказ является внутриполосной ошибкой -32600
вместо этого. Чтения UI-оболочки никогда не запрашивают повышения прав: см. Ресурсы.
Метаданные защищённого ресурса OAuth доступны по адресу:
/.well-known/oauth-protected-resource/api/metabase-mcp
На экране согласия каждая область, запрошенная клиентом, изначально отмечена, а базовые области также заблокированы. Только
оставшиеся отмеченными области предоставляются, и только для токена, который выпускает эта авторизация. Снятие отметки никогда не затрагивает токен,
который уже есть у приложения. Область, оставленная без отметки, не запоминается Metabase. Последующий 403 может вызвать ещё одно повышение прав у
клиентов, которые его поддерживают. Другим клиентам может потребоваться ручная повторная авторизация. Каждый запрос error_description завершается
примечанием о том, что пользователь должен предоставить разрешение на экране согласия.
Некоторые клиенты заменяют error_description из 403 своим собственным текстом, поэтому в результате initialize поля instructions
объясняют ошибки области и модели: ошибка аутентификации обычно означает отсутствующее разрешение, а не истёкший
вход, модель должна назвать неудавшийся инструмент или ресурс и требуемое разрешение, а пользователь предоставляет его,
переподключившись и оставив разрешение отмеченным на экране согласия. Инструкции представляют собой одну статическую строку, одинаковую
для всех вызывающих: нет списка разрешений для конкретного подключения.
Доступ к инструментам по группам
В экземплярах Enterprise с функцией ai_controls администратор может сузить поверхность по группам разрешений в Admin > AI > Usage controls > MCP tools access (показывается, когда сервер MCP включён, с провайдером LLM или без него). Каждая группа имеет строку в mcp_group_permission, указывающую, могут ли её участники подключаться вообще, и, по имени инструмента, явное значение администратора "yes" или "no"; инструмент без записи принимает значение по умолчанию, объявленное его автором, поэтому каждый deftool несёт обязательное :default-access со значением :allowed или :denied, и инструмент, выпущенный позже, получает ровно это значение по умолчанию в каждой группе, пока администратор не переопределит его. Записи привязаны к имени инструмента, поэтому переименование инструмента требует :renamed-from с его прежними именами (сохранённая запись под прежним именем всё ещё действует, и API администратора показывает её под текущим именем), или автор принимает, что выбор каждого администратора для него отбрасывается; metabase.mcp.v2.tool-names-test фиксирует актуальные имена, чтобы ни то, ни другое не произошло случайно. Эффективная политика пользователя — это tool_access каждой его группы, чья строка включает MCP: MCP включён, когда существует любая такая группа, и инструмент разрешён, когда любая из этих групп разрешает его. Строки решают это сами по себе, и они также фиксируют режим разрешений: простой режим (Administrators, All Users и All tenant users) или режим уровня групп (все остальные группы), который является состоянием, в котором у All Users нет строки. Переключатель удаляет строки групп, скрываемых новым режимом, и заполняет группы, которые он включает при входе и не имеющие строки (Data Analysts при входе в режим уровня групп, All Users и All tenant users при выходе из него), а PUT отклоняет группу, скрываемую текущим режимом, поэтому ни один запрос не читает кэшированный режим. Политика применяется поверх областей OAuth в tools/list и tools/call (v2/registry.clj), и пользователь, чьи группы не дают доступа, отклоняется на initialize и на resources/read (для сессий, открытых до отключения MCP) с ошибкой, называющей исправление. Инструменты только для приложений (_meta {:ui {:visibility ["app"]}}, т.е. refresh_ui_credential) не отображаются на странице администратора: они разрешены, пока политика разрешает любой инструмент MCP Apps. Суперпользователи и внутренние вызывающие никогда не сужаются; OSS разрешает всё. В Enterprise сохранённые строки применяются даже без функции ai_controls (истёкший или непроверяемый токен сохраняет ограничения, настроенные администратором); функция ограничивает только страницу администратора и API. Резолвер — это permissions.clj (defenterprise, реализация EE в metabase-enterprise.mcp.permissions), а API администратора — это /api/ee/ai-controls/mcp-permissions.
Доступные инструменты
Сгенерировано из реестра v2 (deftool). Область, указанная здесь, — это то, что реестр проверяет перед запуском
инструмента; некоторые обработчики проверяют дополнительную область, когда узнают, что делает вызов — agent:sql:run, когда источник
разрешается в нативный SQL (question_write), и agent:query:run для выполнения, которое откладывает оповещение или подписка
(alert_write, subscription_write). Эти отказы несут тот же запрос 403 insufficient_scope. tools/list
показывает каждый инструмент независимо от того, что содержит токен, и токен без области не может его вызвать.
| Инструмент | Область действия | Описание |
|---|---|---|
alert_write | agent:delivery:write | Создать или обновить оповещение: уведомление, отправляемое по расписанию, когда результаты сохранённого вопроса соответствуют условию. |
bookmark_content | agent:content:write | Добавить или удалить закладку на контенте для вызывающего пользователя — тот же список избранного, который показывает боковая панель Metabase. |
browse_collection | agent:content:read | Просматривать коллекции структурно — один единый идентификатор для каждого раздела: числовой id, 21-символьный entity_id, «root» (переопределяемый для каждого пространства имён) или «trash» (архивированный контент, только в режиме элементов). |
browse_data | agent:content:read | Просматривать иерархию данных: базы данных → схемы → таблицы → поля. |
collection_write | agent:content:write | Создать, переименовать, переместить, архивировать или восстановить коллекцию — папки, содержащие вопросы, панели мониторинга, модели и документы. |
dashboard_write | agent:content:write | Создать или обновить панель мониторинга и изменить её макет с помощью упорядоченных операций. |
document_write | agent:content:write | Создать или обновить документ. |
duplicate_content | agent:content:write | Скопировать вопрос, панель мониторинга или документ в коллекцию — дешевле и безопаснее, чем читать оригинал и создавать его заново, и это сохраняет всё, что опускают проекции чтения. |
execute_query | agent:query:run | Стандартный способ ответить на вопрос из данных: проверить и выполнить структурированный (MBQL) запрос, возвращая строки плюс query_handle. |
execute_sql | agent:sql:run | Запасной вариант: выполнить сырую SQL-строку против базы данных, возвращая строки плюс query_handle. |
get_content | agent:content:read | Получить контент по {type, id} — типизированное чтение для всего, что найдено через поиск или browse_collection. |
get_parameter_values | agent:content:read | Получить допустимые значения для одного фильтра на панели мониторинга или сохранённом вопросе, чтобы фильтровать по реальным значениям, а не угадывать. |
glossary | agent:content:read | Найти бизнес-термин так, как его определяет этот экземпляр Metabase; имена определённых терминов указаны в описании самого инструмента. |
learn | agent:content:read | Прочитать документацию по задачам (навыкам) этого сервера для диалектов записи, которые схемы не могут полностью описать. |
measure_write | agent:content:write | Создать или обновить меру: именованное, переиспользуемое MBQL-агрегирование, привязанное к одной таблице, на которое ссылаются в агрегировании другого запроса как ["measure", id]. |
metric_write | agent:content:write | Создать или обновить метрику: сохранённое, переиспользуемое агрегирование, которое живёт в коллекции и может быть запрошено само по себе или использовано в других запросах. |
question_write | agent:content:write | Создать, обновить или архивировать сохранённый вопрос или модель. |
refresh_ui_credential | agent:query:run | Обновить ограниченный учётные данные, используемые приложением Metabase MCP. |
render_drill_through | agent:query:run | Отрисовать визуализацию drill-through, в которую только что перешёл пользователь. |
run_saved_question | agent:query:run | Выполнить сохранённый вопрос (карточку) по числовому id или entity_id, возвращая строки встроенно. |
search | agent:content:read | Найти контент по всему экземпляру Metabase по релевантности. |
segment_write | agent:content:write | Создать или обновить сегмент: именованный, переиспользуемый MBQL-фильтр, привязанный к одной таблице, на который ссылаются из фильтров других запросов. |
subscription_write | agent:delivery:write | Создать или обновить подписку на панель мониторинга — запланированная доставка целой панели, например. |
visualize_query | agent:query:run | Визуализировать запрос как интерактивную диаграмму или таблицу, отрисованную встроенно в разговоре. |
execute_query возвращает row_limit строк за вызов (по умолчанию 100, максимум 2000) — это размер страницы, а не ограничение на результат.
Усечённая страница сообщает truncated: true и, когда у запроса есть полный порядок, next_cursor для передачи обратно как
cursor для следующей страницы, пока не придёт страница с truncated: false. Запрос, которому нужны только первые N строк,
несёт limit: N в своём этапе (с order-by); сервер расходует этот лимит по страницам, поэтому последняя
страница приходит полной без курсора, и пагинация завершается сама.
Ресурсы
Сервер предоставляет MCP ресурсы, чтобы клиенты могли получать дополнительный контент по URI без раздувания описаний инструментов.
| URI ресурса | Область действия | Описание |
|---|---|---|
ui://metabase/visualize-query.html | agent:query:run (только учётные данные UI) | Оболочка iframe MCP Apps, на которую visualize_query указывает способному клиенту. |
ui://metabase/render-drill-through.html | agent:query:run (только учётные данные UI) | Оболочка, на которую указывает render_drill_through. |
catalog://metabase/fields | agent:content:read | Точечные пути, которые каждый тип контента принимает в аргументах fields. |
Каждый ресурс перечислен независимо от областей действия токена. Ресурс данных, такой как каталог полей, читается только
токеном, имеющим его область действия; в противном случае чтение получает вызов 403 insufficient_scope, описанный в разделе
Области действия. Оболочка UI читается любым токеном, потому что хост MCP Apps читает оболочку инструмента
вместе с вызовом инструмента, и если бы это чтение было отклонено, хост не перешёл бы к следующему шагу после 403 от вызова инструмента.
Оболочки не несут данных. Оболочка, прочитанная токеном без области действия оболочки, никогда не создаёт учётные данные UI. Данные диаграммы
ограничены вызовом инструмента и refresh_ui_credential (оба agent:query:run), которые iframe нуждается до того, как сможет
запросить что-либо. Неизвестный URI — это ошибка -32602 «Ресурс не найден».
Пакеты навыков доставляются через инструмент learn, а не как ресурсы.
Навигация по сущностям выполняется с помощью инструментов get_content и browse_*, а не схемы URI; ресурсы ui://
выше существуют только для того, чтобы клиент, который может отрисовать iframe, имел что монтировать.
Поддерживаемые методы JSON-RPC
| Метод | Описание |
|---|---|
initialize | Инициализировать MCP-соединение. Возвращает возможности сервера и ID сессии. |
notifications/initialized | Уведомление клиента о завершении инициализации. |
tools/list | Список доступных инструментов, независимо от областей действия токена. |
tools/call | Вызвать инструмент с аргументами. |
resources/list | Список доступных ресурсов, независимо от областей действия токена. |
resources/read | Прочитать ресурс по URI; ресурс данных требует свою область действия. Требуется сессия. |
ping | Пинг поддержания соединения. |
Запросы можно отправлять по отдельности или как JSON-RPC пакет. Сервер отвечает в формате JSON или SSE в зависимости от
заголовка Accept.
Архитектура
Реализация находится в следующих файлах:
-
transport.clj- HTTP-транспорт. Разбирает JSON-RPC запросы, проверяет аутентификацию и заголовки сессии, применяет проверки происхождения (защита от DNS-ребдинга) и направляет к соответствующему методу. Поддерживает как JSON, так и SSE форматы ответов. -
v2/api.clj- Обработчик поверхности инструментов. Связывает транспорт с реестрами инструментов и ресурсов v2 и определяет диспетчеризацию методов (tools/list,tools/call,resources/list,resources/read,ping). -
v2/registry.clj- Реестр инструментов v2. Инструменты саморегистрируются черезdeftool; реестр проверяет области действия, валидирует аргументы, выполняет вызовы и записывает использование. -
scope.clj- Логика сопоставления областей действия. Только точные совпадения: подстановочные знаки, nil-области и sentinel::unrestrictedне соответствуют ничему.
Поток запроса
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