Bonnard

A workspace connection offers an agent, the model in an AI client that acts for a person, up to 17 tools. The AI tools settings in the console at app.bonnard.ai and the caller’s workspace role decide which ones.

Verified 9 Nov 2026.

Each description and input schema is the text the agent receives in tools/list, for one PostgreSQL data source with all settings on. The dialect line changes with the data source: go to SQL dialect by engine.

Anyone connected means a workspace member, a customer’s user, or an API key. Editor or higher is a workspace role. intent is in a tool’s input only while Call intent is on.

Parameter paths: a.b is a field of a, and a[] is one element of the array a. Where a value takes more than one shape, a[kind=chart] is the shape whose kind is chart, and a[sql] is the shape that carries sql.

Tools at a glance

The tools at a glance
ToolTitleSettingWho gets it
explore_schemaExplore schemaExplore schema, always onAnyone connected
sql_queryRun SQLSQL query, always onAnyone connected
render_chartRender chartCharts, on by defaultAnyone connected
fetch_view_dataFetch view dataCharts or Dashboards, on by defaultThe widget in the AI client
create_export_fileCreate export fileExport data, on by defaultAnyone connected
save_dashboardSave dashboardDashboards, on by defaultAnyone connected
delete_dashboardDelete dashboardDashboards, on by defaultAnyone connected
save_chartSave chartSave charts and Export data, on by defaultAnyone connected
explore_chartsExplore chartsSave charts, on by defaultAnyone connected
delete_chartDelete chartSave charts, on by defaultAnyone connected
explore_docsExplore docsDocs, always onAnyone connected, while Charts or Dashboards is on. Editor or higher gets it with both off.
explore_dashboardsExplore dashboardsDashboards, on by defaultAnyone connected
render_dashboardRender dashboardDashboards, on by defaultAnyone connected
search_param_valuesSearch filter valuesDashboards, on by defaultThe widget in the AI client
get_versionsGet context versionsNoneEditor or higher
edit_draftEdit the context draftNoneEditor or higher
publish_draftPublish the context draftAllow AI clients to publish context changes, off by defaultEditor or higher

explore_schema

explore_schema at a glance
FactValue
SettingExplore schema, always on
Who gets itAnyone connected
Annotationstitle: Explore schema, readOnlyHint: true, destructiveHint: false, openWorldHint: false

Description as sent:

“Discover the data. Call with no arguments first for an overview of the data source: the most-connected tables and a total count. Pass search to find tables related to a concept when you don’t know the exact name, table for one table’s full detail (columns, types, row count, sample rows), or full: true to list every table. Table and column names are shown exactly as they must appear in SQL, already quoted where the database requires it. Paste them verbatim; do not add, remove, or change quoting or case.”

Editor or higher gets this text at the end of the description:

“The user can edit this data source’s context, so the overview also reports its gaps and each table’s key, and a table’s detail gives each description, key, time column and join a from (edit, source or catalog), with * where the draft changes it, plus the drafted description, key or time column.”

explore_schema parameters
ParameterTypeNeededDescription
databasestringNo“Data source key, from explore_schema”
searchstringNoQuoted below this table.
tablestringNo“Exact table name to inspect in full: columns, types, row count, and sample rows. Ignored if search is also given; takes precedence over full.”
fullbooleanNo“List every table instead of the default overview. Only applies when search and table are both omitted.”
offsetinteger (min 0)No“Start the full: true listing at this position instead of the beginning. The listing is paged: each response reports listing.nextOffset when more tables remain. Ignored unless full is set.”
includeHiddenbooleanNo“Only on data sources with an event model (PostHog). Skip this source’s curation for this call and list every table (and, with event, every property). Default false.”
eventstringNo“Only on data sources with an event model (PostHog). Exact name of one event, for the properties it carries. Not a search: pass a name you already know. To find one, query the event column directly. Takes precedence over search, table and full.”
metricstringNo“Key of one defined metric, for its calculation and instructions. The overview lists the keys; a data source without metrics says so. Takes precedence over search, table and full.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

search, as sent:

“Find tables when you don’t know the exact name: by concept, keyword, or column. Takes precedence over table and full.”

sql_query

sql_query at a glance
FactValue
SettingSQL query, always on
Who gets itAnyone connected
Annotationstitle: Run SQL, readOnlyHint: true, destructiveHint: false, openWorldHint: false

Description as sent:

“Execute a single read-only SQL SELECT and return rows. Discover names with explore_schema first. Results are capped; add LIMIT/OFFSET to page. Prefer aggregating or filtering in SQL over pulling raw rows: the preview shows at most 250 of them, while the whole bounded result (up to 5000 rows, or 500000 with max_rows) is fetched and captured regardless of what the preview carries. When returned, resultRef identifies the captured bounded rows; pass it to create_export_file instead of rerunning SQL. Set capture_only: true when you only need that reference, to skip the row preview, and max_rows when the capture needs more rows than the standard ceiling holds. Table and column names are shown exactly as they must appear in SQL, already quoted where the database requires it. Paste them verbatim; do not add, remove, or change quoting or case. Dialect: PostgreSQL. Identifier quote: “double quotes”. Dates: date_trunc(‘month’, col), now() - interval ‘7 days’. Cast: col::type. Nulls: coalesce(a, b).”

sql_query parameters
ParameterTypeNeededDescription
databasestringNo“Data source key, from explore_schema”
sqlstringYes“A single read-only SELECT statement”
capture_onlybooleanNo“Return the capture only: resultRef, fields, and row counts, with no rows. Set true when you are capturing a result to hand to create_export_file and will not read the values yourself. Leave unset whenever you intend to inspect, summarise, or chart the rows — this mode returns none. Pairs with max_rows for a bulk capture.”
max_rowsinteger (greater than 0)No“Fetch and capture up to this many rows instead of the standard 5000, for a bulk export. Ceiling: 500000. The preview still never shows more than 250 rows regardless of this value, so pair it with capture_only to skip building one you will not use.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

capture_only and max_rows are in the input only while Export data is on.

render_chart

render_chart at a glance
FactValue
SettingCharts, on by default
Who gets itAnyone connected
Annotationstitle: Render chart, readOnlyHint: true, destructiveHint: false, openWorldHint: false
Renders inui://bonnard/view.html

Description as sent:

“Render one chart, or a carousel of up to 10, and show them to the user. Each entry in charts is either a chart the user saved ({ chart_id }) or a chart drawn from a read-only SQL SELECT now ({ sql, chartType?, title?, encode?, fields?, … }); the two kinds mix freely. The input is always {“charts”:[…]}, even for one chart. One entry shows one chart; two or more show a carousel the user pages through in the order given. For an ad-hoc chart, consider running the query through sql_query first to check the shape of the results. Charts take no params: write literal values. The reply summarises what was drawn with a row preview; the user sees the whole chart. Every chart returns its own resultRef and snapshotRef; pass one to create_export_file to download that chart’s data or image, or to save_chart to keep the chart. The charts the user saved are listed by explore_charts. Dialect: PostgreSQL. Identifier quote: “double quotes”. Dates: date_trunc(‘month’, col), now() - interval ‘7 days’. Cast: col::type. Nulls: coalesce(a, b).”

render_chart parameters
ParameterTypeNeededDescription
databasestringNo“Data source key, from explore_schema”
chartsarray of objects (1 to 10 items)YesQuoted below this table.
charts[chart_id].chart_idstringYes“A chart the user saved, by the id save_chart returned or explore_charts lists. Runs its stored query again.”
charts[sql].sqlstringYes“A single read-only SQL SELECT statement.”
charts[sql].chartTypeone of line, bar, area, pie, scatter, funnel, waterfall, tableNo“line, bar, area, pie, scatter, funnel, waterfall or table; omit to infer from the columns. Every option a type takes, with an example: explore_docs topic “charts/<type>”, e.g. explore_docs({“topic”:“charts/bar”}). Choosing a type: topic “charts”.”
charts[sql].titlestringNo“The chart title.”
charts[sql].encodeobjectNoQuoted below this table.
charts[sql].encode.xstringNo
charts[sql].encode.ystring or array of stringsNo
charts[sql].encode.seriesstringNo
charts[sql].fieldsarray of objectsNo“Formats are never inferred from column names: declare percent, currency or compact per column, e.g. [{“name”:“revenue”,“format”:“currency”,“currency”:“GBP”},{“name”:“gm”,“format”:“percent”,“label”:“Gross margin %”}]. A column alias with spaces or capitals (“Gross margin”) is shown exactly as written; a bare snake_case name only has its underscores spaced and its first letter capitalised. Put a title with % or other symbols in label, not in the SQL alias.”
charts[sql].fields[].namestringYes
charts[sql].fields[].labelstringNo
charts[sql].fields[].formatone of number, percent, currency, compactNo
charts[sql].fields[].currencystringNo
preview_rowsinteger (0 to 150)No“Rows of each chart the reply previews: default 10, up to 150.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

charts, as sent:

“A native JSON array of 1 to 10 charts, never a JSON-encoded string. Each entry is either { chart_id } for a chart the user saved, or { sql, chartType?, title?, encode?, fields?, … } for a chart drawn from a query now; the two kinds may mix. Stacking, horizontal bars, a secondary axis, a line over bars, the x-axis scale, reference lines, axis titles and the funnel style apply to some types only: the type’s docs page lists exactly what it takes, and an option that does not fit the type is rejected with the valid list.”

charts[sql].encode, as sent:

“Map columns to the chart: x is the single column for the x-axis, y one measure or a list, series a column to split one measure by. Set x whenever more than one column could plausibly be it, e.g. SELECT COUNT(*) AS n, year FROM t GROUP BY year (set x: “year”), where a wrong guess would plot the count instead. encode.line and encode.y2 mix a line into bars or add a secondary axis: see the type’s docs page.”

fetch_view_data

fetch_view_data at a glance
FactValue
SettingCharts or Dashboards, on by default
Who gets itThe widget in the AI client
Annotationstitle: Fetch view data, readOnlyHint: true, destructiveHint: false, openWorldHint: false
Visibilityapp

Description as sent:

“Loads a rendered chart or dashboard’s data into the chart widget, or re-renders a dashboard for a filter pick. For the widget only: to see a chart, call render_chart or render_dashboard.”

fetch_view_data parameters
ParameterTypeNeededDescription
refstring (max 200 chars)No“The viewRef a render_chart or render_dashboard result carries.”
dashboard_idstring (max 64 chars)No“Render this dashboard live instead, for a filter pick.”
paramsobjectNo“With dashboard_id: the param values, as render_dashboard takes them.”
item_idstring (max 64 chars)No“With dashboard_id: one chart cell.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

create_export_file

create_export_file at a glance
FactValue
SettingExport data, on by default
Who gets itAnyone connected
Annotationstitle: Create export file, readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false

Description as sent:

“Create a CSV (csv, or gzip-compressed csv.gz) from one resultRef or a PNG from one snapshotRef without rerunning the query. The file holds exactly the rows its capture holds, including a bulk capture taken with sql_query max_rows. resources/read the returned artifactUri to get the exact bytes, then open, save, or use it in the workspace.”

create_export_file parameters
ParameterTypeNeededDescription
databasestringNo“Data source key, from explore_schema”
resultRefstringNo“Captured tabular result reference (result://…). Use this with format csv or csv.gz. Provide exactly one of resultRef or snapshotRef.”
snapshotRefstringNo“Captured chart or dashboard reference (snapshot://…). Use this with format png. Provide exactly one of resultRef or snapshotRef.”
deliverone of reference, inlineNo“reference (default): the reply names the file and you read its bytes with resources/read. inline: the bytes also ride in this reply, for a client that cannot read resources.”
formatstringYes“Output format: csv or csv.gz (gzip-compressed) for resultRef, png for snapshotRef. Prefer csv.gz when the export is large (rows_available exceeded rows_shown on the call that produced this resultRef) and you can gunzip it yourself. Use plain csv for small results, or when the file goes to a person or a UI unprocessed.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

save_dashboard

save_dashboard at a glance
FactValue
SettingDashboards, on by default
Who gets itAnyone connected
Annotationstitle: Save dashboard, readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false

Description as sent:

“Save a dashboard for this user, or edit one they already saved. The id it is stored under is returned; render it with render_dashboard and that id. Omit dashboard_id to create a new one, and send one replace_all with title, a one-line description and items, the tiles in display order: {“operations”:[{“op”:“replace_all”,“title”:“Sales”,“description”:“Revenue by month.”,“items”:[…tiles]}]}. Every change goes inside operations; nothing else sits at the top level beside dashboard_id and expected_version. To edit, pass that dashboard_id together with the expected_version from the last save (or from explore_dashboards), which is refused if someone else changed it first. Read the dashboard first with explore_dashboards and its dashboard_id: an edit replaces the whole tile, so pass its SQL back unless you mean to change it. A version_conflict means it changed: re-read it and reapply your change. Chart and KPI options: docs charts/<type> (e.g. charts/line) and charts/kpi; params and optional [[ ]] filters: docs dashboards/params; open them directly, e.g. explore_docs({“topic”:[“charts/kpi”,“charts/line”]}). The SQL of the tiles you add or edit is run once as you save, and any that fail are named in the reply; the save succeeds either way, and render_dashboard shows each error in place. Dialect: PostgreSQL. Identifier quote: “double quotes”. Dates: date_trunc(‘month’, col), now() - interval ‘7 days’. Cast: col::type. Nulls: coalesce(a, b).”

save_dashboard parameters
ParameterTypeNeededDescription
databasestringNo“Data source key, from explore_schema”
dashboard_idstringNo“The dashboard to edit, as returned by a previous save_dashboard. Omit to create a new one.”
operationsarray of objects (1 to 20 items)Yes“A native JSON array of operation objects. Never a JSON-encoded string. At most 20 per call. The same add / edit / move / remove / set_metadata / replace_all batch the dashboard editor uses; a replace_all must be the only operation in its batch.”
operations[].opone of add, edit, move, remove, set_metadata, replace_allYesQuoted below this table.
operations[].idstring (max 64 chars, pattern ^[a-z][a-z0-9_-]*$)No“The id of the tile to edit, move or remove.”
operations[].itemsarray of objects (1 to 20 items)NoQuoted below this table.
operations[].items[kind=chart].kindchartYes
operations[].items[kind=chart].idstring (max 64 chars, pattern ^[a-z][a-z0-9_-]*$)Yes
operations[].items[kind=chart].sqlstring (max 8000 chars)Yes“A read-only SELECT; {{name}} binds a declared param, and [[ AND … ]] makes a condition optional.”
operations[].items[kind=chart].titlestring (max 200 chars)No“The chart tile’s display name. May include a declared {{param}}.”
operations[].items[kind=chart].chartTypeone of line, bar, area, pie, scatter, funnel, waterfall, tableNo“line, bar, area, pie, scatter, funnel, waterfall or table; omit to infer from the columns. Every option a type takes, with an example: explore_docs topic “charts/<type>”, e.g. explore_docs({“topic”:“charts/bar”}). Choosing a type: topic “charts”.”
operations[].items[kind=chart].encodeobjectNoQuoted below this table.
operations[].items[kind=chart].encode.xstringNo
operations[].items[kind=chart].encode.ystring or array of stringsNo
operations[].items[kind=chart].encode.seriesstringNo
operations[].items[kind=chart].fieldsarray of objectsNo“Formats are never inferred from column names: declare percent, currency or compact per column, e.g. [{“name”:“revenue”,“format”:“currency”,“currency”:“GBP”},{“name”:“gm”,“format”:“percent”,“label”:“Gross margin %”}]. A column alias with spaces or capitals (“Gross margin”) is shown exactly as written; a bare snake_case name only has its underscores spaced and its first letter capitalised. Put a title with % or other symbols in label, not in the SQL alias.”
operations[].items[kind=chart].fields[].namestringYes
operations[].items[kind=chart].fields[].labelstringNo
operations[].items[kind=chart].fields[].formatone of number, percent, currency, compactNo
operations[].items[kind=chart].fields[].currencystringNo
operations[].items[kind=chart].spaninteger (1 to 4)NoQuoted below this table.
operations[].items[kind=kpi].kindkpiYes
operations[].items[kind=kpi].idstring (max 64 chars, pattern ^[a-z][a-z0-9_-]*$)Yes
operations[].items[kind=kpi].sqlstring (max 8000 chars)Yes“A read-only SELECT whose first row is read; {{name}} binds a declared param.”
operations[].items[kind=kpi].titlestring (max 200 chars)Yes“The KPI’s display name. May include a declared {{param}}.”
operations[].items[kind=kpi].valueColumnstring (max 256 chars)No
operations[].items[kind=kpi].deltaColumnstring (max 256 chars)No
operations[].items[kind=kpi].formatone of number, currency, percent, compactNo
operations[].items[kind=kpi].currencystring (max 64 chars)No
operations[].items[kind=kpi].captionstring (max 2000 chars)No“A short line under the KPI value. May include a declared {{param}}.”
operations[].items[kind=kpi].spaninteger (1 to 4)No
operations[].items[kind=text].kindtextYes
operations[].items[kind=text].idstring (max 64 chars, pattern ^[a-z][a-z0-9_-]*$)Yes
operations[].items[kind=text].textstring (max 8000 chars)Yes“The text tile’s body (markdown supported). This field is called text, not body or content. Text tiles run no SQL.”
operations[].items[kind=text].headingstring (max 200 chars)No“The text tile’s optional heading. May include a declared {{param}}.”
operations[].items[kind=text].spaninteger (1 to 4)No
operations[].placement{"position":"start"} or {"position":"end"} or objectNo“Where the tile goes: the start, the end, or after a named tile: {“position”:“start”}, {“position”:“end”} or {“position”:“after”,“afterId”:“by_month”}.”
operations[].placement[position=after].positionafterYes
operations[].placement[position=after].afterIdstring (max 64 chars, pattern ^[a-z][a-z0-9_-]*$)Yes
operations[].titlestring (max 200 chars)No“The dashboard’s display name. May include a declared {{param}}.”
operations[].descriptionstring (max 500 chars)No“A one-line summary of the dashboard, shown by explore_dashboards.”
operations[].columnsanyNo“Ignored: the grid is fixed at 4.”
operations[].paramsarray of objects (max 100 items) or nullNo“A dashboard can declare params. In tile SQL, {{name}} binds a declared param, and [[ AND … ]] around a condition makes it optional so viewers can pick All: WHERE 1=1 [[ AND region = {{region}} ]]. null clears them all. Read docs dashboards/params before declaring one; open it directly with explore_docs.”
operations[].params[].namestring (max 128 chars, pattern ^[a-z_][a-z0-9_]*$)Yes“Used in SQL as {{name}}.”
operations[].params[].labelstring (1 to 60 chars)No“Short noun viewers see; plural for a filter with All.”
operations[].params[].typeone of string, number, dateYes“string, number or date (YYYY-MM-DD): the compared column’s type.”
operations[].params[].requiredbooleanNo“true: nothing loads until a value is chosen.”
operations[].params[].defaultstring (max 1000 chars) or number or {"mode":"first"}No“A literal of the type, or {“mode”:“first”}.”
operations[].params[].descriptionstring (max 2000 chars)No“One sentence: how a caller picks a value.”
operations[].params[].valuesFromobjectNo“{table, column} the filter’s values come from.”
operations[].params[].valuesFrom.tablestring (max 800 chars)Yes“The table holding the values, as explore_schema names it.”
operations[].params[].valuesFrom.columnstring (max 800 chars)Yes“The column holding the values.”
operations[].params[].sortByobjectNo“{column, direction} ordering those values.”
operations[].params[].sortBy.columnstring (max 800 chars)Yes“A column of valuesFrom.table to order by.”
operations[].params[].sortBy.directionone of asc, descYes““desc” or “asc”.”
expected_versionintegerNo“Required when editing: the version the last save returned, or the version field explore_dashboards reports for this dashboard. Omit when creating.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

operations[].op, as sent:

“Which edit to make, and what the other fields mean. add takes items (exactly one tile), placement; edit takes id, items (exactly one tile); move takes id, placement; remove takes id; set_metadata takes title, description, columns, params; replace_all takes items, title, description, columns, params. A field that belongs to another op is rejected, as is a missing one.”

operations[].items, as sent:

“The tiles, always an array, even for one: {“op”:“edit”,“id”:“by_month”,“items”:[{“kind”:“chart”,“id”:“by_month”, …the complete tile}]}. add: the one tile to add. edit: the one replacement tile, whose id must equal id. replace_all: the complete new set of tiles, in display order. Every tile has kind (chart, kpi or text), an id (lowercase letters, digits, _ and -, starting with a letter, at most 64 characters; edit, move, remove and placement refer to tiles by it) and an optional span. A dashboard holds at most 16 chart/KPI tiles, counted across the whole dashboard once your batch is applied; text tiles do not count. Rename a chart tile with an edit that changes its title, a KPI with its title; rename the dashboard with set_metadata.”

operations[].items[kind=chart].encode, as sent:

“Map columns to the chart: x is the single column for the x-axis, y one measure or a list, series a column to split one measure by. Set x whenever more than one column could plausibly be it, e.g. SELECT COUNT(*) AS n, year FROM t GROUP BY year (set x: “year”), where a wrong guess would plot the count instead. encode.line and encode.y2 mix a line into bars or add a secondary axis: see the type’s docs page.”

operations[].items[kind=chart].span, as sent:

“How many of the 4 grid columns this tile spans, 1 to 4; 4 is full width and 2 half. A chart or text tile added without a span gets 2, a KPI 1, and an edit that omits span keeps the tile’s width. columns is ignored: the grid is fixed at 4.”

delete_dashboard

delete_dashboard at a glance
FactValue
SettingDashboards, on by default
Who gets itAnyone connected
Annotationstitle: Delete dashboard, readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false

Description as sent:

“Permanently delete one of this user’s own saved dashboards. Only dashboards they saved themselves can be deleted; verified dashboards and dashboards shared with the user are not yours to remove. Pass the expected_version from explore_dashboards or the last save, which is refused if it has changed since. This cannot be undone, so confirm with the user first.”

delete_dashboard parameters
ParameterTypeNeededDescription
databasestringNo“Data source key, from explore_schema”
dashboard_idstring (max 64 chars, pattern ^[a-z][a-z0-9_-]*$)Yes“The dashboard to delete, one of this user’s own saved ones as explore_dashboards lists them.”
expected_versionintegerYes“The version the dashboard is currently at, as explore_dashboards reports it in its version field.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

save_chart

save_chart at a glance
FactValue
SettingSave charts and Export data, on by default
Who gets itAnyone connected
Annotationstitle: Save chart, readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false

Description as sent:

“Save a chart for this user, or edit one they already saved. To save the chart render_chart just showed, pass its snapshotRef (or resultRef, or the viewRef of a single chart) and a title (an optional description helps you find it again later via explore_charts); the chart is stored exactly as it was drawn and its id is returned. To edit, pass that chart_id with the expected_version from the last save or from explore_charts (refused if it changed since), and the new title/description and/or a reference from a fresh render_chart with the new query. References last 30 minutes; after that, render the chart again and save the new reference. One chart per call: to keep several charts from a carousel, call save_chart once for each.”

save_chart parameters
ParameterTypeNeededDescription
databasestringNo“Data source key, from explore_schema”
chart_idstringNo“The chart to edit, as returned by a previous save_chart or listed by explore_charts. Omit to save a new one.”
expected_versionintegerNo“Required when editing: the version the last save returned, or the version explore_charts reports for this chart. Omit when creating.”
titlestring (max 200 chars)No“The title to store. May be omitted when the rendered chart already has one; otherwise required.”
descriptionstring (max 500 chars)No“Stored so explore_charts and search can match this chart by what it is for, not just its title. Pass an empty string to clear an existing one.”
snapshotRefstringNo“A snapshotRef from a render_chart response, or the viewRef of a one-chart render: saves exactly the chart that produced it. Provide at most one of snapshotRef or resultRef.”
resultRefstringNo“A resultRef from a render_chart response, accepted the same way as snapshotRef.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

explore_charts

explore_charts at a glance
FactValue
SettingSave charts, on by default
Who gets itAnyone connected
Annotationstitle: Explore charts, readOnlyHint: true, destructiveHint: false, openWorldHint: false

Description as sent:

“List the charts this user saved (id, title, description, chart type, version). Show one with render_chart and its chart_id, edit or delete it with the version reported here. Pass chart_id to read one chart instead of the list; that returns its full definition, the SQL and the chart options included, in the exact shape render_chart takes as an entry. Dashboards are separate: explore_dashboards lists those.”

explore_charts parameters
ParameterTypeNeededDescription
databasestringNo“Data source key, from explore_schema”
chart_idstringNo“Read this one chart instead of listing them all. Omit to list.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

delete_chart

delete_chart at a glance
FactValue
SettingSave charts, on by default
Who gets itAnyone connected
Annotationstitle: Delete chart, readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false

Description as sent:

“Permanently delete one of this user’s own saved charts. Pass the expected_version from explore_charts or the last save, which is refused if it has changed since. This cannot be undone, so confirm with the user first.”

delete_chart parameters
ParameterTypeNeededDescription
databasestringNo“Data source key, from explore_schema”
chart_idstring (max 64 chars, pattern ^cht_[a-z0-9_-]+$)Yes“The chart to delete, one of this user’s own saved ones as explore_charts lists them.”
expected_versionintegerYes“The version the chart is currently at, as explore_charts reports it in its version field.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

explore_docs

explore_docs at a glance
FactValue
SettingDocs, always on
Who gets itAnyone connected, while Charts or Dashboards is on. Editor or higher gets it with both off.
Annotationstitle: Explore docs, readOnlyHint: true, destructiveHint: false, openWorldHint: false

Description as sent:

“How-to docs for charts (render_chart) and dashboard params and KPIs (save_dashboard). The pages are fixed: charts (choosing a type), charts/<type> for line, bar, area, pie, scatter, funnel, waterfall and table (the options that type takes), charts/kpi and dashboards/params. Only when asked for them: charts/styling (colours, dashed lines, markers, highlight, legend) and charts/annotations (target, average and median lines, bands, trend lines and moving averages). Open the pages you need directly with topic, several at once as a list; search with query only when you do not know which page. Each page ends with examples that validate.”

Editor or higher gets this text at the end of the description:

“For editing this data source’s context: context/runbook, context/descriptions, context/joins, context/keys-and-time, context/metrics and context/publishing. Open context/runbook first.”

explore_docs parameters
ParameterTypeNeededDescription
topicstring (max 200 chars) or array of strings (1 to 4 items)NoQuoted below this table.
querystring (max 300 chars)No“What you want to do, in plain words, e.g. “dual axis” or “let viewers pick all regions”. Returns the best matching page and section ids.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

topic, as sent:

“A page id: “charts”, “charts/<type>” for line, bar, area, pie, scatter, funnel, waterfall or table, “charts/styling”, “charts/annotations”, “charts/kpi” or “dashboards/params”; add #section to open a page at a section, or pass a list of up to 4 ids. Takes precedence over query.”

explore_dashboards

explore_dashboards at a glance
FactValue
SettingDashboards, on by default
Who gets itAnyone connected
Annotationstitle: Explore dashboards, readOnlyHint: true, destructiveHint: false, openWorldHint: false

Description as sent:

“List the available dashboards (id, title, description, params). Call this first to discover what you can render, then call render_dashboard with a chosen dashboard_id. Pass dashboard_id to read one entry instead of the list; for a dashboard the user saved themselves that returns its full definition, every tile’s SQL included, which is what save_dashboard needs to edit it. For a dashboard that takes params, the detail read also lists each param’s description, default and up to 50 sample values. A param marked clearable is used only inside [[ ]] optional clauses, so it can show All; that is derived from the SQL, not something you set. Pass item_id to re-render a single chart cell by its id (ids appear in the dashboard summary), with preview_rows for its rows.”

explore_dashboards parameters
ParameterTypeNeededDescription
databasestringNo“Data source key, from explore_schema”
dashboard_idstringNo“Read this one dashboard in detail instead of listing them all. Omit to list.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

render_dashboard

render_dashboard at a glance
FactValue
SettingDashboards, on by default
Who gets itAnyone connected
Annotationstitle: Render dashboard, readOnlyHint: true, destructiveHint: false, openWorldHint: false
Renders inui://bonnard/view.html

Description as sent:

“Render one dashboard by dashboard_id. This displays it to the user directly, as an interactive chart or grid of tiles. Pass item_id to re-render a single chart cell by its id (ids appear in the dashboard summary), with preview_rows for its rows. When returned, resultRef and snapshotRef identify captured item data and render specs; pass one to create_export_file instead of rerunning the dashboard. Call explore_dashboards for the dashboards this user can render, the user’s own saved ones included; verified dashboards are listed first.”

render_dashboard parameters
ParameterTypeNeededDescription
databasestringNo“Data source key, from explore_schema”
dashboard_idstring (max 64 chars, pattern ^[a-z][a-z0-9_-]*$)Yes“The dashboard to render, from the ids explore_dashboards lists. Each call runs that dashboard’s stored queries live.”
paramsobjectNo“Values for the dashboard’s declared params, keyed by name (e.g. {“region”: “EU”}); undeclared names are rejected. An omitted param takes its starting value. To show all values for a param that explore_dashboards marks clearable, pass "" (or omit it when it starts as All). A required param must be passed. Each value is bound as a query parameter, so pass a literal, not a SQL fragment.”
item_idstringNo“The id of a single chart cell (from a previous render’s summary): renders that cell alone and runs only its query. KPI and text tiles are not selectable; an unknown id errors and lists the ids that are.”
preview_rowsinteger (0 to 150)No“With item_id, rows of that tile the reply previews: default 10, up to 150.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

search_param_values

search_param_values at a glance
FactValue
SettingDashboards, on by default
Who gets itThe widget in the AI client
Annotationstitle: Search filter values, readOnlyHint: true, destructiveHint: false, openWorldHint: false
Visibilityapp

Description as sent:

“Lists and searches one dashboard filter’s values for the dashboard widget’s filter menu. Not for answering questions: to see a dashboard’s params and sample values, call explore_dashboards with its dashboard_id instead.”

search_param_values parameters
ParameterTypeNeededDescription
databasestringNo“Data source key, from explore_schema”
dashboard_idstring (1 to 64 chars)Yes“The rendered dashboard.”
paramstring (1 to 64 chars)Yes“A declared param with a value source.”
querystring (max 200 chars)No“Case-insensitive text the value must contain. Omit to list in order.”
offsetinteger (0 to 500)No“Values to skip. Default 0.”
limitinteger (1 to 50)No“Values to return. Default 50.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

get_versions

get_versions at a glance
FactValue
SettingNone
Who gets itEditor or higher
Annotationstitle: Get context versions, readOnlyHint: true, destructiveHint: false, openWorldHint: false

Description as sent:

“Lists this data source’s context versions, the draft on top, and the changes in any one of them. Call with no arguments first, then with a version for the tables and metrics it changed, an object for that one’s changes, and a field for one change in full. Read explore_docs context/runbook before editing.”

get_versions parameters
ParameterTypeNeededDescription
databasestringNo“Data source key, from explore_schema”
versioninteger (greater than 0) or draftNo“A version number, or “draft”. Omit to list the versions.”
objectstring (max 300 chars)No“A table or metric the version changed, by name. Lists its changes.”
fieldstring (max 300 chars)No“One of that object’s changes, by its field or id. Returns it in full.”
incompletebooleanNo“With version “draft”: only the metrics a publish would hold back as incomplete.”
offsetinteger (min 0)No“Skip this many rows, to read the next page.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

edit_draft

edit_draft at a glance
FactValue
SettingNone
Who gets itEditor or higher
Annotationstitle: Edit the context draft, readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false

Description as sent:

“Edits this data source’s context draft: descriptions, keys, time columns, which tables AI clients see, joins and metrics, up to 100 operations in one call. Nothing is served to anyone until the draft is published. Each operation is checked on its own, so a refused one leaves the others applied. Read explore_docs context/runbook before editing.”

edit_draft parameters
ParameterTypeNeededDescription
databasestringNo“Data source key, from explore_schema”
operationsarray of objects (1 to 100 items)YesQuoted below this table.
operations[].opone of set_description, set_key, set_time_column, set_visible, add_join, remove_join, create_metric, update_metric, remove_metric, remove_change, revert_changeYes
operations[].tablestring (max 300 chars)No“A table, as explore_schema names it.”
operations[].columnstring (max 300 chars) or nullNo“A column of table. null clears a time column.”
operations[].columnsarray of strings or nullNo“set_key: the columns that identify a row. null clears the key.”
operations[].descriptionstring or nullNo“The text. null clears it.”
operations[].visiblebooleanNo“set_visible: false hides the table from discovery (listing, search, detail, metrics), not from queries; true shows it again.”
operations[].tostring (max 300 chars)No“add_join, remove_join: the other table.”
operations[].pairsarray of objectsNo“add_join, remove_join: the columns to join on.”
operations[].pairs[].fromstring (max 300 chars)Yes“A column of table.”
operations[].pairs[].tostring (max 300 chars)Yes“A column of to.”
operations[].metricstring (max 300 chars)No“A metric, by its name or key.”
operations[].namestringNo“create_metric, update_metric: the metric’s name.”
operations[].calculationstring or nullNo“The SQL expression the metric computes, naming the tables it reads.”
operations[].instructionsstring or nullNo“How an AI client should use the metric.”
operations[].changestring (max 300 chars)No“A change id from get_versions.”
operations[].objectstring (max 300 chars)No“remove_change: a table or metric whose draft changes all go.”
operations[].versioninteger (greater than 0)No“revert_change: the version that made the change.”
operations[].confirmedbooleanNo“remove_join, remove_metric, revert_change: true to remove one the live version has.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

operations, as sent:

“set_description { table, column?, description }, set_key { table, columns }, set_time_column { table, column }, set_visible { table, visible }, add_join { table, to, pairs }, remove_join { table, to, pairs?, confirmed? }, create_metric { name, description?, calculation?, instructions? }, update_metric { metric, name?, description?, calculation?, instructions? }, remove_metric { metric, confirmed? }, remove_change { change } or { object }, revert_change { change, version, confirmed? }.”

publish_draft

publish_draft at a glance
FactValue
SettingAllow AI clients to publish context changes, off by default
Who gets itEditor or higher
Annotationstitle: Publish the context draft, readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false

Description as sent:

“Publishes this data source’s context draft as a new version, so every AI client that reads the data source sees it. Pass the draftRevision from get_versions or edit_draft; a draft that changed since is refused with what changed. Works only once an admin allows publishing from AI clients in Settings › AI tools. Read explore_docs context/runbook before publishing.”

publish_draft parameters
ParameterTypeNeededDescription
databasestringNo“Data source key, from explore_schema”
draftRevisionstring (max 100 chars)Yes“The revision get_versions or edit_draft gave for the draft you reviewed.”
notestring (max 500 chars)No“A note the version keeps, shown in the console.”
intentstring (max 300 chars)No“Why this call is being made, in 15-25 words, third person. Analytics only; it never changes the result. No names, emails, IDs or row values — describe the purpose in general terms.”

SQL dialect by engine

The descriptions of sql_query, render_chart, and save_dashboard end with one dialect line, set by the data source’s engine. With more than one data source, they end with one line for each, after For <key>:. With no data source, they have no dialect line.

Dialect line for each engine
EngineDialect line
PostgreSQL“Dialect: PostgreSQL. Identifier quote: “double quotes”. Dates: date_trunc(‘month’, col), now() - interval ‘7 days’. Cast: col::type. Nulls: coalesce(a, b).”
Amazon Redshift“Dialect: Amazon Redshift (PostgreSQL-like). Identifier quote: “double quotes”. Dates: date_trunc(‘month’, col), dateadd(day, -7, getdate()). Cast: col::type. Nulls: nvl(a, b).”
BigQuery“Dialect: BigQuery GoogleSQL. Identifier quote: backticks. Dates: date_trunc(col, month), date_sub(current_date(), interval 7 day). Cast: cast(x as type) or safe_cast. Nulls: ifnull(a, b).”
ClickHouse“Dialect: ClickHouse. Identifier quote: backticks. Dates: toStartOfMonth(col), now() - INTERVAL 7 DAY. Cast: cast(x as type). Nulls: coalesce(a, b) or ifNull(a, b).”
DuckDB / MotherDuck“Dialect: DuckDB. Identifier quote: “double quotes”. Dates: date_trunc(‘month’, col), now() - INTERVAL 7 DAY. Cast: col::type. Nulls: coalesce(a, b).”
MySQL / MariaDB“Dialect: MySQL/MariaDB. Identifier quote: backticks. Dates: date_format(col, ‘%Y-%m-01’), now() - interval 7 day. Cast: cast(x as signed) (there is no as int). Nulls: ifnull(a, b). Concat: concat(a, b); || is OR here, not string concatenation.”
Microsoft SQL Server“Dialect: Microsoft SQL Server (T-SQL). Identifier quote: [brackets]. Row limiting: select top (n) … or order by … offset 0 rows fetch next n rows only; there is no LIMIT. Dates: datetrunc(month, col), dateadd(day, -7, getdate()). Cast: cast(x as type) or try_cast. Nulls: isnull(a, b) or coalesce(a, b). Concat: a + b, not ||. Unicode strings: N’text’.”

This table does not include PostHog.