GRANT read, to an assistant

Your AI asks. You decide what runs.

Over the Model Context Protocol, an assistant such as Claude Code uses the running app: it reads the structure, runs reading queries within limits, and proposes everything else. Off by default.

The order report is slow. Why?

describe_table orders: 6 columns, 1 index

explain_query Seq Scan on orders, 48,112 rows

propose_query CREATE INDEX orders_placed_idx …

The report filters on placed_at, and there is no index on it. I proposed one: you will see the statement in the app, and it runs only if you click “Run”.

CASE connection WHEN …

Four levels, per connection.

What an assistant may do is decided in the backend, connection by connection. The assistant never opens a connection: it works on the ones you opened.

off

Off

Nothing. The connection is not even named. This is the default, and it stays the default for every connection with a protection level until you say otherwise.

structure

Structure only

Schemas, tables, columns, keys, DDL. No statement of the assistant runs, not even a plan: its estimates come from the data.

read

Reading queries

One reading statement per call, on a session that may only read on the server, for at most 30 seconds, within the limits below.

propose

Writing as a proposal

The assistant may propose statements that write. Each one is shown to you and runs only after your click. A read-only connection never writes, not even this way.

COUNT (*) = 8

Eight tools.

ToolNeedsWhat it does
status–Version and limits
list_connections–Id, name, kind, open or not, level. No host, no user, no password
get_schemastructureThe schemas, or the objects of one schema
describe_tablestructureColumns, keys, relations, DDL
run_queryreadOne reading statement, limited
explain_queryreadThe estimated plan, without execution
write_to_tabstructureText into a new tab. Nothing runs
propose_queryread / proposeA proposal to you. It runs only after your click

READ ONLY twice

What “reading” means.

Two locks, not one. The statement must be classified as reading by the same check that protects production. And it runs on a session of its own that the server allows only to read.

PostgreSQL

Read-only transactions

default_transaction_read_only on the session of the assistant.

MySQL · MariaDB

Read-only session

SET SESSION TRANSACTION READ ONLY.

SQLite

query_only

The pragma is set for the call and taken back afterwards.

SQL Server · Oracle

Rolled back

They have no such switch for a session: the statement runs in a transaction that is rolled back.

ClickHouse

readonly = 1

Set on the session of the server, where it cannot be taken back. Table functions that reach out (url, file, remote) do not count as reading.

DuckDB

Structure only

A query there can read any file of the computer, so none is run for an assistant.

one statement

No session control

BEGIN, COMMIT, USE, SET and the like do not count as reading.

30 s

A time limit

A call ends after thirty seconds at the latest.

LIMIT 200

What may leave.

Whatever an assistant reads goes to the assistant, and with it to its provider. So the answer is bounded, and you can see every call.

200rows per answer by default, 1,000 at most
100kcharacters per answer by default
2000characters per value, then it is cut and says so
256bittoken between the assistant’s helper and the app
Hidden columns

Columns whose name contains password, passwd, pwd, secret, token, api_key, private_key, credential or a pattern of your own come back as [hidden].

This is a filter over names, and it says so: an alias (SELECT password AS x) or an expression gets around it. If you need to be sure, give the connection “structure only”, or give the assistant a database user of its own without rights on those columns.

Proposals

propose_query shows you the statement, the connection and the reason the assistant gives, as plain text. “Run” opens a tab and runs the statement like one you typed: with the protection of the connection, in your history. The assistant learns whether you accepted, the rows affected, or the start of the error.

There is one dialog per proposal, “Run” is disabled for a moment so a click meant for something else does not land on it, and after five minutes a proposal no longer runs. Statements with invisible or direction-changing characters are refused: what you read is what runs.

Seeing what happens

The status line says “AI connected” while an assistant is. The dialog keeps a protocol of every call: tool, connection, statement, rows, outcome. It lives in memory only. “Disconnect” cuts off every assistant; switching the control off closes the port and removes the file with the token.

Data are not instructions

Text from a database (values, names, comments) can pretend to be an instruction to the assistant. The app runs nothing that stands in data, and its answers tell the assistant that values are data. Whether an assistant keeps to that is not up to the app. That is why everything that writes needs your click on a statement you can read yourself.

Errors

If a statement that names a hidden pattern fails, the assistant only learns that it failed: an error text can quote values.

SET ai = on

Set it up in four steps.

1. Menu Schema → AI control (MCP) …, switch it on.

2. Choose per connection what an assistant may do.

3. “Copy the command for Claude Code” and run it in a terminal, or copy the entry for the configuration file of another assistant.

4. “Test the connection” starts the helper the way an assistant would and asks the app through it.

The helper schema-commander-mcp ships beside the app. It speaks MCP over stdio, knows no database and writes nothing down.

# in a terminal
$ claude mcp add schema-commander -- \
    /path/to/schema-commander-mcp

# how the pieces talk
assistant ──stdio──▶ schema-commander-mcp
          ──127.0.0.1 + token──▶ the app
          ──▶ the tools, in the backend

The app listens only while the control is on, only on 127.0.0.1, on a random port. Port and token stand in a file only your user can read.

SELECT * FROM your_database;

Run it.

Free and open source under the MIT license. The packages are not signed yet.