← Back to blog
Use CasesSQL9 min read

August 14, 2026

Gate SQL writes so agents never hold DATABASE_URL

A developer using Cursor asked Claude to "clean up the test data" in production. Claude ran DELETE FROM users WHERE email LIKE '%+test%'. The predicate matched 1,847 production accounts. Production DATABASE_URL was in the MCP config because it was the only connection string on hand.

The model can recite that DELETE is dangerous. The root cause was architectural: the agent held DATABASE_URL. Nothing in the path was an evaluate.

Fix credential ownership

Default DB MCP: the host holds a connection string, and the model chooses SQL. Prompt text that says "this is production" is advisory. The driver still runs the statement.

A read-only URL works until someone needs an INSERT, a status update, or a migration. Then the choice becomes: hand over write credentials and hope, or remove the tool. Prefer a third path: the gate owns DATABASE_URL, classifies, evaluates sql.read / sql.write / sql.ddl, and executes only on allow.

Configure @limetry/sql

@limetry/sql (open source) owns DATABASE_URL. The agent never receives the raw connection string. It gets limetry_sql_query. On each statement the gate:

  1. Classifies with a conservative keyword heuristic — read, write, ddl, or unknown. Unknown maps to sql.write (over-gate rather than unguarded write).
  2. Evaluates an ActionIntent with action_type: "sql.read", "sql.write", or "sql.ddl".
  3. Decides: allow (execute and return results), deny (structured error), or approval_required (park until operator signs off).
{
  "mcpServers": {
    "limetry-sql": {
      "command": "npx",
      "args": ["-y", "@limetry/sql"],
      "env": {
        "DATABASE_URL": "postgresql://...",
        "LIMETRY_API_KEY": "...",
        "LIMETRY_POLICY_ID": "...",
        "LIMETRY_BASE_URL": "http://localhost:3810"
      }
    }
  }
}

The agent sees results from allowed reads and clear errors from denied writes. It never sees DATABASE_URL. Point LIMETRY_BASE_URL at your self-run Limetry server.

Keep classification conservative

This is a heuristic, not a full SQL parser. Dialects, extensions, and CTEs that wrap writes will outrun one. Map INSERT / UPDATE / DELETE / DROP / ALTER / TRUNCATE / CREATE / GRANT to write or DDL; unknown → sql.write; else read. Over-gating costs seconds. Under-gating deletes rows.

Park writes with approval_required

Reads can allow. A production DELETE should deny or park. Needed INSERT / bounded UPDATE can return approval_required: the agent gets a structured wait state; the operator uses limetry_sql_list_pending or the approval API, then approves or rejects. Shipped policies/postgres.json is read-only — add require_approval_action_types to enable parked writes.

Point at your SQL database

Set DATABASE_URL to any SQL connection string your team trusts. Same classify → evaluate → allow/deny/approval_required.

Run dry-run before enforcement

Dry-run is the default (LIMETRY_SQL_DRY_RUN on unless set to "false"). Classify and evaluate without executing. Inspect audit, confirm SELECT allows and DDL denies, then set LIMETRY_SQL_DRY_RUN=false. Removing the variable keeps dry-run; it does not enable execution.

Store privacy-safe SQL audit

Audit (minimal by default) keeps sql.read / sql.write / sql.ddl, decision, timestamp, and enough statement shape to review — not full SQL at rest. A 200-character sql_preview in intent metadata is truncation, not redaction. Answer "was this a denied write?" without turning audit into a replayable log of predicates and values.

Operational notes

With the gate, that cleanup classifies as sql.write, evaluate returns deny, and The database never sees it. Agents retry; without the gate, speculative writes hit prod. Do not put DATABASE_URL in agent-visible MCP env.

CI starts a SQL service and a Limetry node: SELECT must allow and execute; DROP and DELETE must deny before the driver.

Related: The DELETE that erased 1,847 accounts and Adding write access without giving away the keys.

Run Limetry on your own stack

Self-host the open source evaluation server, wire evaluate into your agents, and keep privacy-safe audit under your control.