GC-301f · Module 2
SQL Databases
3 min read
SQL database tools for Gemini CLI connect through standard client libraries — pg for PostgreSQL, mysql2 for MySQL, better-sqlite3 for SQLite. The connection string comes from an environment variable. The tool opens a connection pool at server startup and reuses it across tool calls. Never open a new connection per query. Pool configuration matters: set a maximum of 5 connections for MCP server tools. These are not web servers handling hundreds of concurrent users — they serve one AI agent making sequential queries.
Parameterized queries are non-negotiable. Every value that comes from Gemini — table names excluded — goes through parameter binding. The query template uses $1, $2 placeholders (PostgreSQL) or ? placeholders (MySQL), and the values array passes separately. This is not optional security hygiene. Gemini generates tool inputs from natural language. A user saying "find orders where name is Robert; DROP TABLE orders" will produce exactly that string as a parameter. Parameterization neutralizes it.
import pg from "pg";
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
max: 5,
idleTimeoutMillis: 30000,
});
async function queryOrders(status: string, limit: number = 20) {
const result = await pool.query(
`SELECT id, customer_name, total, created_at
FROM orders
WHERE status = $1
ORDER BY created_at DESC
LIMIT $2`,
[status, Math.min(limit, 100)]
);
return {
count: result.rowCount,
orders: result.rows,
};
}
Read-only access patterns protect production data. Create a dedicated database user for the MCP server with SELECT-only permissions. Revoke INSERT, UPDATE, DELETE, and DDL privileges. If a tool needs write access — creating a record, updating a status — use a separate connection with a scoped user that can only modify specific tables. The principle: minimum privilege per tool. A reporting tool should never have the ability to delete data.