Overview
This skill covers Grille's SQL module — structured database access with connection-level RBAC, transaction support, and row-safety limits. Apply it whenever Claude needs to read from or write to databases, inspect schemas, or run multi-step mutations within a transaction.
Grille supports PostgreSQL and SQLite connections configured by name in grille.toml. Active connections: nyxis (SQLite), grille-demo (Postgres, AKV-backed password).
Tools
sql_query— Execute a SELECT statement. Returns rows up to the configuredmax_rowslimit (100–500 depending on connection). Read-only path — mutation statements are rejected.sql_execute— Execute INSERT, UPDATE, or DELETE. UPDATE and DELETE trigger a Grille Safety Check automatically (see below). Rejected onread_onlyconnections.sql_explain— Show the query execution plan for a SELECT statement. Pass the SELECT only — Grille adds EXPLAIN. Setanalyze=true(PostgreSQL only) for real timing and row counts via EXPLAIN ANALYZE. SQLite uses EXPLAIN QUERY PLAN automatically.sql_begin— Open a transaction session. Returns asession_idfor subsequent calls.sql_commit— Commit and close an open transaction session.sql_rollback— Roll back and close an open transaction session.sql_list_tables— List all tables with row counts.sql_describe— Describe a table's schema: columns, types, nullable, defaults, primary key.
Grille Safety Check — UPDATE and DELETE
When sql_execute receives an UPDATE or DELETE, Grille automatically runs a
SELECT COUNT(*) with the same WHERE conditions before executing. The result is
returned as a Grille Safety Check block. The write does not execute until the
user explicitly authorizes it.
Flow:
- Call
sql_executewith the UPDATE or DELETE statement (noauthorizedparameter) - Grille returns the Safety Check block showing the affected row count
- Present the count to the user: "This will modify/delete N rows. Do you want to allow this?"
- If the user authorizes: call
sql_executeagain with the identical statement andauthorized=true - If the user declines: do not proceed
Zero-row writes are blocked outright — Grille will not execute a write that affects no rows. This is always a sign of a wrong WHERE clause, not a valid operation.
authorized=true without a prior Safety Check is rejected by Grille. Do not pass
authorized=true on the first call — it will be refused.
Example Safety Check flow:
# First call — triggers Safety Check
grille:sql_execute
connection="rekn"
statement="UPDATE orders SET status = 'archived' WHERE created_at < '2024-01-01'"
# Grille returns:
# ━━━ Grille Safety Check ━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Operation: UPDATE
# Table: orders
# Connection: rekn
# Rows affected: 4,847
#
# This operation will modify 4,847 rows and cannot be undone.
# Authorization is required before Grille will execute this statement.
#
# To authorize: call sql_execute again with authorized=true
# To cancel: do not proceed
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# → Present to user: "This will modify 4,847 rows. Do you want to allow this?"
# → User says yes:
# Second call — authorized
grille:sql_execute
connection="rekn"
statement="UPDATE orders SET status = 'archived' WHERE created_at < '2024-01-01'"
authorized=true
Patterns
Read-only inspection:
grille:sql_query
connection="nyxis"
query="SELECT id, name, created_at FROM items ORDER BY created_at DESC LIMIT 20"
Schema discovery before writing:
grille:sql_list_tables connection="nyxis"
grille:sql_describe connection="nyxis" table="items"
→ Then construct queries against real column names
Multi-step mutation with transaction:
1. grille:sql_begin connection="rekn"
→ returns session_id = "abc-123"
2. grille:sql_execute
connection="rekn"
session="abc-123"
statement="UPDATE orders SET status = 'shipped' WHERE id = 42"
→ Grille Safety Check returned (1 row)
→ User authorizes
3. grille:sql_execute
connection="rekn"
session="abc-123"
statement="UPDATE orders SET status = 'shipped' WHERE id = 42"
authorized=true
4. grille:sql_execute
connection="rekn"
session="abc-123"
statement="INSERT INTO shipment_log (order_id, ts) VALUES (42, NOW())"
→ INSERT has no Safety Check — executes immediately
5. grille:sql_query
connection="rekn"
session="abc-123"
query="SELECT status FROM orders WHERE id = 42"
→ Verify before committing
6a. grille:sql_commit session="abc-123"
— OR —
6b. grille:sql_rollback session="abc-123" ← if verification failed
Constraints
- Do not use
sql_executefor SELECT statements.sql_executeis for mutations only. Usesql_queryfor reads. The tools enforce this.
⛔ sql_execute rejects SELECT. STOP — use sql_query for reads.
- Always show the Safety Check result to the user before passing
authorized=true. The Safety Check exists for the user's benefit — presenting the row count and asking for consent is required, not optional. Never passauthorized=truewithout user confirmation.
⛔ Never pass authorized=true without showing the user the row count and receiving explicit consent.
- Do not run multi-step mutations without a transaction. Without
sql_begin/sql_commit, eachsql_executeautocommits. A failure midway leaves the database partially mutated. Always wrap multi-step writes in a transaction.
⛔ Multi-step mutations without a transaction leave the DB partially mutated on failure. STOP — open a transaction with sql_begin first.
- Always call
sql_rollbackif any step in a transaction fails. Leaving an open transaction session consumes a database connection and may block other operations. - Do not attempt writes on
read_onlyconnections. Connections markedread_only = truerejectsql_executeat the Grille Security layer before reaching the database. - New SQL connections require a full Claude Desktop restart.
grille_reload_configdoes not register new connection pool entries. - Row results are capped at
max_rows. Default 100–500 per connection. AddWHEREandLIMITclauses for large tables.
Examples
Example 1: Find recent errors in application logs
grille:sql_describe connection="nyxis" table="app_logs"
→ Confirm column names
grille:sql_query
connection="nyxis"
query="SELECT ts, level, message FROM app_logs WHERE level = 'ERROR' ORDER BY ts DESC LIMIT 50"
Example 2: Authorized bulk update with Safety Check
# Step 1 — trigger Safety Check
grille:sql_execute
connection="rekn"
statement="UPDATE users SET display_name = email WHERE display_name IS NULL"
# Grille returns: "Rows affected: 142 — authorization required"
# Present to user: "This will update 142 rows. Authorize?"
# User: "Yes"
# Step 2 — authorized execution
grille:sql_execute
connection="rekn"
statement="UPDATE users SET display_name = email WHERE display_name IS NULL"
authorized=true
Overview
This skill covers Grille's SQL module — structured database access with connection-level RBAC, transaction support, and row-safety limits. Apply it whenever Claude needs to read from or write to databases, inspect schemas, or run multi-step mutations within a transaction.
Grille supports PostgreSQL and SQLite connections configured by name in grille.toml. Active connections: nyxis (SQLite), grille-demo (Postgres, AKV-backed password).
Tools
sql_query— Execute a SELECT statement. Returns rows up to the configuredmax_rowslimit (100–500 depending on connection). Read-only path — mutation statements are rejected.sql_execute— Execute INSERT, UPDATE, or DELETE. Returns affected row count. Rejected onread_onlyconnections.sql_begin— Open a transaction session. Returns asession_idfor subsequent calls.sql_commit— Commit and close an open transaction session.sql_rollback— Roll back and close an open transaction session.sql_list_tables— List all tables with row counts.sql_describe— Describe a table's schema: columns, types, nullable, defaults, primary key.
Patterns
Read-only inspection:
grille:sql_query
connection="nyxis"
query="SELECT id, name, created_at FROM items ORDER BY created_at DESC LIMIT 20"
Schema discovery before writing:
grille:sql_list_tables connection="nyxis"
grille:sql_describe connection="nyxis" table="items"
→ Then construct queries against real column names
Multi-step mutation with transaction:
1. grille:sql_begin connection="rekn"
→ returns session_id = "abc-123"
2. grille:sql_execute
connection="rekn"
session_id="abc-123"
query="UPDATE orders SET status = 'shipped' WHERE id = 42"
3. grille:sql_execute
connection="rekn"
session_id="abc-123"
query="INSERT INTO shipment_log (order_id, ts) VALUES (42, NOW())"
4. grille:sql_query
connection="rekn"
session_id="abc-123"
query="SELECT status FROM orders WHERE id = 42"
→ Verify before committing
5a. grille:sql_commit session_id="abc-123"
— OR —
5b. grille:sql_rollback session_id="abc-123" ← if verification failed
Diagnose a slow query:
# Step 1: check the plan (no execution)
grille:sql_explain
connection="nyxis"
query="SELECT * FROM network_samples WHERE timestamp > 1700000000 ORDER BY timestamp DESC"
→ SEARCH network_samples USING INDEX idx_net_ts ← index hit, fast
# Step 2: if no index, check real timing on Postgres (actually executes)
grille:sql_explain
connection="rekn"
query="SELECT * FROM orders WHERE customer_id = 42"
analyze=true
→ Shows real row counts, actual time, buffer hits
Constraints
- Do not use
sql_executefor SELECT statements.sql_executeis for mutations only. Usesql_queryfor reads. The tools enforce this —sql_queryrejects mutation statements and vice versa.
⛔ sql_execute rejects SELECT. STOP — use sql_query for reads.
- Do not run multi-step mutations without a transaction. Without
sql_begin/sql_commit, eachsql_executeautocommits. A failure midway leaves the database in a partially-mutated state. Always wrap multi-step writes in a transaction.
⛔ Multi-step mutations without a transaction leave the DB partially mutated on failure. STOP — open a transaction with sql_begin first.
- Always call
sql_rollbackif any step in a transaction fails. Leaving an open transaction session consumes a database connection and may block other operations. A try/catch mental model: on any error, roll back before reporting the failure. - Do not attempt writes on
read_onlyconnections. Connections markedread_only = trueingrille.tomlrejectsql_executeat the Grille layer before reaching the database. Checksql_list_tablesoutput for the connection type. - Do not add a new SQL connection in
grille.tomland expectgrille_reload_configto register it. New SQL connections require a full Claude Desktop restart.grille_reload_confighot-swaps existing config values but does not register new connection pool entries. - Row results are capped at
max_rows. Default is 100–500 per connection. AddWHEREandLIMITclauses to stay within the cap for large tables. Results exceeding the cap are truncated — you will not receive an error, but you may receive incomplete data.
Examples
Example 1: Find recent errors in application logs
grille:sql_describe connection="nyxis" table="app_logs"
→ Confirm column names
grille:sql_query
connection="nyxis"
query="SELECT ts, level, message FROM app_logs WHERE level = 'ERROR' ORDER BY ts DESC LIMIT 50"
Example 2: Safely backfill a column
grille:sql_begin connection="rekn"
→ session_id = "tx-001"
grille:sql_execute
connection="rekn"
session_id="tx-001"
query="UPDATE users SET display_name = email WHERE display_name IS NULL"
grille:sql_query
connection="rekn"
session_id="tx-001"
query="SELECT count(*) FROM users WHERE display_name IS NULL"
→ Expect 0 rows — if not 0, rollback
grille:sql_commit session_id="tx-001"