Skip to content

CONCEPT Cited by 1 source

Destructive-query protection

Definition

Destructive-query protection is a server-side static check on a SQL string that blocks structurally-dangerous statements before execution, applied at a tool / proxy boundary in front of a database. The two canonical rules:

  • UPDATE or DELETE without a WHERE clause — blocked. (Rewrites every row; almost never intentional.)
  • TRUNCATE — blocked outright. (Empties the table; irreversible without a backup.)

Named and canonicalised in PlanetScale's MCP server launch (2026-01-29):

"UPDATE or DELETE statements without a WHERE clause are blocked, and TRUNCATE is not allowed."

Why it exists

LLMs occasionally emit exactly the dangerous shape the rule catches — DELETE FROM users; instead of DELETE FROM users WHERE id = 42; — for reasons ranging from token-level off-by-one errors to misunderstood natural-language requests ("delete that user" → no user-identifier parsed). On a production database, the consequences are catastrophic and typically irrecoverable without restoring a backup.

Destructive-query protection bounds the blast radius of an adversarial or buggy agent:

  • The worst a misfiring execute_write_query can do is row-granular (e.g. DELETE FROM users WHERE id = 1 still works — and is sometimes wrong, but the damage is bounded to one row).
  • Table-wide damage requires explicit WHERE that evaluates to true for many rows (e.g. WHERE 1=1, WHERE id IS NOT NULL). Agents that deliberately write those patterns are harder to misuse accidentally; the shape of the query now has to carry explicit intent.
  • TRUNCATE bypass has no common legitimate form the agent would structure — so an outright block removes the primitive entirely without meaningful UX cost for normal workloads.

Relationship to MySQL sql_safe_updates

The idea is not new. MySQL's sql_safe_updates session variable has shipped since MySQL 3.22 (1998), raising ERROR 1175 on UPDATE/DELETE without a WHERE or a LIMIT. The mysql client sets it with --safe-updates. Rails' destroy_all emits an application-level warning for the same reason.

What's new with the MCP-server framing is that:

  • The check is applied at the tool / proxy boundary, not as a session variable the client could un-set.
  • It's framed as an agent-safety guardrail rather than a human-ergonomics feature.
  • It composes with other MCP-specific safeguards: ephemeral credentials, query tagging, and DDL elicitation.

What it does not protect against

Destructive-query protection is a narrow, shape-based rule. It does not protect against:

  • DELETE FROM users WHERE id IS NOT NULL; — has a WHERE, still wipes the table.
  • UPDATE accounts SET balance = 0 WHERE id = <victim>; — precision strike, fully within the rule.
  • DROP TABLE users; — a DDL statement, caught by the separate DDL human-confirmation gate, not by this check.
  • SQL injection via dynamic query construction — the rule operates on the final SQL, which already has the injected predicate.
  • Resource exhaustion — SELECT * FROM huge_table is not destructive in the "data-loss" sense but can still cripple a database.

The shape is the minimum-viable guardrail that correlates with common accidental-destruction patterns. Fuller safety requires layered defences: least privilege on the credential, auditing via query tagging, human review of the query before it runs, and (for DDL) explicit confirmation gates.

Where it fits in the LLM-safety stack

In the hosted-MCP safeguard bundle, the layers roughly stack as:

  1. OAuth permission scope — what tools the agent can call at all.
  2. Tool-surface minimization — read-only agents don't see execute_write_query.
  3. Destructive-query protection (this concept) — structural block on UPDATE/DELETE without WHERE, TRUNCATE.
  4. DDL elicitation — human confirmation for CREATE / DROP / ALTER.
  5. Ephemeral credentials — minimise lifetime of whatever credential ended up used.
  6. Query tagging — ensure every MCP query is attributable in Insights post-hoc.

Destructive-query protection is the third layer — cheap, structural, no UX cost for normal queries, narrow coverage.

Seen in

Last updated · 378 distilled / 1,213 read