DistillQL

Your agent knows your tables. It does not know what net revenue means.

DistillQL is a Python package that sits between your AI agent and your databases. It drafts what your company's terms mean from the queries people already run, hands the agent that page, and runs every query read-only as the person asking.

Register interest
$ distillql crawl distillql.yaml postgres 10 tables 393 queries 25 repeated reports snowflake 6 tables 84 queries 6 repeated reports wrote .distillql/context.yaml: 26 definitions, 19 joins, 14 metrics 9 need a decision, most used and least certain first $ distillql review .distillql/context.yaml net revenue service fees minus referral credits, by month 31 runs of rpt_finance_monthly agree 3 ad-hoc queries leave the credits out active renter any GMV in the month, stopped storage included from 4 months of active_renters, no disagreement renter region two readings in the history 12 queries: the region on the day of the charge 7 queries: the account's region today approve one, edit, or leave it drafted

How a question travels, before and with DistillQL

Before
  1. Question

    "What was net revenue in August?"

  2. Context

    The agent gets the schema: every table and column, and nothing about what they mean.

    Nothing says what net revenue is

  3. SQL

    The agent writes a query and chooses a definition on its own.

    A plausible wrong number

  4. Run

    A database server runs it as a shared service account.

    Every question sees all the account can

  5. Answer

    Every row returns into the conversation and the agent reads off a figure.

    No record of what ran, or why

With DistillQL
  1. 1 Question

    The same question, from the same person, in the agent they already use.

    Nothing new to learn

  2. 2 Context

    DistillQL hands over the definition, the tables it uses and the report that already computes it.

    Meaning your team approved

  3. 3 SQL

    The agent writes the query from that definition. DistillQL checks it: one read, known columns, defined terms.

    Undefined means "undefined", not a guess

  4. 4 Run

    DistillQL runs it as the person asking, on every database it touches, within a cost cap.

    Your own row filters and grants apply

  5. 5 Answer

    The rows stay in DistillQL. The agent gets a summary and the record of what ran; it can propose a good answer for your team to keep.

    Every answer shows its work

DistillQL is one server with five tools, standing where the catalog server and the database server would be. The agent still writes the SQL. Each stage below says what DistillQL does there, with an example from the synthetic marketplace we test on.

1Question

  • Asked in the agent you already use. Point Claude Code, Claude Desktop, Cursor, LM Studio, or an open model through goose at DistillQL's server. It appears as five tools: search, query, profile, write, verify.
  • Or asked from Python. import distillql gives the same five functions and the engine underneath them.
  • Nothing to model first. pip install distillql, point it at your databases, run the crawl. Our ten-table test database crawls in under a second; a few hundred tables take minutes.
Claude Code, Claude Desktop, Cursor: ~/.claude.json
{ "mcpServers": { "distillql": { "command": "python", "args": ["-m", "distillql.mcp_server", "distillql.yaml"] } } }
LM Studio: ~/.lmstudio/mcp.json
{ "mcpServers": { "distillql": { "command": "python", "args": ["-m", "distillql.mcp_server", "distillql.yaml"] } } }
Ollama, through goose: ~/.config/goose/config.yaml
GOOSE_PROVIDER: ollama GOOSE_MODEL: qwen3 extensions: distillql: type: stdio cmd: python args: ["-m", "distillql.mcp_server", "distillql.yaml"]

The same server in each. Ollama runs the model, not the agent, so it pairs with an agent that speaks MCP; goose is one.

2Context

  • Drafts the definitions from what your company already runs. The schema, a profile of every table and the query history, mined into join paths, measures, metrics, the reports that repeat, and the definitions behind them. Each line carries the queries it came from.
  • Serves them all, marked drafted or verified. Nothing has to be reviewed before the first question. The agent sees each definition with its status and prefers the verified ones. Your team verifies a line in one call; an agent can propose one, never approve it.
  • Says "undefined" when nothing was approved. A term with no definition is handed over as such, so the agent asks instead of guessing.

net revenue

Service fees minus referral credits, by month.

31 runs of rpt_finance_monthly agree
3 ad-hoc queries leave the credits out

verified by the finance team, 12 Sep

The agent's guess, without it

$1,284,310

fees summed, credits left out: 6% high and plausible

With it

$1,207,955

matches the finance report

If definitions already live in dbt metrics, a semantic layer, Snowflake Semantic Views or a catalog such as OpenMetadata, DistillQL reads those first and mines history only for what they leave out: one set of definitions, not two. On Snowflake alone, Cortex Analyst does part of this inside Snowflake; DistillQL does it over Postgres and the warehouse together, in the agent you already use.

3SQL

  • The agent still writes it. DistillQL does not compile SQL from a model. It gives the agent the definition and the SQL that already computes it, and the agent writes from those.
  • One read-only statement, checked before it runs. Writes, DDL, a second statement and unknown functions are refused with the reason. A result is kept only by writing to a table your team named.
  • Checked against a cost cap you set. DistillQL estimates each query against the rows and bytes you allow and refuses over the cap before the warehouse bills a thing.
  • select * from eventsRefused before anything ran. events has about 2.1 billion rows and the query has no filter; the cap is 50 million. Add a date range, or ask profile for a summary.
  • delete from events where ts < '2024-01-01'Refused. query runs one read-only statement.
  • select pg_read_file('/etc/passwd')Refused. pg_read_file is not an allowed function.
  • select sum(fee) - sum(credit) from … where month = '2026-08'Ran. One read, 2 tables, estimated 18 MB, within the cap.

4Run

  • As the person asking. DistillQL signs in as them, or as their database role, so the grants and row filters you already maintain decide what each question can see. There is no shared account to over-privilege.
  • Where your data runs. A Python package on your machine or your server. There is no DistillQL service and no telemetry. DistillQL reads schemas and history with code, not a model; the only model in the loop is the one your agent already uses.
  • Across databases when it must. A question that spans Postgres and the warehouse is split, each part run where its data lives, and joined locally. Today that runs on Postgres and DuckDB; the three warehouse connectors are written and unproven.

As a shared service account

renterregionGMV
Halden LabsEU48,200
Oriel AIUS44,910
MistvaleEU31,075
TanagerAPAC29,660
NorthbeamUS27,300
QuillonUS22,480

With DistillQL, as priya.n (EU)

renterregionGMV
Halden LabsEU48,200
MistvaleEU31,075
4 rows outside EU never read. The filter is Postgres's own, applied because the query arrived as her.

5Answer

  • The rows stay in DistillQL. The agent gets a handle: a count, a sample, a profile, a chart, or a write to a named table. Ten thousand rows cost it a few hundred tokens, not four hundred thousand.
  • Every answer carries its record. What ran, where, as whom, which definitions it used, where each column came from, and what it cost. A week later, "where did this number come from?" takes one look, not a transcript.
  • Good answers are kept. A verified question and its SQL are saved for the next agent to reuse instead of rewriting, and for your team to correct in one place.
question
Net revenue, August 2026
answer
$1,207,955, 1 row; handle h_7f3a
definition
net revenue, verified by the finance team 12 Sep 2026
ran as
priya.n, read only
where
postgres: billing.charges, billing.credits
columns
net_revenue ← sum(charges.fee) − sum(credits.amount)
cost
0.4 s, 18 MB scanned, within the cap
saved as
verified query "net revenue by month", 2 reuses

6Where everything lives

  • One config file you write. distillql.yaml names each database and how to sign in as the person, the row and byte caps, and the one folder write may put files in. No credentials go anywhere else.
  • One context file DistillQL writes. .distillql/context.yaml is the crawl's output: every drafted definition, join path, metric and repeated report, each with the queries it came from, a confidence, and a status of drafted or verified. Plain YAML; your team can edit it, diff it, keep it in git.
  • Query history comes from the database. Postgres keeps it in pg_stat_statements; Snowflake in its query history. DistillQL reads what is already there. It does not install anything in your database.
  • Every answer's record comes back with the answer. Today it is returned to the agent and to Python, not stored. Writing each record to .distillql/records/, and writing verified queries back into the context file, are the next build; both are small.
  • Results live in memory until you keep them. Handles sit in DistillQL's local engine for the session. write saves one to a named table or a file in the write folder; nothing else leaves.
distillql.yaml you write it: databases, identity, caps .distillql/ context.yaml DistillQL writes it: definitions, joins, metrics, reports; each with sources, confidence, drafted | verified records/ next build: a record per answer <write folder>/ only what the agent asked to keep in your database, untouched pg_stat_statements the query history DistillQL reads grants, row filters what each person may see in memory, for the session handles result rows, until written or dropped

Nothing is sent anywhere. Two files on your disk, and the database you already have.

We measured it before we described it

Right answersCost per questionTokens per question
AI today: the schema and a SQL tool, guessing what the terms mean77%$0.03519,669
DistillQL: default settings95%$0.02217,835
DistillQL: enhanced with human-in-the-loop definitions100%$0.02116,441