Most ways of giving an AI agent access to a database start the same way: you paste a connection string into a config file. mysql://root:hunter2@prod-db:3306/shop. From that moment the agent has everything you have — every table, every privilege, no supervision, and a copy of your password sitting in plaintext on disk.
CroDB v2.1 takes the opposite approach. It ships an MCP server, but the agent never gets a connection string, never gets a password, and cannot open a database connection at all. It gets a supervised window onto connections you have already opened and explicitly shared, and every change it wants to make stops at a dialog you have to read.
This article explains what that looks like in practice, and — just as importantly — where the limits are.
What MCP is, briefly
The Model Context Protocol is a standard way for an AI client (Claude Code, Claude Desktop, Codex, and others) to discover and call tools that a local application exposes. An MCP server advertises a set of tools; the agent decides which to call and with what arguments; the server decides what actually happens.
That last clause is where the whole design lives. An MCP server is not an API wrapper. It is a policy boundary. Whatever the model decides to ask for, the server is what says yes or no.
The server lives inside CroDB
CroDB does not ship a separate command-line tool that talks to your databases. The MCP server runs inside the app itself, and reuses the app's own connection layer.
That has three consequences worth spelling out:
Your credentials never move. Passwords stay in the Keychain, where CroDB already keeps them. SSH tunnels are the ones CroDB already opened. Nothing is copied into a config file for an MCP client to read.
The agent inherits your session, not your privileges. It can only work through a connection you currently have open in CroDB. Close the connection and the agent's access disappears with it.
The safety rules are the same ones the app already enforces. Agent SQL goes through the same tokenizer, statement classifier and risk analyzer that guard the SQL editor. An agent is held to the app's existing standard — and then to a stricter one on top.
The server listens on 127.0.0.1 only, on port 7657 by default, and requires a bearer token. Loopback is not the same as trusted: every other process on your Mac can reach loopback too, so the token is not optional.
Three gates before anything runs
Access is deliberately layered, so that no single mistake is enough.
Gate one: the grant. Every saved connection has an agent access level — No Access, Read-only, or Read & Write. New connections start at No Access, and so does every connection you already had when you upgraded. Agents cannot see a connection you have not deliberately shared; it does not appear in list_connections at all, and knowing its id does not help.
Gate two: the open connection. A shared connection still has to be open in CroDB. The agent cannot connect on your behalf, because connecting would mean reading a password. If a connection is closed, the agent is told to ask you to open it.
Gate three: the statement. Every statement is classified before it is sent. On a read-only connection, only SELECT, WITH … SELECT, SHOW, DESCRIBE, VALUES, TABLE and EXPLAIN are accepted — a deliberately narrower list than "things that usually read", because verbs like CHECK and the cursor statements have engine-specific forms that do more than read.
That gate catches the cases a keyword check would wave through:
-- Refused: a DELETE hiding in a CTE, returning rows like a SELECT
WITH moved AS (DELETE FROM orders WHERE id = 1 RETURNING *) SELECT * FROM moved
EXPLAIN ANALYZE DELETE FROM orders
SELECT * FROM orders INTO OUTFILE '/tmp/orders.csv'
-- Refused: EXPLAIN ANALYZE really executes the statement
-- Refused: a SELECT that writes a file
One statement per call, too. A batch is refused, because you cannot judge what you cannot see, and because a batch is where the second statement hides.
What agents can read
Fifteen tools, most of them read-only. The useful ones in practice:
describe_table— columns with types and nullability, indexes, foreign keys, check constraints, triggers. One call, identical shape on MySQL and PostgreSQL, and far cheaper than making the model reconstruct all that frominformation_schema.read_table— rows with paging, sorting and filtering, without any SQL. This is the preferred path for looking at data: it cannot be malformed and it cannot reach anything but the table named.run_query— a single read-only statement, for the joins and aggregates thatread_tablecannot express.explain_query— the query plan, without running the query.
There are also crodb:// resources, so you can hand an agent "the shape of this database" as context rather than making it spend tool calls rediscovering your schema every turn.
Results are capped: 200 rows, 4 KB per cell, 256 KB per response, 15 seconds per query, all adjustable. When a result is cut, the response says so explicitly. A partial answer that reads as a complete one is worse than no answer.
Writes stop at a dialog
execute_statement does not execute anything. It hands the statement to you.
CroDB shows the SQL verbatim, alongside the same risk analysis the editor would show — UPDATE without WHERE, Destructive SQL, production connection. You approve or you deny. On a production connection you type the database name first. Dismissing the dialog with Escape counts as a refusal, because silence must never read as consent.
The tool call waits about 25 seconds for you — long enough for the common case where you are at the keyboard — and then hands the agent a ticket to poll, so a client that gives up waiting does not cancel a decision you are in the middle of making. Requests expire after five minutes. A stale approval dialog is a trap: nobody remembers, an hour later, what the agent was trying to do.
Approval is the main protection but not the only one. Some statements are refused outright, no matter who approves them:
GRANT,REVOKE,REASSIGN— privilegesCREATE/ALTER/DROP USERandROLE— accountsSET GLOBAL— server-wide configurationKILL,SHUTDOWN,FLUSH,INSTALL— server state- anything touching server files:
INFILE,OUTFILE,DUMPFILE
The reasoning is blunt. A tired person clicking through an approval dialog is exactly the failure mode a prompt-injected agent would aim for, so the blast radius of a "yes" is kept to the data in front of you. Privilege escalation is not something a click should be able to buy.
The pattern worth adopting: transactions
The best way to use write access is not to approve statements one at a time. It is this:
begin_transaction → a dedicated session, isolated from your tabsexecute_statement → the change, approved onceread_table / run_query → read back what actually changed (inside the transaction, so it sees its own rows)commit_transaction → you decide, knowing the real row counts
Inside a transaction the agent sees its own uncommitted rows, which is what makes "change it, then tell me exactly what changed" possible. Writes inside a transaction report "durable": false, so an agent cannot honestly claim a change has landed when it has not.
Rolling back runs immediately and needs no approval — discarding your own uncommitted work requires nobody's permission. Committing does, because that is the point of no return. If you refuse a commit, the transaction stays open so it can still be rolled back.
Agent transactions run on their own sessions and never touch the transactions you have open in your own tabs. Quit the app with one outstanding and CroDB tells you, by name, before it rolls it back.
The underrated tool: hand it to the human
open_in_editor puts the agent's SQL into a new CroDB query tab and runs nothing.
It works even on a read-only connection, and that is the point. A read-only agent can still write you a migration, a backfill, a cleanup script — and you run it, in your own editor, with your own eyes on it. For most changes worth making, that is a better workflow than an approval dialog: same review, more context, no rush.
open_table does the same for data, opening the grid with the rows the agent is describing.
Seeing what happened
An MCP server that can read your databases should never be invisible.
The window toolbar carries a live status indicator: grey when nothing is shared, green when agents can read, orange when something wants your attention, with the port and counts in the tooltip. Clicking it opens the AI Agents settings.
Underneath, every call — succeeded, refused, or failed — lands in an activity list in Settings, and every statement an agent runs is saved to Query History beside your own. And there is one button, Disconnect All Agents, that stops the server, refuses everything waiting for approval, and rolls back any transaction an agent left open.
Setting it up
Turn on the server in Settings → AI Agents, set at least one connection to Read-only, and copy the generated configuration. CroDB produces the exact snippet for your client, with the live port and token already in it.
Claude Code connects over HTTP:
claude mcp add --transport http crodb http://127.0.0.1:7657/mcp \ --header "Authorization: Bearer <token>"
Claude Desktop and Codex launch a small stdio bridge — the CroDB binary in --mcp-stdio mode, which is pure transport and forwards to the running app:
[mcp_servers.crodb]command = "/Applications/CroDB.app/Contents/MacOS/CroDB"args = ["--mcp-stdio"]env = { CRODB_MCP_TOKEN = "…", CRODB_MCP_PORT = "7657" }
Because CroDB is sandboxed it cannot write into another app's configuration file, so it generates the text and you paste it. Four presets are included: Claude Code, Claude Desktop, Codex, and a generic description for anything else that speaks MCP.
Four prompts ship with it too — optimize_query, explain_schema, write_migration, investigate_slow_query — each steering the agent towards evidence rather than guesswork. The migration prompt explicitly tells the agent not to use execute_statement: migrations belong in your versioned tooling, not applied ad hoc from a chat window.
What it does not do
- CroDB has to be running. The server lives in the app. The stdio bridge will launch it if it is closed, but there is no headless mode.
- Agents cannot open connections. This is deliberate and will not change. Opening a connection means reading a password.
- Nothing leaves your Mac. No account, no cloud, no telemetry. The server is bound to loopback and refuses anything without the token.
- Results are partial by default. The caps exist for a reason, but an agent that ignores a truncation notice will reason from incomplete data. The notice is explicit; models are not always careful readers.
- Row data is untrusted input. Text in a database can be written to look like instructions. CroDB caps what comes back and labels it as data, and no write happens unattended — but prompt injection is a live problem and this reduces the blast radius rather than eliminating it.
- Approval fatigue is real. The design tries to earn each prompt: reads never interrupt you, transactions batch a whole change into one decision, and
open_in_editorexists so routine changes need no dialog at all. If you find yourself clicking Approve without reading, that is a signal to move the work to the editor.
Why this shape
There are already many database MCP servers. Nearly all of them take a connection string and hand the model raw SQL, which makes them a thin, credentialed pipe to production.
CroDB is not trying to be a faster pipe. It is trying to be the place where a person stays in the loop — where an agent can read broadly, propose freely, and change nothing without someone who understands the consequences saying so. The database client already had the connection, the credentials, the risk analysis and the audit trail. Putting the MCP server there, rather than beside it, is what makes the safe version possible.
Available in CroDB v2.1 on macOS 15 and later, for MySQL and PostgreSQL.