MCP tool reference
Every tool sanda's MCP server offers, with the scope it needs, its parameters and what it does. Generated from the tool definitions the server runs.
This is every tool sanda's MCP server can offer an assistant. The names, titles, scopes and parameters below are read from the same tool definitions the server runs, so they cannot drift from what a client sees.
How a credential is offered tools
tools/list returns the tools this credential may use, not every tool. A tool is left off the list, and refused if called anyway, when any of these is true:
- The credential does not hold the scope. Each tool needs one scope. A wider scope includes a narrower one, so a credential holding
sql:writealso gets everythingsql:runandquery:rungive. See scopes. - The edition does not include it. The sanda search tools (
search_documentsand every tool needingsearch:manage) are on Standard and Enterprise. - The person behind it is no longer allowed. A privileged scope is honoured only while the person who granted it is an owner or admin.
- The workspace is closed. Then only the tools that read remain: asking questions, looking up the docs and seeing connections.
action_status is offered only to a credential that holds a scope one of the held tools needs. See permissions and approvals.
A token created with no extra switches is offered search_fluid, describe_table, run_query, search_documents and search_docs.
Reading an entry
Each entry starts with the tool's name (the string a client sends), then a row of markers: the tool's title, what kind of tool it is, the scope it needs and, where it applies, the editions that include it. The kind comes from the tool's annotations, which a client can use to skip a confirmation prompt or add one:
- Read only means the tool changes nothing, so calling it again is safe.
- Writes means the tool changes something.
- Destructive means the tool can remove or overwrite something, such as taking tables off the map, dropping a view or committing SQL.
The parameter table lists every top-level parameter with its type, whether it is required, and what it means. Where a parameter is an object or a list, its fields are described in the parameter's own text or in the entry's notes.
Limits every tool shares
| Limit | Value |
|---|---|
Rows or hits returned by run_query, run_sql and search_documents |
At most 50 |
Rows returned by execute_sql |
50 by default, at most 500 |
Rows loaded or added in one load_rows or append_rows call |
10,000 |
| Request body | 1,000,000 bytes |
| Time a tool holds a call open when it builds or runs something | 55 seconds. A longer job belongs on a schedule |
Time a run_query or run_sql statement may run |
20 seconds |
| Held actions a workspace may have waiting | 50 |
A tool that cannot answer returns a sentence and isError: true rather than a failure, for example that two tables would fan out or that a column does not exist. An assistant is expected to read it and try again differently.
Resources
sanda also serves two resources for clients that read them. Both need fluid:read, like search_fluid. A credential without it is listed no resources, and reading one is refused the way a tool is, with a step-up challenge for a connected app:
| Resource | What it holds |
|---|---|
becca://fluid/brief |
The one-page map of the workspace: its tables and the names of every metric, dimension, filter and category. Read this first |
becca://fluid/context |
Everything in full: every column, expression, sample value and join. Large, so prefer the brief plus tool calls |
cherry and MCP
cherry, the console's own agent, uses most of these tools too, under the signed-in person's role and the capabilities switched on under Settings · cherry. It is not offered execute_sql, set_intake, append_rows or the tools that manage sanda search, and action_status is for MCP credentials alone. cherry also has tools of its own for building reports, which are never served over MCP. See cherry.
Read the fluid and your data
Find what the workspace has modelled, look closely at a table, ask a question and search the text inside rows.
search_fluid
Search the fluidRead onlyfluid:read
Finds what the workspace has modelled: metrics, dimensions, named filters, tables and columns. An assistant calls it before using any name it has not already seen in the workspace brief or an earlier answer, because a metric found here is the definition the business agreed on.
| Parameter | Type | Required | Description |
|---|---|---|---|
query |
string | No | A word or fragment to match against names, synonyms, descriptions, definitions and column names. Leave it empty to list everything. |
kind |
string, one of all, metrics, dimensions, filters, tables, columns |
No | Narrows the search to one kind of thing: all (the default), metrics, dimensions, filters, tables or columns. |
The answer is text, one line per match. A search that finds nothing says so, which is an answer: try a synonym before concluding the question cannot be asked.
describe_table
Describe a tableRead onlyfluid:read
Describes one table in full: every column with its type, the grain, the primary key, the time column, the metrics and dimensions measured on it and every join it can follow. An assistant calls it before it names a column.
| Parameter | Type | Required | Description |
|---|---|---|---|
table |
string | Yes | The table's business name, its schema-qualified relation or its identifier, for example order details, mapping.order_details or order_details. Case and separators do not matter. |
A table that is not on the map is not found, and the answer names the closest tables that are.
run_query
Run a composed queryRead onlyquery:run
Answers a question with rows. The assistant names what it wants by reference (metrics, dimensions, filters, conditions) and sanda composes the SQL, resolves the joins and runs it, so a number means what the workspace says it means. This is the tool for almost every question.
| Parameter | Type | Required | Description |
|---|---|---|---|
metrics |
array of strings | No | Metric names from search_fluid. These are the numbers the business has defined. |
dimensions |
array of strings | No | Dimension names to break the numbers down by. |
granularity |
string, one of day, week, month, quarter, year |
No | How coarsely to group a time dimension: day, week, month, quarter or year. Empty means one row per underlying value. |
columns |
array of objects | No | Raw columns for questions no metric covers, each as a fully-qualified schema.table.column and an aggregate (sum, avg, min, max, count, count_distinct, or empty to list the value). A well-behaved assistant says when it has leaned on one, because the answer rests on a column name rather than an agreed definition. |
filters |
array of strings | No | Named filter names from search_fluid: conditions the workspace has already agreed on. |
conditions |
array of objects | No | Extra narrowing for this question only. Each condition has a field (a dimension name or a fully-qualified column), an op, a list of values and an optional named period in relative. Operators are the same ones the console's filters use. |
order_by |
array of objects | No | How to sort, first entry first, as by (a metric, dimension or column already in the query) and direction (asc or desc). Left out, a number broken down by a label comes back largest first and a time series in time order. |
limit |
integer | No | How many rows to return. The default and the maximum are both 50. |
Operators and named periods are listed in filters. The schema marks every key of a condition as required, so send all four: use an empty list for values and an empty string for relative when they do not apply. relative is only for in_range and names a period such as last_month; sanda resolves it in UTC and states the window it used.
{
"metrics": ["net revenue"],
"dimensions": ["region", "issue date"],
"granularity": "month",
"conditions": [
{ "field": "issue date", "op": "in_range", "values": [], "relative": "last_6_months" }
],
"limit": 10
}The answer is text: a table of rows, the window any named period resolved to, and the SQL sanda composed. Metrics measured on different tables can be asked for together, and each is counted on its own table, so revenue beside order count counts every order once. Each metric keeps its own condition. Two tables that would fan out if joined come back as a sentence explaining the refusal, not as a wrong number. A column whose name looks like it holds a secret is never returned.
search_documents
Search the text inside the rowsRead onlyquery:runStandard · Enterprise
Finds rows by what their text says, for questions about wording rather than about a number, such as which invoices mention water damage. It searches meaning and words together and answers with rows and never a total.
| Parameter | Type | Required | Description |
|---|---|---|---|
query |
string | Yes | What to look for, in words. A phrase or a sentence works better than a keyword, because meaning is half of how this searches. |
service |
string | No | Which search service to search, by name. Leave it out when the workspace has only one. |
k |
integer | No | How many rows to return. The default is 10 and the maximum is 50. |
filters |
array of objects | No | Narrows the search by a service's attribute columns, each as column, op (eq, neq, in, contains, gt, gte, lt, lte) and value. Only columns the service declares as attributes can be filtered. |
Every hit carries the value of its table's key column. To turn hits into a number, take those keys and call run_query with a condition on them.
This is a question of the data, so it needs only query:run, and it is offered to any client holding that scope on an edition with sanda search (Standard and Enterprise). If the workspace has no search service yet, a call answers that there is nothing to search. Managing services is a separate, privileged scope (search:manage).
run_sql
Run SQL you wroteRead onlysql:run
Runs one read-only SELECT the assistant wrote itself. It is the fallback for a question run_query cannot express, such as a window function or a cohort. It bypasses the workspace's own definitions, so the number is the assistant's rather than the business's.
| Parameter | Type | Required | Description |
|---|---|---|---|
sql |
string | Yes | One PostgreSQL SELECT (a leading WITH is fine). No semicolon, no comments, no DDL or DML. Every relation must be one describe_table listed. |
why |
string | Yes | One sentence naming what run_query could not express. It is required, and a client is expected to show it to the person beside the answer. |
Runs through the reader role inside a read-only transaction, returns at most 50 rows and stops after 20 seconds. A column whose name looks like a credential or an identity number is refused, whatever the statement calls it.
A default integration token does not see this tool at all: it is listed only for credentials granted sql:run. cherry is offered it only when its sql capability is switched on under Settings · cherry.
search_docs
Search the sanda docsRead onlyfluid:read
Searches sanda's own documentation at docs.sanda-os.com.au for how the product works and how to do something in it. An assistant calls it for a question about sanda, never for a question about your data, and gives you the link it used.
| Parameter | Type | Required | Description |
|---|---|---|---|
query |
string | Yes | What to look for, in a few words, for example connect xero, incremental sync or invite a member. |
limit |
integer | No | How many sections to return, from 1 to 5. The default is 3. |
The answer is text: for each match, the page and section title, the address of the section, and the section's own words. A search that finds nothing says so and points at the docs site.
This reads public pages and nothing of the workspace's data, so it needs only fluid:read, and a call is never held for approval. It makes no model call, so it costs nothing on the AI limit. cherry uses it too, to answer how-to questions with a link.
See and refresh the workspace
Check which connections and warehouses exist, how fresh and how full they are, and start a sync.
list_connections
List connectionsRead onlyworkspace:read
Lists every data connection in the workspace: the system it pulls from, its health, when it last synced, how many rows it has landed, its schedule and where it lands. An assistant uses it to answer whether the data is fresh.
Takes no parameters.
Never returns a credential.
connection_status
One connection, in detailRead onlyworkspace:read
Shows one connection's last ten runs with status, rows, duration and any error text, and the dataset its rows became. While a sync is running it also reports the stage, rows read so far, the average rate and each stream's state.
| Parameter | Type | Required | Description |
|---|---|---|---|
connection |
string | Yes | The connection's name or id, from list_connections. |
list_warehouses
List warehousesRead onlyworkspace:read
Lists each warehouse with its storage against its limit, compute, table and row counts, and whether ingest is paused. An assistant uses it before a large load to check how much room is left.
Takes no parameters.
Never returns a connection string.
sync_now
Start a syncWritesworkspace:manage
Starts one connection's sync now instead of waiting for its schedule. The rows land in the background, and connection_status shows how far the run has got.
| Parameter | Type | Required | Description |
|---|---|---|---|
connection |
string | Yes | The connection's name or id, from list_connections. |
A sync reads from the source system and lands rows in the warehouse, so it costs compute like any other sync. This is the only tool that drives a connection.
Write the semantic fluid
Put landed tables on the map, define metrics, dimensions, filters and relationships, and review what sanda proposed. Definitions go live at once.
list_warehouse_tables
List tables in the warehouseRead onlyfluid:write
Lists every table that has landed in the warehouse, with its columns, a row estimate and whether it is already on the semantic map. This is the raw layer describe_table cannot see, and the first step before import_tables.
| Parameter | Type | Required | Description |
|---|---|---|---|
only |
string | No | One schema-qualified relation to describe on its own, for example raw.xero__invoices. Leave it empty to list them all. |
import_tables
Put warehouse tables on the mapWritesfluid:write
Puts landed tables on the semantic map so the fluid can query them. sanda reads each table's shape, publishes a view over the columns chosen and files it under its connector's dataset, then reports the kind, key and time column it guessed and the joins it drew.
| Parameter | Type | Required | Description |
|---|---|---|---|
relations |
array of strings | Yes | Schema-qualified relations to add, for example raw.xero__invoices, from list_warehouse_tables. Up to 60 at a time. |
columns |
object | No | Optional. For each relation, the column names to keep. The key and time columns are always kept. Leave a relation out to take every column. |
kinds |
object | No | Optional. For each relation, fact or dimension, when the caller already knows. A fact records things that happened (orders, invoices) and a dimension records things that are (products, customers). This overrides sanda's guess. |
relationships |
string, one of infer, none |
No | infer (the default) draws joins that the column names assert, such as a column named exactly like another table's key. none draws none. Ambiguous matches are reported and never drawn. |
Importing a table that is already on the map refreshes it and does not duplicate it. Nothing here changes the rows in the warehouse.
set_table_kind
Mark a table as a fact or a dimensionWritesfluid:write
Says whether a table on the map is a fact or a dimension. Getting this right is what stops a query summing a price list. An assistant calls it after import_tables when sanda's guess was wrong.
| Parameter | Type | Required | Description |
|---|---|---|---|
table |
string | Yes | The table as the fluid names it, its relation or its identifier. Case and separators do not matter. |
kind |
string, one of fact, dimension |
Yes | fact or dimension. |
retire_tables
Take tables off the mapDestructivefluid:write
Takes tables off the semantic map. The rows in the warehouse are untouched, but every metric, dimension, filter and relationship written against those tables goes with them.
| Parameter | Type | Required | Description |
|---|---|---|---|
tables |
array of strings | Yes | Business names, relations or identifiers, as import_tables named them. |
Destructive to the model, not to the data. A well-behaved assistant reads the table names back to the person before calling it. cherry always asks first, whatever its mode.
define
Define something in the fluidWritesfluid:write
Creates or replaces one definition in the fluid, live at once: a metric, dimension, filter, relationship, alias, category or dataset, or a statement of what a table already on the map means.
| Parameter | Type | Required | Description |
|---|---|---|---|
kind |
string, one of metric, dimension, filter, relationship, alias, category, dataset, table |
Yes | What to define: metric, dimension, filter, relationship, alias, category, dataset or table. |
definition |
object | Yes | The fields for that kind, as an object. Each kind's fields are listed in the notes below. |
A definition with the same name replaces the old one, and names are matched without regard to case. The reply names the table the definition landed on and says if an expression uses a column that table does not have.
| Kind | Fields |
|---|---|
metric |
name, table, expression (one SQL fragment over that table's own columns, for example sum(total_ex_gst)), and optionally aggregation (sum, avg, count, count_distinct, min, max, ratio, custom), filters, unit, format (currency, percent, integer, decimal, duration), description, synonyms |
dimension |
name, column (as schema.table.column), and optionally kind (categorical, time, geo, boolean, numeric), description, synonyms |
filter |
name, table, expression (a named WHERE condition), optionally description |
relationship |
left_column, right_column, key (the business name of the shared identity), optionally left_cardinality, right_cardinality (one or many), description |
alias |
column, term, optionally description |
category |
name, optionally description |
dataset |
name, optionally description |
table |
table, and any of description, grain, primary_key, time_column, kind, name. Only the fields given change |
{
"kind": "metric",
"definition": {
"name": "net revenue",
"table": "invoices",
"expression": "sum(total_ex_gst)",
"format": "currency",
"synonyms": ["turnover"]
}
}list_proposals
List proposals awaiting reviewRead onlyfluid:write
Lists everything sanda's own learning pass has proposed and nobody has signed off: tables, metrics, dimensions, filters and relationships. Proposals are invisible to queries until they are accepted.
Takes no parameters.
review_proposal
Accept or reject a proposalWritesfluid:write
Accepts or rejects one proposal. An accept may carry corrections, so a nearly right proposal is one call. A relationship is decided on both sides together.
| Parameter | Type | Required | Description |
|---|---|---|---|
kind |
string, one of metric, dimension, filter, mapping, table, category, dataset, glue |
Yes | What is being reviewed: metric, dimension, filter, mapping, table, category, dataset or glue. |
id |
string | Yes | The proposal's id, from list_proposals. |
action |
string, one of accept, reject |
Yes | accept or reject. |
edits |
object | No | Optional corrections applied when accepting, in the same shape define takes for that kind. |
Accept a relationship's tables before the relationship.
delete_definition
Delete a definitionDestructivefluid:write
Removes one metric, dimension, filter, mapping, category, dataset or glue definition from the fluid for good. Redefining with define is better than deleting and recreating, because a delete loses the definition's history.
| Parameter | Type | Required | Description |
|---|---|---|---|
kind |
string, one of metric, dimension, filter, mapping, category, dataset, glue |
Yes | What to remove: metric, dimension, filter, mapping, category, dataset or glue. Use retire_tables for a table. |
id |
string | Yes | The definition's id. |
cherry always asks first, whatever its mode.
Load and change warehouse data
Land tables of an agent's own rows, add rows to tables opened for intake, and run one committed SQL statement of its own.
load_rows
Load rows into the warehouseWriteswarehouse:write
Lands a table of an agent's own rows in the warehouse as raw.csv__<table>, through the same loader and storage limit a sync uses. Typical uses are a forecast it computed or a lookup it was given. It cannot touch a table a sync landed.
| Parameter | Type | Required | Description |
|---|---|---|---|
table |
string | Yes | A short name. It becomes raw.csv__<name>, lower-cased with underscores. |
columns |
array of objects | Yes | The columns in order, each with a name and an optional type (text, bigint, integer, numeric, boolean, date, timestamptz or jsonb). Leave the type empty to infer it from the values. Up to 250 columns. |
rows |
array of anys | Yes | The rows, each either a list of values in column order or an object keyed by column name. Empty cells are null. Up to 10,000 rows per call. |
mode |
string, one of replace, append |
No | replace (the default) swaps the table for the new rows, in their new shape, and keeps the views built on it. It is refused, naming the view, when one of your own views reads a column the new rows drop or retype. append adds to it and must match its columns. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
import |
boolean | No | When true, also puts the table on the semantic map so run_query can reach it. |
intake |
boolean | No | When true, also opens the table for intake, so credentials holding warehouse:append may add rows to it. |
Send rows: [] with declared types to create an empty table. For more than 10,000 rows, append in batches.
{
"table": "sales forecast",
"columns": [{ "name": "month", "type": "date" }, { "name": "forecast", "type": "numeric" }],
"rows": [["2026-10-01", 125000], ["2026-11-01", 131500]],
"import": true
}set_intake
Open or close a table for intakeWriteswarehouse:write
Opens a hand-loaded table for intake, or closes it. While a table is open, credentials holding warehouse:append may add rows to it and do nothing else. Closing leaves the rows and the map as they are.
| Parameter | Type | Required | Description |
|---|---|---|---|
table |
string | Yes | The table as load_rows named it. dad habits, csv__dad_habits and raw.csv__dad_habits are one table. |
open |
boolean | Yes | true opens the table, false closes it. |
note |
string | No | Optional. What the table is for, shown beside it in the Intake tables panel of Settings · Integrations. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
The shape for a log a person keeps by talking to an assistant: land the table once with load_rows, open it here, then give the person a credential holding only warehouse:append. Not offered to cherry.
append_rows
Add rows to an intake tableWriteswarehouse:append
Adds rows to a table an owner or admin has opened for intake. It only ever appends: it cannot create, replace or delete a table, and a table that is not open refuses.
| Parameter | Type | Required | Description |
|---|---|---|---|
table |
string | Yes | The table, as it was opened for intake. |
columns |
array of strings | No | Only when rows are lists: the column names in the order the values come. |
rows |
array of anys | Yes | The rows, each an object keyed by column name or a list in columns order. Send an empty list to be told the table's columns and types. Up to 10,000 rows per call. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
Send dates as YYYY-MM-DD, numbers as numbers and booleans as true or false, and leave a field null when the value is not known: the warehouse refuses a value a column cannot hold. If the same day is told twice, append a second row and let the workspace's model decide which counts.
This is the one write any member may grant, because its reach is only the tables an owner or admin has opened. Not offered to cherry.
{
"table": "daily log",
"rows": [{ "day": "2026-09-30", "steps": 8200, "note": "Long walk" }]
}execute_sql
Run and commit SQL you wroteDestructivesql:write
Runs one SQL statement the agent wrote on a warehouse, as the warehouse's builder role, and commits it. The builder reads every landing table and writes only in the derived schema. This is the SQL shell held by a machine, and there is no undo.
| Parameter | Type | Required | Description |
|---|---|---|---|
sql |
string | Yes | Exactly one PostgreSQL statement. Semicolons inside a string are fine and a second statement is refused. Qualify every relation with its schema (raw., derived. or mapping.). |
why |
string | Yes | One sentence on what the statement is for. It is required and is kept in the audit trail beside the statement. |
mode |
string, one of write, read |
No | write (the default) runs and commits the statement. read runs it inside a read-only transaction the database enforces, for looking before deciding. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
rows |
integer | No | How many rows to return from a statement that returns rows. The default is 50 and the maximum is 500. |
A sync's landing tables, a published mapping view, the search indexes and sanda's own bookkeeping are out of reach whatever the statement says. A statement that changes rows answers with its command tag and the number of rows touched.
When the workspace asks for approval before an agent acts, a write-mode call is held for an owner or admin and does not run until one approves it. A read call is never held. See permissions and approvals. Not offered to cherry.
Views and procedures
Define, build and tune views, materialized views and procedures in the derived schema of a warehouse.
list_sources
List what a view may be built onRead onlywarehouse:model
Lists every relation in a warehouse that a view may be built on, with columns and types: landing tables, derived objects already defined, and the mapping tables. It is read as the role that would own the view, so it lists exactly what a CREATE will accept.
| Parameter | Type | Required | Description |
|---|---|---|---|
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
A published mapping.* view is not listed and cannot be built on. Build on the table behind it.
list_views
List derived views and proceduresRead onlywarehouse:model
Lists every view, materialized view and procedure the workspace has defined in a warehouse's derived schema: its definition, its version, when a materialized view was last built and whether it is on the semantic map.
| Parameter | Type | Required | Description |
|---|---|---|---|
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
create_view
Define a view or materialized viewWriteswarehouse:model
Defines a view in the derived schema from one SELECT over anything list_sources names. A materialized view stores its result and is built now, on the workspace's compute. The view goes on the semantic map in the same call unless told not to.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | A lowercase identifier. It becomes derived.<name>. |
sql |
string | Yes | One SELECT (or WITH ... SELECT) over the relations list_sources names, by schema-qualified name. One statement, no semicolons, no comments. |
materialized |
boolean | No | When true, stores the rows and rebuilds them on refresh, instead of computing on every read. |
unique_key |
array of strings | No | Materialized views only: the columns that identify one row, so a refresh can run without blocking readers. |
description |
string | No | What the view is for, in a sentence. |
replace |
boolean | No | When true, redefines derived.<name> if it already exists. |
publish |
boolean | No | Whether to put the view on the semantic map. The default is true. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
A materialized view's build runs on the workspace's compute and is billed to it, which is why the scope that allows this is privileged. See usage.
refresh_view
Rebuild a materialized viewWriteswarehouse:model
Rebuilds one materialized view now and reports how long it ran and what it cost. An assistant calls it after the tables behind the view have synced.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | The materialized view's name, from list_views. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
Runs for up to 55 seconds inside the call. A build that needs longer, up to 14 minutes, belongs on a schedule.
drop_view
Drop a derived objectDestructivewarehouse:model
Drops one view, materialized view or procedure from the derived schema, taking it off the semantic map first. It is refused while another derived view reads it, because sanda never cascades. The definition stays in the history and the rows do not.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | The object's name, from list_views. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
When the workspace asks for approval before an agent acts, this call is held for an owner or admin. See permissions and approvals. cherry always asks first, whatever its mode.
create_procedure
Define a procedureWriteswarehouse:model
Defines a procedure in the derived schema: a multi-step transform in plpgsql or SQL that builds, merges or reshapes derived tables, with COMMIT allowed between steps. It runs as the warehouse's builder role.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | A lowercase identifier. It becomes derived.<name>. |
body |
string | Yes | The procedure body: the plpgsql block, or the SQL statements. |
args |
string | No | The arguments as name type, name type, for example since date, region text. Empty for none. Types are text, integer, bigint, numeric, boolean, date, timestamptz, timestamp, interval, jsonb and uuid. |
language |
string, one of plpgsql, sql |
No | plpgsql or sql. |
description |
string | No | What the procedure does, in a sentence. |
replace |
boolean | No | When true, redefines derived.<name> if it already exists. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
The builder role may read landing tables, derived objects and the mapping tables, and may create and write only in derived. It cannot write a row anywhere else, whatever the body says. Run it with call_procedure.
call_procedure
Run a procedureWriteswarehouse:model
Runs one procedure now, with its arguments in declared order, and reports how long it ran and what it cost.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | The procedure's name, from list_views. |
args |
array of anys | No | Argument values in declared order. Leave it out for a procedure with none. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
Runs for up to 55 seconds inside the call. A run that needs longer belongs on a schedule.
warehouse_advice
What is slowing this warehouse downRead onlywarehouse:model
Reads a warehouse's own statistics and says what is slowing it down or costing it storage: a missing index, a table autovacuum has fallen behind on, stale statistics after a sync, an unused index, storage a rewrite would give back, or a table large enough to partition. It writes nothing.
| Parameter | Type | Required | Description |
|---|---|---|---|
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
Each finding carries an id, the figures behind it and the statement sanda would run. A finding with no statement is one sanda will not act on, and it says why. Pass the ids you want acted on to optimise_warehouse.
optimise_warehouse
Apply what the advice foundWriteswarehouse:model
Runs the findings you name by id from warehouse_advice: a vacuum, an analyse, an index created or dropped. sanda re-reads the warehouse first and runs only findings that are still current. It never runs SQL you send it.
| Parameter | Type | Required | Description |
|---|---|---|---|
ids |
array of strings | Yes | Finding ids from warehouse_advice, for example vacuum:raw.xero__invoices. Up to eight at a time. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
Each statement runs on the workspace's compute, is recorded on the run ledger and is billed as the seconds it takes. A finding that would lock a table against readers says so in the advice, so read it before sending its id.
Schedules
Run views and procedures on a clock or after a sync, and read what each run did and cost.
list_schedules
List schedulesRead onlywarehouse:model
Lists every task graph in a warehouse: its steps, its cadence, whether it is paused or has suspended itself after failures, when it next runs and how its last run went.
| Parameter | Type | Required | Description |
|---|---|---|---|
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
create_schedule
Schedule a task graphWriteswarehouse:model
Defines a task graph: an ordered list of steps on the warehouse, each a materialized view to refresh or a procedure to call, and when it runs. Steps run in order, and a step is skipped when the one before it failed.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | A name for the graph, for example nightly rebuild. |
tasks |
array of objects | Yes | The steps in order, from one to 20. Each is { "refresh": "view_name" } or { "call": "procedure_name", "args": [ ... ] }, with an optional step name. |
cron |
string | No | A five-field cron expression evaluated in UTC. sanda's scheduler runs on a 15 minute grid, so the minute field must be 0, 15, 30 or 45. Leave it out for a graph that runs only after a sync or by hand. |
timezone |
string | No | The zone the cron was written for, kept for display. Evaluation is always UTC. |
after_sync |
string | No | A connection, by name or id. The graph runs each time that connection lands rows, which is usually the better trigger. It may be combined with cron. |
suspend_after_failures |
integer | No | Pause the graph after this many failures in a row, from 1 to 100. The default is 3 and 0 never pauses. |
description |
string | No | What the graph is for, in a sentence. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
One run has at most 14 minutes, and every run is billed as the seconds its statements executed.
{
"name": "nightly rebuild",
"tasks": [{ "refresh": "revenue_by_month" }, { "call": "close_month", "args": ["2026-09-01"] }],
"cron": "0 18 * * 1-5"
}That cron is weekdays at 18:00 UTC.
run_now
Run a task graph nowWriteswarehouse:model
Queues one task graph to run now rather than waiting for its schedule. Because the scheduler runs on a 15 minute grid, it starts at the next quarter hour. A graph already queued or running is not queued again.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | The graph's name, from list_schedules. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
pause_schedule
Pause or resume a task graphWriteswarehouse:model
Pauses a task graph so nothing fires it (not its schedule, not a sync landing, not run_now), or resumes one that is paused or has suspended itself. Resuming resets its failure count.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | The graph's name, from list_schedules. |
resume |
boolean | No | true to resume. Leave it out or set it to false to pause. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
run_history
Run historyRead onlywarehouse:model
Shows what ran and what it cost: the recent runs of one task graph with each step's outcome and seconds, or the versions, builds and calls of one derived view or procedure.
| Parameter | Type | Required | Description |
|---|---|---|---|
schedule |
string | No | A task graph, by name, to see its runs. |
view |
string | No | A derived object, by name, to see its versions and runs. Give one of schedule or view. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
Manage sanda search
Create, change, build and remove search services over tables on the map, and estimate what indexing costs first.
list_search_services
List search servicesRead onlysearch:manageStandard · Enterprise
Lists every sanda search service in a warehouse: the table and columns it indexes, how many rows and how large the index is, when it last built and what that cost. It also says whether sanda search is switched on for the workspace at all.
| Parameter | Type | Required | Description |
|---|---|---|---|
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
The search tools are available on Standard and Enterprise. Not offered to cherry.
describe_search_service
Describe a search serviceRead onlysearch:manageStandard · Enterprise
Describes one search service in full: its definition and state, every build with the rows it embedded and what it cost, and every version its definition has had. An assistant uses it to find out why a service is broken.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | The service, by name. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
estimate_search_build
Estimate what indexing would costRead onlysearch:manageStandard · Enterprise
Estimates what indexing a table would cost before anything is created: rows, tokens, dollars and how that compares with the workspace's daily AI limit.
| Parameter | Type | Required | Description |
|---|---|---|---|
source |
string | Yes | The table on the map, as mapping.<table>. |
text_columns |
array of strings | Yes | The columns whose text would be indexed. |
key_column |
string | No | The column that identifies a row. |
model |
string | No | The embedding model to use. Leave it out for sanda's default. |
chunk_chars |
integer | No | How much text one indexed piece holds. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
create_search_service refuses without confirm: true when a build would use more than half a day's AI limit. This is how to know that in advance.
create_search_service
Create a search serviceWritessearch:manageStandard · Enterprise
Indexes the text of a table so it can be searched by meaning as well as by words. The first build sends the text of the columns named to an embedding model outside Australia and is charged against the workspace's daily AI limit.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | A lowercase name for the service. It becomes a table in the warehouse. |
source |
string | Yes | A table on the semantic map, as mapping.<table>. A view in the derived layer must be published to the map first. |
key_column |
string | No | The column that identifies a row. It must be unique and never empty, because a hit is only useful if it can be joined back to a metric. |
text_columns |
array of strings | Yes | The columns whose text is indexed. At least one. |
attribute_columns |
array of strings | No | Columns kept beside the text as filters, such as status, region or a date. They are not indexed as text. |
model |
string | No | The embedding model to use. Leave it out for sanda's default. |
chunk_chars |
integer | No | How much text one indexed piece holds. The default is 1200 characters. |
fts_config |
string, one of simple, english |
No | Word matching: simple, or english, which stems words and drops common stop words. |
after_sync |
string | No | A connection, by name or id. The service rebuilds each time it lands rows, which is usually the right trigger. |
cron |
string | No | A five-field UTC cron on the quarter hour (minute 0, 15, 30 or 45), for a service rebuilt on a clock instead. With neither trigger the service is rebuilt when asked. |
timezone |
string | No | The zone the cron was written for, kept for display. Evaluation is always UTC. |
description |
string | No | What the service is for, in a sentence. |
confirm |
boolean | No | Set to true to go ahead when the estimated cost is a large share of the day's AI limit. |
replace |
boolean | No | Set to true to replace a service of the same name, rebuilding it from scratch. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
sanda search must be switched on by a workspace owner in the console before this works. An agent holding the scope cannot switch it on, because it is a decision about where a business's data may be processed. A column whose name says it holds a secret is refused outright. Not offered to cherry.
update_search_service
Change a search serviceWritessearch:manageStandard · Enterprise
Redefines a search service or changes when it rebuilds. Changing the model, text columns, chunk size, word matching, key or source rebuilds the whole index at the cost of a first build. Changing only the attribute columns, description or cadence costs nothing.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | The service to change, by name. |
source |
string | No | A new source table on the map. |
key_column |
string | No | A new key column. |
text_columns |
array of strings | No | The new list of columns to index. |
attribute_columns |
array of strings | No | The new list of filter columns. |
model |
string | No | A different embedding model. |
chunk_chars |
integer | No | A new chunk size. |
fts_config |
string, one of simple, english |
No | simple or english. |
after_sync |
string | No | A connection, by name or id. Null clears it. |
cron |
string | No | A five-field UTC cron on the quarter hour. Null clears the schedule. |
timezone |
string | No | The zone the cron was written for, kept for display. |
description |
string | No | A new description. |
enabled |
boolean | No | false pauses rebuilding. Searches keep working on what is already indexed. |
suspend_after_failures |
integer | No | Pause rebuilding after this many failures in a row. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
A well-behaved assistant tells the person before it rebuilds a large table. Not offered to cherry.
build_search_service
Index a search service nowWritessearch:manageStandard · Enterprise
Queues an index build now instead of waiting for the service's own trigger. Only rows whose text changed since the last build are sent to the model, so it is cheap on an unchanged table.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | The service, by name. |
full |
boolean | No | When true, discards the index and rebuilds every row. It costs what the first build cost. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
This only queues the build. sanda's scheduler runs on a 15 minute grid, and indexing then runs in slices across as many ticks as it takes. Use run_search_build to spend the call indexing now. Not offered to cherry.
run_search_build
Index a search service now, inside this callWritessearch:manageStandard · Enterprise
Queues a build and then spends up to 55 seconds of the call indexing it, so a small index finishes inside the call. It reports what it read, embedded and cost.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | The service, by name. |
full |
boolean | No | When true, rebuilds every row and not only what changed. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
An index too large for one call keeps its place and says there is more to do: call again to carry it on, or leave it to the scheduler. This builds an index and does not query one. Use search_documents for that. Not offered to cherry.
drop_search_service
Remove a search serviceDestructivesearch:manageStandard · Enterprise
Removes a search service and deletes its index from the warehouse. Searches against it stop at once, and rebuilding it later costs a full first build. The definition and every build it ran stay on record.
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | The service to remove, by name. |
warehouse |
string | No | Which warehouse, by name or id, when the workspace has more than one. |
When the workspace asks for approval before an agent acts, this call is held for an owner or admin. See permissions and approvals. Not offered to cherry.
Email reports
Schedule, change, pause, send and remove report deliveries.
list_deliveries
List report deliveriesRead onlyreports:deliver
Lists every report delivery: the report it sends, its cadence, who it goes to, whether it is paused, when it next goes and how its last run went, with each delivery's id. It also names the reports that have no delivery.
| Parameter | Type | Required | Description |
|---|---|---|---|
report |
string | No | Only this report's deliveries, by name or id. |
Reading deliveries needs the same scope as creating them, because the list is who receives what.
create_delivery
Schedule a report deliveryWritesreports:deliver
Emails a report on a schedule. At each firing sanda asks every block on the report again and sends the answers to the recipients. Email only: Slack and webhook deliveries are set up on the Report schedules page, because their address is a secret.
| Parameter | Type | Required | Description |
|---|---|---|---|
report |
string | Yes | The report to send, by name or id. |
recipients |
array of strings | Yes | Email addresses, up to 25. |
cron |
string | Yes | A five-field cron on the quarter hour (minute 0, 15, 30 or 45), for example 0 8 * * 1 for Mondays at 8. |
timezone |
string | No | An IANA zone the cron is local to, for example Australia/Sydney. It stays on local time across daylight saving. Leave it out and the cron is UTC. |
note |
string | No | A closing note for the email, up to 500 characters. |
A workspace may limit which email domains a delivery can go to, and the answer says so when a recipient is outside them. To send a report once, create the delivery, send it with send_delivery, then pause it with pause_delivery.
When the workspace asks for approval before an agent acts, a call naming a recipient outside the domains its members use is held for an owner or admin, because a report emailed to someone cannot be recalled. See permissions and approvals. cherry always asks before an email, whatever its mode.
{
"report": "Board pack",
"recipients": ["cfo@example.com.au"],
"cron": "0 8 * * 1",
"timezone": "Australia/Sydney"
}update_delivery
Change a report deliveryWritesreports:deliver
Changes who a delivery goes to, its closing note or when it goes. recipients is the whole new list, so to add someone pass the current recipients with them, and to remove someone leave them out.
| Parameter | Type | Required | Description |
|---|---|---|---|
report |
string | No | The report the delivery sends, by name or id. |
delivery |
string | No | The delivery's id, from list_deliveries, when the report has more than one. |
recipients |
array of strings | No | The whole new list of email addresses. |
cron |
string | No | A new five-field cron on the quarter hour. It stays on the delivery's own clock unless timezone names another. |
timezone |
string | No | The IANA zone a new cron is local to. Leave it out to keep the delivery's own. |
note |
string | No | A new closing note. An empty string removes it. |
Held for approval on the same terms as create_delivery when a recipient is outside the domains the workspace's members use.
send_delivery
Send a report delivery nowWritesreports:deliver
Sends a delivery now, to its recipients, instead of waiting for its schedule. It goes out within a minute or two.
| Parameter | Type | Required | Description |
|---|---|---|---|
report |
string | No | The report the delivery sends, by name or id. |
delivery |
string | No | The delivery's id, when the report has more than one. |
A paused delivery can still be sent by hand. cherry always asks first, whatever its mode.
pause_delivery
Pause or resume a report deliveryWritesreports:deliver
Pauses a delivery so its schedule stops sending it, or resumes a paused one. A resumed delivery next goes at its next firing from now.
| Parameter | Type | Required | Description |
|---|---|---|---|
report |
string | No | The report the delivery sends, by name or id. |
delivery |
string | No | The delivery's id, when the report has more than one. |
resume |
boolean | No | true to resume. Leave it out or set it to false to pause. |
delete_delivery
Remove a report deliveryDestructivereports:deliver
Removes a delivery. The report stays, and the record of every run it made stays on the timeline. Only the schedule and its recipients go.
| Parameter | Type | Required | Description |
|---|---|---|---|
report |
string | No | The report the delivery sends, by name or id. |
delivery |
string | No | The delivery's id, when the report has more than one. |
pause_delivery is the way to stop one for a while. cherry always asks first, whatever its mode.
Approvals
Find out what happened to an action the workspace held for a person to approve.
action_status
Check an action waiting for approvalRead onlyany held scope
Reports what happened to an action the workspace held for approval: still waiting, declined, expired, or approved and what the tool answered when it ran. With no id it lists this credential's ten most recent actions.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | No | The id the held call answered with. Leave it out to list recent actions. |
A credential sees only its own actions. The tool is listed only for credentials that hold a scope a held tool needs (sql:write, warehouse:model, search:manage or reports:deliver), because a credential that can only ask questions never has anything waiting. It is read-only, and it is never held itself.
A well-behaved assistant never tells a person a held action is done until this says it ran. See permissions and approvals.
Something unclear or out of date? Tell us, and we will fix the page.