The modeling database for AI agents.
Tables, pivots and conditional formulas that recalculate instantly.
Read https://finicast.com/agent and build my model in FiniDBOr let it install the engine itself: npx finidb guide
Works with Claude Code, Codex, Cursor and any MCP client.
growth recomputes every forecast cell.Spreadsheet vs FiniDB
Spreadsheets were designed for eyes and mice. FiniDB was designed for agents: named tables, named line items, named periods, formulas placed by condition — no cell addresses, no copy-down, no broken ranges.
=SUMIFS($C:$C,$A:$A,$A2,$B:$B,B$1) copied across 720 cells.frame = hist: SUM(SELECT("amount","ledger","account","=",THIS("account"),"period","=",THIS("period")))Add rows to ledger, add accounts, add periods: nothing to recopy. The method applies to every cell whose frame is hist.
A worked example: an income statement
Two historical years and three forecast years. Eight line items, eight formulas. History and forecast carry different formulas for the same line, because the condition places them — that is the whole idea.
| Line item | Historical years frame = hist | Forecast years frame = fcst |
|---|---|---|
Revenue revenue | entered from the actuals | 'prior revenue' * (1 + 'growth') |
Revenue growth growth | IFERROR('revenue' / 'prior revenue' - 1, "") | entered: the driver you tune |
COGS cogs | entered from the actuals | 'revenue' * LOOKUP("value","drivers","id","cogs_pct") |
Gross profit gp | 'revenue' - 'cogs' | 'revenue' - 'cogs' |
Gross margin gm | 'gp' / 'revenue' | 'gp' / 'revenue' |
Opex opex | entered from the actuals | 'prior opex' * (1 + LOOKUP("value","drivers","id","opex_growth")) |
Operating income opinc | 'gp' - 'opex' | 'gp' - 'opex' |
Operating margin opm | 'opinc' / 'revenue' | 'opinc' / 'revenue' |
Read one row: revenue growth is derived in history and assumed in the forecast, and the same line carries both formulas because each one names the years it applies to. 'prior revenue' is the previous period of the revenue line; frame comes from a Frame column on the periods table, so adding a year changes nothing else.
| 2024hist | 2025hist | 2026fcst | 2027fcst | 2028fcst | |
|---|---|---|---|---|---|
| Revenue | 1,000 | 1,200 | 1,416 | 1,628 | 1,824 |
| Revenue growth | — | 20.0% | 18.0% | 15.0% | 12.0% |
| COGS | 420 | 480 | 538 | 619 | 693 |
| Gross profit | 580 | 720 | 878 | 1,010 | 1,131 |
| Gross margin | 58.0% | 60.0% | 62.0% | 62.0% | 62.0% |
| Opex | 380 | 430 | 473 | 520 | 572 |
| Operating income | 200 | 290 | 405 | 489 | 558 |
| Operating margin | 20.0% | 24.2% | 28.6% | 30.0% | 30.6% |
curl -O https://finicast.com/income-statement.json npx finidb apply income-statement.json --data ./model npx finidb query ./model is
One file: the periods with their Frame, the line items, the drivers, the entered actuals, and the eight methods above. Read it — it is the whole model, and it is what an agent writes.
Why an agent is better off in FiniDB
Eight claims, each demonstrable in the model above. Numbers are engine-time benchmark targets from the FiniDB benchmark suite.
- 01
No coordinates
A1:F1 becomes revenue for frame = fcst. The agent never reasons about where a value lives.
account = rev AND frame = fcst
- 02
One formula per concept, not per cell
A P&L with 200 accounts × 60 periods is ~20 formulas, each attached to a condition. Fewer tokens, reviewable diffs.
frame = fcst AND account = rev: PREV("value") * (1 + growth) - 03
Facts stay in tables
100,000 ledger rows are a table with an index, not 100,000 rows of a sheet. SUM(SELECT(...)) replaces SUMIFS over ranges that break when rows are added.
SUM(SELECT("amount","ledger", "account","=",THIS("account"))) - 04
Time is a dimension
Periods live in a table with Frame (hist/fcst), quarters, years. PREV, CUMULATIVE and YTD work by name.
PREV("value") CUMULATIVE("rev", -2) YTD("rev") - 05
Instant, incremental recalculation
A changed input recomputes only its dependents, even with 100,000-row fact tables.
100,000 rows, 1 change, < 1 ms engine time (B1.2 target ≤ 5 ms) benchmark
- 06
Explainable
Any cell can say which formula produced it and which inputs fed it. Agents verify their own work.
POST …/tables/pl/cells/explain
- 07
Deliverable to humans
The same model renders as a modern grid UI and exports to Excel (values + formats) for stakeholders who want a workbook.
finidb export ./model xlsx --out model.xlsx
- 08
A real database
Users, passwords, many databases per server, REST API, MCP server, CLI. Locally with npx finidb or hosted at finicast.com.
finidb://user:password@host:7407/database
How an agent uses it
Five steps, each one MCP tool or one REST call against the same API.
- 1
Connect
- MCP
finidb_describe- REST
GET /db/<project>/models
- 2
Describe the data
- MCP
finidb_import_csv- REST
POST /db/<project>/import/csv
- 3
Create tables & pivots
- MCP
finidb_create_table · finidb_create_pivot- REST
POST /db/<project>/commands
- 4
Place formulas
- MCP
finidb_add_method- REST
POST …/tables/pl/methods
- 5
Verify & export
- MCP
finidb_list_errors · finidb_export- REST
GET …/export/xlsx
Live demo
A read-only sales-ops model: edit the growth input and watch the pivot update, with a timing badge showing engine time.
Runs anywhere
Embedded with no daemon, a local daemon, or hosted here. Same commands, same file format.
npm
npm install -g finidb finidb serve --data ~/finidb-data --port 7407
Docker (planned)
docker run -p 7407:7407 -v finidb:/data ghcr.io/finicast/finidb serve
For humans too
The same model renders in a workspace: grid, pivot builder, conditional formulas, explain panel, export to Excel. Free while in preview.
Grid
Virtualised tables and pivots with frozen headers and frames.
Pivot builder
Drag reference tables into rows, columns and cells.
Conditional formulas
Name, apply on, where-chips, formula. Last match wins.
Explain panel
Which method produced this cell and which inputs fed it.
Export to Excel
Values and formats, one sheet per table or view.