For developers
Connect Postgres. Write SQL. Read it anywhere.
eddy follows your Postgres, keeps the SQL models you write current on every commit, and serves them as a Postgres database, an HTTP API and an MCP server. Your coding agent can use the same MCP server to preview and apply models while production keeps running.
01 · MCP
Your coding agent, connected.
Add eddy as an MCP server in Claude, Claude Code or any MCP client. The agent reads the catalog and the views, previews SQL against real data, and — if you named it a writer — creates the model and waits for it to be ready.
generated from the OpenAPI · readers and writers · every change attributed
$ claude mcp add --transport http eddy https://acme.eddy.example/mcp signed in through AuthKit · writer › add a model for weekly active seats per account ▸ eddy.get_catalog 12 models · 6 tables ▸ eddy.preview_model refused a correlated scalar subquery DataFusion did not decorrelate is not rendered ▸ eddy.preview_model ok · 3,112 rows · 9 operators ▸ eddy.create_model command 312 · hydrating ▸ eddy.get_command 312 ready · built in 3.0 s, still ticking ▸ eddy.query select * from eddy.weekly_active_seats … Added weekly_active_seats. Rewrote the subquery as a join so eddy can keep it incremental; it matches Postgres row for row.
02 · The loop
Write, preview, review, apply, read.
The same lifecycle from your agent, the API or the console: nothing reaches production without a preview, and a replace shows its effect on the data first.
- 01 · write
SQL, where it lives
A dbt project (eddy reads the compiled manifest) or a folder of .sql files. Nothing eddy-specific in the SQL.
- 02 · preview
Ask before you build
preview_modelsays what the model returns and what the renderer can’t run yet, as data, with the reason, so you or your agent rewrite it. - 03 · review
See what changes
For a replace,
model_reviewruns both definitions in Postgres and shows the rows that move, what rebuilds and how long it takes. - 04 · apply
Online, logged
Create, replace or drop appends a command. The model builds beside the running tenant, is judged by Postgres from its first change, and the history names who asked.
- 05 · read
From anywhere
Your workspace’s Postgres endpoint for
psqland your app (views undereddy.), HTTP under/api, MCP at/mcp: one snapshot per read, at most one tick behind the last commit. - 06 · debug
Follow any number
Provenance walks a row back to its source rows; the trace follows one transaction through every operator it reached.
03 · Your workspace
Connect once. Get three endpoints.
Connect your Postgres and your dbt project, and the workspace gives you a Postgres endpoint, an HTTP API and an MCP server over the same live views. There is nothing to deploy or scale.
$ psql "$EDDY_URL" acme=> select count(*) from eddy.account_health; 3208 Time: 0.4 ms
source app@db.internal:5432 · slot eddy · 12 tables models dbt project · 12 models · all ready customers a tenant per account_id · 1,000 postgres postgres://acme.eddy.example:5439/acme http https://acme.eddy.example/api mcp https://acme.eddy.example/mcp readers everyone you invite writers dev@acme.com, ops-agent@acme.com
04 · SQL
The SQL dbt runs, shown as steps.
Joins (inner and outer), window functions, recursive CTEs, running balances, sessions, rolling windows, funnels, latest-per-key and top-N: fifty-six models, from jaffle-shop marts to TPC-H shapes, diffed against DuckDB after every chunk. What the renderer can’t run yet, preview_model says with the reason, such as a correlated scalar subquery that can be rewritten as a join.
select a.id, a.name,
sum(s.mrr) as mrr,
count(u.id) as events_30d,
case when count(u.id) < 50
then 'high' else 'low'
end as risk
from subscriptions s
join accounts a on a.id = s.account_id
left join usage_events u
on u.account_id = a.id
and u.at > now() - interval '30 days'
where s.status = 'active'
group by a.id, a.name
- fromsubscriptions18,204
- joinaccounts18,204
- wherestatus = 'active'15,977
- left joinusage, last 30 days402,118
- group byone row per account3,112
05 · Requirements
What your Postgres needs.
The connection check tests each of these before anything is read, and tells you the fix for any that fail.
- wal_level = logicalLogical decoding on; on RDS and Aurora through the parameter group.
- A free replication sloteddy needs one; adding a table online briefly needs a second.
- A role with REPLICATION and SELECTOn the published tables. A superuser works, but isn’t needed.
- Primary keysOn every table eddy reads; a table without one is keyed by columns unique today, with a warning.
- One publication, one sloteddy creates them, or uses a publication you make for it.
- max_slot_wal_keep_sizeBounds the WAL the slot can hold; the check says how long eddy could be stopped before the slot is invalidated.
06 · In and out
Postgres and Kafka in. A Postgres database out.
eddy follows your system of record and your event streams, and serves what it keeps the way your stack already reads data: as Postgres.
Sources
- PostgresLogical replication from the database your product runs on. Every commit, inserts, updates and deletes.live
- KafkaProduct events from a topic (Segment’s message format today), exactly once through a restart, joined with your Postgres tables in one query.live
- Files on S3Parquet and JSONL that land in a bucket, appended or replaced whole. Built and tested; coming to hosted workspaces next.next
- More sourcesTell us what your data lives in. We add sources behind the same interface, in the order customers need them.ask
Served as
- A Postgres databaseYour app,
psql, psycopg, SQLAlchemy and Metabase connect as they would to Postgres. Every metric is a table undereddy.live - An HTTP APIEvery view and every operation, described by an OpenAPI document, one customer’s rows at a time if you want them.live
- An MCP serverThe same views and operations as tools for agents, with read or write access per user.live
07 · Limits today
What it doesn’t do yet.
Stated plainly, so you can plan around it.
- Not self-serveEarly access; we onboard each team by hand.
- Files on S3Built and tested; not yet in hosted workspaces.
- KafkaSegment’s message format today.
- TenancyBy a tenant column in your tables, one store per customer.
- SQLWhat DataFusion plans, rendered operator by operator; the rest is refused with the reason at preview.
- PricingUsage-based, by rows processed and state kept; rates not published yet.
Keep going
Not self-serve yet.
Leave a work email and a founder replies within two business days; we onboard every team by hand, on your own schema, and connect your editor to it.