ask_data
ChatGPTAsk a natural-language question about THIS tenant's freight data (shipments, jobs, parties, charges, on-time, margin) and get back rows + the SQL that produced them + a "data as of" freshness stamp. The question is turned into tenant-scoped SQL by the same governed generator the in-app analytics assistant uses; your companyId is derived from your token (you cannot query another tenant). Defaults to the fast warehouse mirror (refreshed ~twice daily — see the freshness stamp); pass dataSource:"live" only when you need up-to-the-minute data (slower). Results are capped (see rowCount/truncated) — if truncated, narrow the question or ask for an aggregate. Multi-company tokens: COMBINED queries across your companies run on the WAREHOUSE only — for live/real-time data, query ONE company at a time (pass a single companyId) and combine across companies yourself. When a multi-company answer breaks down BY company, the result includes a companies map ([{id,code,name,currency}]) — label each row with the company code/name (never the numeric companyId), and show money in each company's own currency; never sum money across currencies.
ask_data
ChatGPTAsk a natural-language question about THIS tenant's freight data (shipments, jobs, parties, charges, on-time, margin) and get back rows + the SQL that produced them + a "data as of" freshness stamp. The question is turned into tenant-scoped SQL by the same governed generator the in-app analytics assistant uses; your companyId is derived from your token (you cannot query another tenant). Defaults to the fast warehouse mirror (refreshed ~twice daily — see the freshness stamp); pass dataSource:"live" only when you need up-to-the-minute data (slower). Results are capped (see rowCount/truncated) — if truncated, narrow the question or ask for an aggregate. Multi-company tokens: COMBINED queries across your companies run on the WAREHOUSE only — for live/real-time data, query ONE company at a time (pass a single companyId) and combine across companies yourself. When a multi-company answer breaks down BY company, the result includes a companies map ([{id,code,name,currency}]) — label each row with the company code/name (never the numeric companyId), and show money in each company's own currency; never sum money across currencies.
build_card
ChatGPTSave a question (or the result you just got from ask_data) as a Metabase CARD (a single chart/ table), so it can be revisited, scheduled, rendered as a picture, or assembled into a dashboard with create_dashboard. Pass EITHER a question (I generate, validate + save it) OR the sql + signedSql from a prior ask_data result. Single-tenant: the card lands in that tenant's collection with the companyId HARDCODED in the SQL (tenant-scoped by construction — never a selectable filter, so it can't be re-pointed at another tenant). Multi-company tokens: pass companyIds (a SUBSET) to build a COMBINED multi-company card — WAREHOUSE-ONLY, lands in your private multi-company (staff) collection, never a single tenant's. Returns the cardId + a viewable embed URL. (To assemble several saved cards into a dashboard, use create_dashboard.) Chart cards auto-format: data labels, compact numbers (21.94B / 34.6%), month-on-x YoY with the years side-by-side, and a metric-themed colour (revenue=blue, cost/overhead=amber, profit=green/violet). For a KPI tile showing a single number with its YoY change (e.g. "Revenue 85.8B ▲ +35%"), use display="smartscalar" with a per-PERIOD query (one row per year/month).
build_card
ChatGPTSave a question (or the result you just got from ask_data) as a Metabase CARD (a single chart/ table), so it can be revisited, scheduled, rendered as a picture, or assembled into a dashboard with create_dashboard. Pass EITHER a question (I generate, validate + save it) OR the sql + signedSql from a prior ask_data result. Single-tenant: the card lands in that tenant's collection with the companyId HARDCODED in the SQL (tenant-scoped by construction — never a selectable filter, so it can't be re-pointed at another tenant). Multi-company tokens: pass companyIds (a SUBSET) to build a COMBINED multi-company card — WAREHOUSE-ONLY, lands in your private multi-company (staff) collection, never a single tenant's. Returns the cardId + a viewable embed URL. (To assemble several saved cards into a dashboard, use create_dashboard.) Chart cards auto-format: data labels, compact numbers (21.94B / 34.6%), month-on-x YoY with the years side-by-side, and a metric-themed colour (revenue=blue, cost/overhead=amber, profit=green/violet). For a KPI tile showing a single number with its YoY change (e.g. "Revenue 85.8B ▲ +35%"), use display="smartscalar" with a per-PERIOD query (one row per year/month).
cancel_report
ChatGPTStop + delete a scheduled report (Metabase subscription) by reportId (from list_reports). For an admin / multi-company token, pass the same companyId you listed under. You can only cancel reports for a company you are authorized for.
cancel_report
ChatGPTStop + delete a scheduled report (Metabase subscription) by reportId (from list_reports). For an admin / multi-company token, pass the same companyId you listed under. You can only cancel reports for a company you are authorized for.
create_dashboard
ChatGPTAssemble EXISTING saved cards (from build_card) into a Metabase DASHBOARD in your tenant's own collection — the multi-card companion to build_card. Build the cards first with build_card, then pass the dashboard name and a cards array. Each item is EITHER a chart/table ({ cardId, display? }) OR a markdown text/heading block ({ text }) for a title, section header, or an executive-insight note — so you can lay out a designed, sectioned dashboard. Omit row/col/sizeX/sizeY for AUTOMATIC layout, or give ALL FOUR on EVERY item for a precise, screenshot-style layout (text blocks make great full-width section headers, e.g. row spanning sizeX:24 sizeY:1-2). Every cardId must be one you are authorized for and within this dashboard's tenant (anti-IDOR; you cannot place another tenant's card). Chart cards auto-format their axes + data labels + compact numbers. Returns the dashboardId + a viewable embed URL.
create_dashboard
ChatGPTAssemble EXISTING saved cards (from build_card) into a Metabase DASHBOARD in your tenant's own collection — the multi-card companion to build_card. Build the cards first with build_card, then pass the dashboard name and a cards array. Each item is EITHER a chart/table ({ cardId, display? }) OR a markdown text/heading block ({ text }) for a title, section header, or an executive-insight note — so you can lay out a designed, sectioned dashboard. Omit row/col/sizeX/sizeY for AUTOMATIC layout, or give ALL FOUR on EVERY item for a precise, screenshot-style layout (text blocks make great full-width section headers, e.g. row spanning sizeX:24 sizeY:1-2). Every cardId must be one you are authorized for and within this dashboard's tenant (anti-IDOR; you cannot place another tenant's card). Chart cards auto-format their axes + data labels + compact numbers. Returns the dashboardId + a viewable embed URL.
data_freshness
ChatGPTReport how current the fast warehouse mirror is (when its ETL last ran, and whether it is healthy). Use this to stamp or caveat a warehouse answer, or to decide whether to rerun a question with dataSource:"live". Tenant-agnostic (it is about ETL recency, not any one tenant's rows).
data_freshness
ChatGPTReport how current the fast warehouse mirror is (when its ETL last ran, and whether it is healthy). Use this to stamp or caveat a warehouse answer, or to decide whether to rerun a question with dataSource:"live". Tenant-agnostic (it is about ETL recency, not any one tenant's rows).
edit_card
ChatGPTModify an existing Metabase card through the SAME governed pipeline as ask_data (generate/validate + tenant-scope check) — the safe alternative to raw Metabase admin. Pass an instruction (natural language, e.g. "group by month" or "only FCL shipments") OR sql (+ signedSql from a prior ask_data). The edit ALWAYS stays scoped to the CARD's OWN company — you cannot retarget a card to another tenant. You may edit any card within your authorized scope, including cards created outside this tool, as long as the card's collection maps to a company you're authorized for. Read it first with get_card and check its scopeGuard: if the card's CURRENT SQL fails the tenant-scope guard (legacy hand-written cards often do), an instruction edit will ALSO fix the scoping as part of the same update (compliance upgrade — the saved SQL is always re-validated, fail-closed, so only compliant SQL can land). Expect a larger diff in that case; if generation still can't produce compliant SQL you get the guard's verdict — retry, or describe the FULL desired query to ask_data and pass its sql + signedSql here. Note: instruction edits run a full generation (can take 60-90s); prefer the sql+signedSql path when you already have the query.
edit_card
ChatGPTModify an existing Metabase card through the SAME governed pipeline as ask_data (generate/validate + tenant-scope check) — the safe alternative to raw Metabase admin. Pass an instruction (natural language, e.g. "group by month" or "only FCL shipments") OR sql (+ signedSql from a prior ask_data). The edit ALWAYS stays scoped to the CARD's OWN company — you cannot retarget a card to another tenant. You may edit any card within your authorized scope, including cards created outside this tool, as long as the card's collection maps to a company you're authorized for. Read it first with get_card and check its scopeGuard: if the card's CURRENT SQL fails the tenant-scope guard (legacy hand-written cards often do), an instruction edit will ALSO fix the scoping as part of the same update (compliance upgrade — the saved SQL is always re-validated, fail-closed, so only compliant SQL can land). Expect a larger diff in that case; if generation still can't produce compliant SQL you get the guard's verdict — retry, or describe the FULL desired query to ask_data and pass its sql + signedSql here. Note: instruction edits run a full generation (can take 60-90s); prefer the sql+signedSql path when you already have the query.
get_card
ChatGPTRead the definition (name, SQL, display type, company) of an existing Metabase card you are authorized to see — your own tenant's cards, or (multi-company tokens) any of your companies' cards. Use this before edit_card. The response includes scopeGuard {safe, reason}: when safe=false the card's CURRENT SQL predates the tenant-scope guard — an instruction-based edit_card will then ALSO fix the scoping as part of the edit (compliance upgrade), so expect a larger SQL diff than the instruction alone implies. You cannot read a card outside your authorized scope.
get_card
ChatGPTRead the definition (name, SQL, display type, company) of an existing Metabase card you are authorized to see — your own tenant's cards, or (multi-company tokens) any of your companies' cards. Use this before edit_card. The response includes scopeGuard {safe, reason}: when safe=false the card's CURRENT SQL predates the tenant-scope guard — an instruction-based edit_card will then ALSO fix the scoping as part of the edit (compliance upgrade), so expect a larger SQL diff than the instruction alone implies. You cannot read a card outside your authorized scope.
get_card_image
ChatGPTGet a viewable image + signed embed URL for one of YOUR saved cards (from build_dashboard). Returns a PNG (rendered server-side by Metabase, tenant-scoped) when available, plus an embed URL that opens the live card in a browser. Use this to SHOW the user a chart, not just rows.
get_card_image
ChatGPTGet a viewable image + signed embed URL for one of YOUR saved cards (from build_dashboard). Returns a PNG (rendered server-side by Metabase, tenant-scoped) when available, plus an embed URL that opens the live card in a browser. Use this to SHOW the user a chart, not just rows.
list_companies
ChatGPTReturn the companyId(s) this analytics token is authorized to read. A single-tenant token returns its one company; a multi-company token returns its set (pass one as ask_data's companyId to narrow, or omit to query the combined set); a team token can query ANY company (you must pass an explicit companyId to ask_data). Each company comes with its code (e.g. "DGFMY") and name in companies — show THOSE to the user, never the bare numeric companyId.
list_companies
ChatGPTReturn the companyId(s) this analytics token is authorized to read. A single-tenant token returns its one company; a multi-company token returns its set (pass one as ask_data's companyId to narrow, or omit to query the combined set); a team token can query ANY company (you must pass an explicit companyId to ask_data). Each company comes with its code (e.g. "DGFMY") and name in companies — show THOSE to the user, never the bare numeric companyId.
list_reports
ChatGPTList the recurring email reports (Metabase subscriptions) for your tenant — or, for an admin / multi-company token, a client you name via companyId. Shows name, cadence, recipients. Use cancel_report to stop one.
list_reports
ChatGPTList the recurring email reports (Metabase subscriptions) for your tenant — or, for an admin / multi-company token, a client you name via companyId. Shows name, cadence, recipients. Use cancel_report to stop one.
schedule_report
ChatGPTTurn a question (or a saved card) into a RECURRING email report for your tenant — or, for an admin / multi-company token, a client you name via companyId (on their behalf; it lands in THEIR Metabase). Pass question (or cardId, or sql+signedSql) + name + cadence (daily/weekly/monthly). Recipients DEFAULT to your own email; you may pass recipients but only same-domain addresses are allowed (anti-spam) and at most 5. ALWAYS run with dryRun:true first and show the user what would be scheduled (cadence + recipients), then re-run with dryRun:false to actually arm it. Returns the reportId + when it next sends. Manage with list_reports / cancel_report.
schedule_report
ChatGPTTurn a question (or a saved card) into a RECURRING email report for your tenant — or, for an admin / multi-company token, a client you name via companyId (on their behalf; it lands in THEIR Metabase). Pass question (or cardId, or sql+signedSql) + name + cadence (daily/weekly/monthly). Recipients DEFAULT to your own email; you may pass recipients but only same-domain addresses are allowed (anti-spam) and at most 5. ALWAYS run with dryRun:true first and show the user what would be scheduled (cadence + recipients), then re-run with dryRun:false to actually arm it. Returns the reportId + when it next sends. Manage with list_reports / cancel_report.
send_report_now
ChatGPTFetch a card's current data and EMAIL IT ONCE, right now (no schedule). Pass question (or cardId, or sql+signedSql) + name (the email subject). Recipients DEFAULT to your own email; explicit recipients must be same-domain (anti-spam) and at most 5. Use this for "email me this now"; use schedule_report for a recurring send.
send_report_now
ChatGPTFetch a card's current data and EMAIL IT ONCE, right now (no schedule). Pass question (or cardId, or sql+signedSql) + name (the email subject). Recipients DEFAULT to your own email; explicit recipients must be same-domain (anti-spam) and at most 5. Use this for "email me this now"; use schedule_report for a recurring send.
whoami
ChatGPTReturn the resolved principal for the calling analytics token: realm, role, and the server-derived scope your queries are bound to — scope.type is "fixed" (one company), "set" (several — see scope.companyIds), or "all" (any company, team tokens). Call this first to confirm your MCP token authenticated and what it can read; use list_companies for the company list and pass companyId to ask_data to narrow.
whoami
ChatGPTReturn the resolved principal for the calling analytics token: realm, role, and the server-derived scope your queries are bound to — scope.type is "fixed" (one company), "set" (several — see scope.companyIds), or "all" (any company, team tokens). Call this first to confirm your MCP token authenticated and what it can read; use list_companies for the company list and pass companyId to ask_data to narrow.