Every database tool assumes a human at the keyboard. Quarry assumes an agent — and gives it one safety-railed query kernel with many faces: CLI, GUI, agent skill, MCP.
$ qy exec shop --sql "select …" --format json ✓ exit 0 — auto LIMIT applied $ qy exec shop --sql "delete from orders" ✗ exit 8 — blocked without --write
DBeaver, TablePlus, pgAdmin — great tools, all designed for a person clicking. But more and more queries are run by AI agents, in skills, scripts, and CI. An agent can't "be careful". The tool has to be.
Every query returns {columns, rows, rowCount, truncated, elapsedMs, engine, sql} — a stable, structured contract. No screen-scraping, no prose parsing.
Read-only by default, automatic row caps, graduated prod confirmation. The rails are in the kernel, so no client — human or agent — can pick an entry point that skips them.
Exit codes are API: 0 ok, 2 connection, 3 SQL, 8 safety block. An agent branches on outcomes instead of guessing from stack traces.
We didn't bolt a chatbot onto a database GUI.
Your agent already is one — Quarry gives it a safe socket into your data.
qy gui serves a local, zero-build web workbench over the same kernel: grouped connection tree with an environment switcher (prod turns red), a multi-tab SQL editor with autocomplete, one-click EXPLAIN, a type-aware grid with keyboard navigation and a collapsible JSON inspector, searchable history — light & dark, English & 中文, state persists across restarts.
Connection management, query execution, schema introspection, and safety rails live in one importable kernel. Everything else is a thin shell. Fix a bug once — every face gets it. Add a rail once — no face can forget it.
connections · execution · introspection · safety rails · result contract
Writes and DDL are blocked unless you pass --write. Production connections require an additional confirmation on top of that. Every query gets an automatic LIMIT 500 unless you opt out. Environments default to dev — the safest one.
A workspace is just a directory: connections.toml plus named queries as plain .sql files with metadata headers. It lives in your repo, versioned by git, shared between teammates and agents alike. The kernel itself carries zero secrets and zero business logic — and Quarry aggregates multiple workspaces into one view.
[shop_dev] url = "postgresql://…dev…/shop" db = "shop"; env = "dev" [shop_prod] url = "postgresql://…prod…/shop" db = "shop"; env = "prod" [internal_db] url = "postgresql://…@127.0.0.1/appdb" ssh_host = "bastion.example.com" # auto tunnel
-- @name: recent_orders -- @db: shop -- @desc: Latest orders with customer names -- @param: days (int, default=7) SELECT o.id, c.name, o.amount, o.status FROM orders o JOIN customers c ON c.id = o.customer_id WHERE o.created_at > now() - make_interval(days => :days) ORDER BY o.created_at DESC;
Same logical database across dev / staging / prod folds into an env-set — one saved query runs against any environment: qy run recent_orders --env prod.
Pure Python stdlib. Engines shell out to the battle-tested clients you already have — no Electron, no daemon, no cloud, no telemetry. Install with pipx, it just runs.
from quarry import run_query — the same kernel the CLI, GUI and MCP server use is three lines away in your own tooling, with the same rails and the same result contract.
Your connections, queries and results never leave your machine. The GUI binds to localhost and rejects non-local origins. There is nothing to phone home to.