DEV Community

Cover image for Let Claude Code read your production database — even a write disguised as SELECT gets refused
metataro
metataro

Posted on Edited on

Let Claude Code read your production database — even a write disguised as SELECT gets refused

Pointing an AI agent at a database is easy. Pointing it at production is where people stop.

dbboard is an open-source desktop database client with an MCP server that gives Claude Code read-only access to Postgres, MySQL, Turso / libSQL, Cloudflare D1, MongoDB and Firestore — enforced by the database, not by a keyword filter.

The usual guard is a keyword filter: reject anything that starts with UPDATE or DROP. It holds until the agent writes a CTE that deletes, a SELECT … FOR UPDATE, or two statements in one string. And the agent still needs the password, which now sits in its config and its transcript.

dbboard is built so that this question has a boring answer.

dbboard with a Postgres server open: databases, schemas and tables in the sidebar, and a JOIN with 180 rows in the grid

The one idea: the database says no, not only a filter

dbboard ships a separate MCP server, dbboard-mcp, that hands the connections you already have to Claude Code or Claude Desktop. Every read passes two checks.

First, the statement is parsed — a real SQL parser, not string matching — and anything that is not a single read-only statement is refused. Then it runs inside the database's own read-only mode, or through an adapter that cannot write at all:

Engine How a read is run
Postgres, Neon, Supabase, CockroachDB, Aurora DSQL BEGIN TRANSACTION READ ONLY
MySQL / MariaDB SET TRANSACTION READ ONLY
Turso / libSQL PRAGMA query_only
Cloudflare D1 no read-only mode exists, so the parser is the enforcement and fails closed
Cloud Firestore the adapter has no write path to reach: it implements the read endpoints and nothing else
MongoDB only six commands (find, aggregate, count, distinct, listCollections, listIndexes) and only their known options; the pipeline is walked for a $out or $merge hidden inside a $facet, $lookup or $unionWith

The second check is the one that matters. Here is dbboard-mcp against a local Postgres with fictional data, called exactly as an agent calls it:

run_read_query  DELETE FROM orders WHERE quantity = 1
→ query failed: not a single read-only statement: only read-only SELECT / WITH / EXPLAIN statements are allowed

run_read_query  WITH gone AS (DELETE FROM orders RETURNING id) SELECT count(*) FROM gone
→ query failed: not a single read-only statement: a data-modifying statement (in a CTE or set expression) is not read-only

run_read_query  SELECT nextval('orders_id_seq')
→ query failed: cannot execute nextval() in a read-only transaction

run_read_query  SELECT count(*) FROM orders
→ 180
Enter fullscreen mode Exit fullscreen mode

The first two are caught by the parser. The third is a plain SELECT that still writes — it advances a sequence — so no parser flags it. Postgres refuses it, because the transaction itself is read-only. The table still has all 180 rows, and the sequence did not move.

Three more rules sit around that:

  • Passwords never reach the agent. They stay in the OS keychain. The agent sees { id, name, kind } for each connection, and no error message embeds a URL.
  • Writing is off until a person turns it on, per connection. Even then only an allowlist runs (INSERT, UPDATE, DELETE, CREATE TABLE, …). DROP of anything, TRUNCATE, GRANT / REVOKE and user or role changes are refused whatever the setting says.
  • Results are bounded, so one careless query cannot pour a whole table into the agent's context.

How to give Claude Code read-only access to your database

Download dbboard-mcp for your platform from the latest release (one self-contained executable), then:

# macOS
claude mcp add dbboard --scope user -- "$HOME/.local/bin/dbboard-mcp"
Enter fullscreen mode Exit fullscreen mode
# Windows (PowerShell)
claude mcp add dbboard --scope user -- "$env:LOCALAPPDATA/dbboard/dbboard-mcp.exe"
Enter fullscreen mode Exit fullscreen mode

The agent can then list connections and tables, describe a table, search a connection's schema by column name, follow foreign keys, and run read queries. Ask it "which orders in the last week have no shipment row?" and it finds the tables itself.

And for the person: one window

The desktop app is the same connections, for you:

  • Every database you look after in one sidebar — Postgres (Neon, Supabase, CockroachDB, Aurora DSQL), MySQL / MariaDB, Turso / libSQL, Cloudflare D1, Firestore and MongoDB. An SSH tunnel reaches the one behind a bastion; a colour and a tag on each connection show which one you are about to run against.
  • Leave the database name blank and one login lists every database it can open, with schemas and tables beneath.
  • Fix one value without writing an UPDATE: double-click the cell, type, save. Edits are keyed on the primary key, so a table without one stays read-only instead of guessing.

What it is not

Not a hosted service: nothing leaves your machine except your own queries to your own databases. Not code-signed either — a certificate is a recurring purchase this project does not make, so every release ships SHA256SUMS.txt to check instead.

Try it

  1. Download from the download page (Windows x64 / ARM64 .exe, macOS .dmg)
  2. Manage → Add connection, pick the database type, fill in the server
  3. Add dbboard-mcp to your agent as above

MIT-licensed. Bug reports and requests: Issues.

Top comments (0)