Tables
Structured data tables scoped to your project — create, query, and manage tabular data that playbooks and agents can read and write.
Tables are structured tabular databases scoped to your project. Use them to store, query, and manage structured data that your playbooks and AI agents can read and write programmatically.
Creating a table
Open the Creation Picker (Cmd+N) and select Table, or use the create_data_store MCP tool from an agent session.
Each table has typed columns. Data is persisted in a per-project SQLite database.
Column types
| Type | Description |
|---|---|
text | Plain text |
number | Numeric values |
date | Date/time values |
bool | True/false |
relation | Foreign key to another table |
Columns also support format sub-types for richer display: email, currency, percent, rating, singleSelect, multiSelect, and rank. Select formats carry options with id, label, and color.
Natural keys
An optional natural key column marks the value that uniquely identifies a row (a date, an email, an external ID). It's what makes repeated writes idempotent instead of duplicating rows.
What a key collision does depends on which path is writing:
- Playbook
writeToStoresteps upsert. A row whose key already exists is updated in place. This is what makes a periodic playbook safe to re-run. - The
insert_data_store_rowsMCP tool refuses by default. A row whose key already exists fails and the existing row is left untouched, because an agent inserting blind shouldn't be able to overwrite data it never read. Passmode: "upsert"to insert-or-refresh, ormode: "update"to refresh only.
Views
Tables support alternate view modes:
- Table — the default spreadsheet-style view.
- Kanban — cards grouped by a
singleSelectcolumn. - Gantt — timeline view using start and end date columns.
- List — rows as filterable cards behind a persistent filter rail (see List view below).
Switch or add a view from the view switcher at the top of the table. Each alternate type shows a + ‹type› button until it's been created. List is always available; Kanban needs a singleSelect column and Gantt needs a date column before its button enables. View tabs are named by type — there's no per-view rename. You can also create and manage views with the create_data_store_view / update_data_store_view MCP tools.
List view
The List view renders each row as a card behind a persistent left filter rail — a fast way to browse and narrow a table without scanning a grid. It's built to stay responsive on very large tables (it virtualizes the cards, so it handles stores with hundreds of thousands of rows).
Adding a List view
Click + List in the view switcher. Because a list needs no particular column type, the button is always enabled. A setup sheet opens where you configure the view; you can reopen it later from the Configure List menu (the sliders icon in the header). The setup sheet has four controls:
| Control | What it does |
|---|---|
| Layout | Grid (multi-column cards, the default) or Feed (a single centered column). |
| Card title | The column shown as each card's heading. Defaults to the natural-key column, else the first plain-text column, else the first column. |
| Card fields | Which columns appear on the card face. Defaults to all other visible columns (the title is shown separately). |
| Sort by | The column to sort cards by, with an ascending/descending toggle. Defaults to Created date, ascending. |
You can also flip Grid ↔ Feed and change the sort directly from the toolbar above the cards.
The filter rail
The rail on the left always shows one widget per filterable column, picked by the column's type:
| Column type | Filter widget |
|---|---|
singleSelect, multiSelect, relation | Checklist with live counts and an (empty) option. A search box appears above the list when a column has many values. |
text, email, url, phone | Search box (case-insensitive substring). |
number, currency, percent | Min – Max range. |
date | Today / Yesterday / Last 7d presets, plus from / to date pickers. |
bool | Any / Yes / No. |
Filters combine with AND. The checklist counts are computed against the other active filters — each option's number tells you how many rows you'd get if you also picked it. A dot marks any column that's currently filtering, and Clear all (N) at the top of the rail resets everything.
Working with cards
Each card shows its title, its field columns, and select values as pills. Click a card (or press Enter when it's focused) to open the row editor. Clicking a select pill on a card toggles that value as a filter, the same gesture as the Kanban board.
When nothing is showing, the view tells you why:
- No rows yet — use New to add one. — the table is empty.
- No cards match the current filters. — rows exist but none match; Clear filters and Clear search buttons appear as relevant.
Creating a List view from an agent
The create_data_store_view / update_data_store_view MCP tools accept view_type: "list". The optional list config mirrors the setup sheet: layout ("grid" | "feed"), cardTitleColumnID, cardFieldColumnIDs, sortColumnID, and sortDescending. All are optional — a bare list view renders every row as a card with sensible defaults.
Querying data
Use the query_data_store MCP tool to filter, aggregate, group, sort and page a table — addressing columns and select options by their display names, not internal ids. It returns JSON, and it's the way to get an exact count without pulling rows:
{ "store": "Internal Tickets",
"select": [{ "column": "Status" }, { "agg": "count", "as": "n" }],
"group_by": ["Status"] }run_data_query remains available for hand-written UUID-keyed query specs and cross-table joins.
Queries can also be embedded directly into notes as live chart/table blocks — the note auto-refreshes when the underlying data changes.
MCP tools
| Tool | Description |
|---|---|
create_data_store | Create a new table |
describe_data_store | Get a table's schema — columns, types, select options — with no rows |
get_data_store | Get a table's schema plus one page of rows (limit / offset / next_offset) |
list_data_stores | List all tables in the project |
add_data_store_column | Add a column |
update_data_store_column | Modify a column's type or options |
delete_data_store_column | Remove a column |
insert_data_store_rows | Insert rows — refuses a natural-key collision unless you pass mode: "upsert" |
update_data_store_row | Update a specific row |
update_data_store_rows | Bulk-update rows — a list of per-row changes, or where+set to change every matching row |
delete_data_store_row | Delete a row |
query_data_store | Filter, aggregate, group, sort and page by column display name |
run_data_query | Query with filters, sort, and projection (raw UUID-keyed spec; supports joins) |
create_data_store_view | Create a Kanban, Gantt, or List view |
Playbook integration
Two dedicated playbook step types work with tables:
- writeToStore — upserts rows with natural-key-aware writes. Validates column IDs against the live schema.
- readFromStore — queries with column filters and limits. Returns rows as step output for downstream steps to consume.