Cut your BI costs. Let your agent do the analysis.
Open-source BI on one server. Your agent builds it, runs it and answers from metrics your team agreed on.
- Questions your agents asked
- 90,000
- Your server
- $70
- A cloud warehouse
- $853 to $2,609
1 TB of data, 3,000 questions a day. List prices read 27 October 2026.
- Data apps and agents
- reports: built on your metrics. agents: ask in plain words.
- Metrics Book
- metrics: one definition each. engine: anfra.
- Models
- transform: dbt, tested. store: DuckDB.
- Your data
- load: dlt, on a schedule. from: databases, APIs, files.
Cloud BI is easy but expensive. Open source is cheap but hard to run.
- A cloud warehouse and a BI tool run it for you, and bill every query and every seat.
- An agent on a cloud warehouse lets your agent build and ask, and bills every question it asks.
- An open-source stack, by hand, costs one server, and takes a skilled person to build and keep running.
Metrics Book gives you the open-source price, and your agent does the running.
No surprise bill at the end of the month
A cloud warehouse bills every question, so one stuck agent lands on next month's invoice. Your server is sized for your busiest hour, and its price is fixed. Set your own data and questions below.
Your server, a Hetzner AX41, costs $70 a month. BigQuery and Snowflake cost $853 to $2,609, 12 to 37 times as much.
- Your server
- $70
- BigQuery
- $853
- Snowflake
- $2,609
Your serverHetzner AX41
6 cores · 64 GB RAM · 512 GB NVMe
Listed at € 59.00 per month, excl. VAT; + € 0.00 one-time setup fee, excl. VAT
Needs 5 cores, 26 GB RAM and 413 GB disk: 3 DuckDB threads to answer each question in about 2 seconds, and 187 GB of DuckDB files.
| Your server | BigQuery | Snowflake | |
|---|---|---|---|
| Computequeries and dbt | includedin the server's price | $824133 TiB scanned a month | $2,574Small warehouse, awake 11.0 hours a day (660 credits a month) |
| Storageyour data | includedon the server's NVMe | $18931 GiB stored | $251 TB stored |
| Serverthe stack | $66AX41: 6 cores (AMD Ryzen 5 3600), 64 GB RAM, 2 x 512 GB NVMe mirrored | $10Hetzner Cloud CX33 for the rest of the stack | $10Hetzner Cloud CX33 for the rest of the stack |
| Backupsoff the server | $4Storage Box BX11, 1 TB, for backups | includedin the vendor's price | includedin the vendor's price |
| Total | $70 | $853 | $2,609 |
What goes wrong when an agent runs your BI
It reads the wrong number
Net sales means something different in every dashboard. The agent can't tell which is right.
Here: each metric is one file that says what it counts, what it is not, and its caveats. The agent reads it before it asks.
It sees what you can't
With a shared warehouse key, an agent can read every table, whoever is asking.
Here: every request runs as the person asking. Cerbos decides it, and the audit log keeps it.
It changes last quarter's numbers
An agent with write access edits a metric, and a month you closed moves.
Here: definitions change only in git, through a pull request a person reviews. A closed month moves only when the pull request says it will.
Your agent does the analysis, from numbers you trust
Ask in plain words and get an answer you can act on. "Net sales" means one thing in every chat.
It finds the metric
Your agent searches the book by meaning, so "revenue" finds net sales.
It reads the definition
What it counts, what it is not, and the caveats your team wrote down.
It asks as you
The query runs with your access, and the audit log keeps it.
YouWhat were net sales last month, by sales channel?
search "net sales"metric total_net_sales_excl_vat · dataset ecommerce_salesget_metric total_net_sales_excl_vatcaveat: the latest weeks read high until refunds arrivequery ecommerce_sales by sales_channelallowed for you · 4 rows · written to the audit logNet sales excl VAT last month, by web, app, marketplace and store, by your team's definition. Refunds are still arriving, so the total may drop a little.
metric total_net_sales_excl_vat {
label: 'Total net sales excl VAT'
description: '''
What customers paid for the items they
bought, after discounts and less refunds.
Not the same as: Total gross sales excl
VAT, which is at list price.
Caveats: refunds keep arriving for weeks,
so the latest weeks read high.
'''
}An illustration of what an agent does over the MCP connector.
From an empty folder to 115 metrics in about a minute
Start from the e-commerce template, not a blank page. Your agent takes it from there.
$ uv tool install metrics-bookmb makes projects, adds templates and runs every check. Every command takes --json.
$ mb init acme_metrics
$ cd acme_metricsA new git repository: its folders, stack.yml, compose.yml and an AGENTS.md for your agent.
$ mb templates install ecommerceSample data, dbt models, 115 metrics and 8 reports, each file labelled as template.
$ mb dev upThe whole stack in Docker on your machine. 37 months of sample orders load and model.
$ # the connector's URL, as mb dev up prints it
$ claude mcp add --transport http metrics_book <url>Codex and Cursor take the same URL. Then ask in plain words.
Not released yet. These commands work from 0.1, the first release. See what works today.
Everything runs on one server
Follow one question through it. Every part is open source and code first, so your agent can change it and you can review it.
Every question is checked before it reaches your data
An agent asks as the person using it. A policy in git decides what each person may see, and the audit log keeps every answer and every refusal.
No shared key
Agents sign in over MCP with OAuth, as the person using them. Service accounts sign in with their own key pair. The query engine sits on an internal network only the API reaches.
Cerbos decides first
Every data request is decided before the query engine sees it. If Cerbos cannot answer, the request is refused, never let through.
What you may not see looks missing
A dataset you may not read gets the same refusal as one that does not exist, and the catalog lists only what you can use.
Every decision is logged
Refusals included. The log is append only, and a request whose audit row cannot be written gets no answer.
Policies live in git, with tests
mb checkcompiles and tests them on every change. No tool, route or screen edits a metric or a policy, and keys are stored only as digests or public keys.
Who asks
Asks for
- 1Signs in as themselves. Sam, marketing, roles viewer
- 2Cerbos decides.denysensitivity:restricted keeps viewer out
- 3The audit log keeps it, before any answer goes out.
- 4The answer. No metric by that name is available to you.
audit_event, newest first
| id | principal | via | dataset | decision |
|---|---|---|---|---|
| 1 | person:sam | mcp | ecommerce_profit | deny |
sensitivity:restricted.Access is decided per dataset. Rules per row, such as each market seeing only its own orders, are not in 0.1: put those rows in a dataset of their own. The access layer is built and tested; the MCP tools that use it are being built. See what works today.
Keep BigQuery or Snowflake if you want
The warehouse is one setting. Your pipelines, models, metrics, reports and access stay the same, so you can adopt the rest now and move your data to DuckDB later, or not at all.
# stack.yml warehouse: type: duckdb
The warehouse module writes
- dlt destination
- duckdb
- dbt adapter
- dbt-duckdb
- anfra data source
- DuckDB
Stays the same
- pipelines/, your dlt sources
- transform/, your dbt models
- semantic/, your metrics and reports
- access/, who may see what
- the API, MCP and the audit log
Built. The default, on your server.
Why it can switch
dlt, dbt and anfra each work with the same warehouses: BigQuery, Snowflake, Databricks, Redshift, Postgres, ClickHouse and DuckDB among them. Only one module knows which one you use.
Nothing in the template ties you to DuckDB
The template's dbt models use dbt's cross-database macros, such as
dbt.date_trunc, and the end-to-end test runs the template on DuckDB and Postgres on every change.What you keep paying
On BigQuery or Snowflake, their bill stays yours, as the calculator above shows. The rest of the stack still runs on one small server.
0.1 ships DuckDB and Postgres. BigQuery and Snowflake are each one module, built and tested against a real account before they are listed as supported.
Built on great open source
Metrics Book stands on these projects, and they deserve the credit.
Where to run it
One Docker Compose app on a Linux server with enough memory for your data. How to deploy says which servers give you the most for your money.
Open source, under Apache 2.0
Nothing is held back for a paid edition, because there is none. Your server, your git repository, your data.
Report a bug or ask for a feature
Open an issue on GitHub.
Contribute
How to set up, check a change and open a pull request.
The one closed part
anfra's compiler and query runner are closed binaries from Holistics.
Power BI Metrics Book
The sister project, for a company whose numbers live in Power BI. The two share one look and one set of words.
Need help with implementation or strategy?
Enum Studio builds and maintains Metrics Book, and helps companies set it up and build on it.