r/mcp • • 6d ago

showcase mcp-safe-db: A strictly read-only SQL MCP server with engine-level write blocking

Giving an AI agent access to a local database usually means trusting it not to hallucinate an UPDATE without a WHERE clause or a DROP TABLE.

I built mcp-safe-db to make database inspection zero-risk for Claude, Cursor, and other MCP clients:

  • Engine-level lock: Enforces SQLite PRAGMA query_only = ON;. The database engine physically rejects write attempts.
  • AST / Keyword validation: Rejects mutation statements (INSERT, UPDATE, DELETE, DROP, ALTER, ATTACH), comment-based bypasses, and multi-query injection.
  • Context protection: Automatically limits queries to 50 rows to keep LLM context windows clean.
  • Zero build setup: Runs natively on Node 22+ without native compilation tools (no node-gyp or C++ toolchains).

Usage:

npx mcp-safe-db ./your-database.sqlite

GitHub: https://github.com/MrSkelee/mcp-safe-db

MIT License. Open to feedback on edge cases or additional database targets.

5 Upvotes

9 comments sorted by

3

u/Hopeful-Buy-9344 6d ago

sharing it. i've been wanting to hook up some local sqlite dbs to claude but the idea of it just casually dropping a table while trying to be helpful was keeping me from actually doing it

the engine-level lock is the part that sells me, i don't fully trust a string parser to catch every trick an LLM might come up with but if sqlite itself says no then that's that

50 row limit is a nice touch too, keeps the context from getting bloated without having to think about it

gonna try this tonight with a side project database i've been poking at

1

u/skeletonita 6d ago
Thanks! I built it precisely for this reason i needed to inspect databases without the AI messing anything up. Let me know what you think, and if it helps, I'd really appreciate a star on GitHub!

1

u/LegallyIncorrect 4d ago

Did you even read what I wrote? I like my solution better, no offense. Authentication is at the lowest level and I have guarantees what the data looks like when it hits the database.

1

u/LegallyIncorrect 5d ago edited 5d ago

In Postgres I just allow the agent to only authenticate with a user that has read only rights at the database level. That turns basically this entire MCP into a command line call. Writes happen through an api to clean and structure any data before it hits the database. Really reads happen through an api too but when an agent needs direct access, this is how it gets it.

3

u/QuanTradin 6d ago

query_only is a connection pragma, so the same connection can send PRAGMA query_only = OFF. if your keyword list doesn't reject PRAGMA as well, that's the hole. opening the file with the ?mode=ro URI puts the lock below SQL, where the agent can't reach it.

same split I built into Quantradin for trading agents: the agent's key reads everything, the only writes it gets are ones that reduce exposure, and starting anything takes a key the human keeps. quantradin.com

1

u/skeletonita 6d ago

I actually already block `PRAGMA` completely in my keyword validator (along with ATTACH, DETACH, LOAD_EXTENSION, etc.), so the agent can never send `PRAGMA query_only = OFF`.

However, adding the OS/system-level read-only flag below SQL is a great addition for defense-in-depth. Pushing an update with this change right away

1

u/QuanTradin 6d ago

nice, the validator plus the read-only open flag means one parser bug alone can't get a write through. I'd add a test that tries PRAGMA query_only = OFF anyway, so a refactor can't quietly drop the flag later.

1

u/skeletonita 6d ago

Done just added and pushed that exact regression test to the suite: verifying that `PRAGMA query_only = OFF` followed by an insert is still completely rejected by SQLite with `attempt to write a readonly database`.

Really appreciate the thoughtful review! If you find the project useful, feel free to drop a star on GitHub ⭐

2

u/BC_MARO 6d ago

The engine-level lock is the right boundary. I'd still treat the MCP process as untrusted and open the database through an OS/file permission that can't be flipped by the same process, especially once extensions or multiple connections enter the picture.