Read Database
Introduction
The Read Database tool runs ad-hoc SQL against your application database through a connection that is read-only at the database layer. Writes don't get blocked by the tool — they get rejected by the database itself, because the connection has no grants (MySQL and Postgres) or is opened in read-only mode (SQLite).
When enabled, the tool also injects your schema (tables, columns, foreign keys) into the agent's system prompt, so the agent can write joins without guessing column names.
It's disabled by default.
How Read-Only Is Enforced
The enforcement is at the connection level, not at the tool level. There is no SQL parsing or write-statement allowlist.
- MySQL and Postgres — the tool uses a separate database user whose only grant is
SELECTon your schema. Even if the agent emitsUPDATE, the DB rejects it with a permission error. - SQLite — the tool opens the same file in read-only mode (
file:/path?mode=ro) and re-assertsPRAGMA query_only = ONbefore every query, so writes through the main file or anyATTACH-ed database are blocked.
This means the trust boundary lives where you can audit it (SHOW GRANTS, the SQLite PRAGMA) rather than in PHP code the agent could in principle talk its way around.
Enabling Read Database
Run the wizard:
php artisan laraclaw:setup-read-database
It detects your default DB driver and:
- MySQL, MariaDB, Postgres — prints the
CREATE USER/GRANT SELECTSQL you'll need to run as a DB admin, then prompts for the read-only username and password and verifies the connection actually has read access. - SQLite — flips the env flag. No credentials needed; the same file is opened in read-only mode.
- Other drivers — disables the tool.
You can also set the env vars directly:
LARACLAW_READ_DATABASE_ENABLED=true
LARACLAW_READ_DATABASE_USERNAME=laraclaw_reader
LARACLAW_READ_DATABASE_PASSWORD=...
LARACLAW_READ_DATABASE_TIMEOUT_SECONDS=10
Running Queries
| Parameter | Required | Description |
|---|---|---|
query | Yes | A single SQL SELECT statement |
The result is returned as a JSON array of rows. A trailing semicolon is stripped. On failure, the response is {"error": "..."} so the agent can see what went wrong and try again.
Output Limits
Results are capped at 500 rows. If the cursor returns more, the response is wrapped:
{
"rows": [...],
"truncated": true,
"note": "Result exceeded 500 rows; add LIMIT or refine the query to see the rest."
}
The note nudges the agent toward adding a LIMIT clause rather than blindly retrying.
Query Timeouts
A per-query timeout is set on every call:
- MySQL and MariaDB —
SET SESSION MAX_EXECUTION_TIME - Postgres —
SET statement_timeout - SQLite — no equivalent; falls back to PHP's request time limit
The default is 10 seconds. Set LARACLAW_READ_DATABASE_TIMEOUT_SECONDS=0 to disable.
Schema in the Prompt
When the tool is enabled, the agent's system prompt is appended with a description of your schema:
- SQLite — every
CREATE TABLE/CREATE INDEXfromsqlite_master. - MySQL and MariaDB —
SHOW CREATE TABLEfor every table. - Postgres — column and foreign key metadata as JSON (there is no single
SHOW CREATE TABLEequivalent).
The snapshot is cached for one hour. After running migrations, clear the cache to refresh:
php artisan cache:forget laraclaw:read_database:schema
Security Considerations
!NOTE Read Database is the narrower alternative to Tinker. If all you need is "let the agent answer questions about my data", enable this and leave Tinker off — it has a much tighter blast radius.
- The agent can read everything the read-only user can read. Audit your grants. If
laraclaw_readercan see thepayment_methodstable, the agent can too. - Inbound messages can influence prompts. Anyone who can DM the bot can in principle convince the agent to run a query. Trust your trusted senders.
- Set
LARACLAW_LOG_AGENT_REQUESTS=trueto keep an audit trail of every query the agent has run. - Self-describing schemas help. A
users.last_login_atcolumn will be queried correctly far more often thanusers.lla.