Why I built a SQL client when 10 already exist — then let AI agents into it

dev.to

There are plenty of good SQL clients. I built another one. For a long time the honest answer to "why?" was just taste — I wanted something fast, keyboard-first, monospace-native, with no telemetry and no ceremony. A tool that gets out of the way. That's a fine reason to build for yourself, but it's a weak reason to expect anyone else to care.

Then AI agents showed up, and the question changed.

The new problem nobody's client was built for

More and more of my day runs through an AI agent. And increasingly that agent wants to touch the database — check a migration, count some rows, verify data before it writes code against it.

The naive way to give it that is genuinely alarming: hand the agent raw connection credentials, or wire up a "run this SQL" tool with no guardrails. Now a probabilistic system has an unmonitored write path to your data. One confidently-wrong UPDATE ... WHERE (or a missing WHERE) and you're restoring from backup.

I didn't want to hand an agent my database. I wanted to lend it — with rules. None of the clients I had were built around that, so that became the actual reason for mine to exist.

The design: three mechanisms, not one big trust fall

data-peek can now run a local MCP server, so any MCP client (Claude Code, etc.) can work against your connections. The interesting part isn't the protocol — it's the safety model. Three independent mechanisms:

1. Reads that physically can't write

The read tools (run_query, list_schemas, explain_query) don't just promise to be read-only. Each query runs inside a transaction that is always rolled back, and on PostgreSQL the session is additionally set to SET TRANSACTION READ ONLY. Results are capped at 500 rows.

So even if an agent crafts something clever — a data-modifying CTE, a function with side effects — the transaction is discarded either way. "Read-only" is enforced by the database, not by string-matching the SQL. Defense in depth beats a regex every time.

2. Writes ask permission — from a human

There's exactly one tool that can change data, and it doesn't run anything on its own. execute_statement shows you the exact SQL in an approval dialog inside the app and blocks until you decide. Approve and it runs; reject and it doesn't; ignore it for 60 seconds and it auto-rejects.

The agent proposes. You dispose. That single interaction turned out to be the whole product — the moment you feel in control instead of hoping the model behaves.

3. Everything is remembered — tamper-evidently

Every statement — reads and writes, agent or human — lands in a local audit log. Each entry is hashed together with the hash of the entry before it, forming a chain. Edit or delete a past entry and the chain breaks from that point, which a one-click Verify surfaces (it reports the first broken entry). You can export the whole thing to CSV/JSON.

It's local, off by default, and prunable. Nothing is uploaded — the point is you can answer "what did the agent actually run," not that I can.

The boring guarantees that make it usable

  • Off by default. The server only runs when you flip it on in Settings, and stops when you flip it off.
  • Localhost only. Bound to 127.0.0.1, secured with a bearer token you can regenerate to revoke clients.
  • Credentials never leave. No connection password is ever exposed through any tool.

Setup is one command it generates for you — claude mcp add ... — and you're connected.

What building it taught me

  • The UX is the security. The read-only transaction and the hash chain are the load-bearing engineering, but the thing that makes people trust it is a dialog that shows the exact SQL. Safety you can see beats safety you're told about.
  • Guarantees vary by database. "Read-only" is a clean, enforceable concept in Postgres and a fuzzier one elsewhere, so the guarantee is layered — DB-level where possible, statement-level as a backstop. Pretending one mechanism covers every engine would have been a lie.
  • Off-by-default is a feature, not a cop-out. For a tool that can hand your database to an agent, the safest default is "does nothing until you ask."

So — why another SQL client?

Because the ten good ones were built for a world where the only thing querying your database was you. That's no longer true, and "let an agent in, but on your terms" turned out to be a real, unmet design problem — not a checkbox you bolt onto an existing tool.

data-peek is a fast desktop SQL client for PostgreSQL, MySQL, SQL Server, and SQLite. It's MIT source, free for personal use, and there's a continuity guarantee so adopting it isn't a leap of faith.

If you've given an agent database access some other way, I'd genuinely like to hear how you handled the write path — that's the part I'm least done thinking about.

Source: dev.to

arrow_back Back to News