Implementation Guide

    Blocking DROP, TRUNCATE, and Unscoped DELETE Queries from the Postgres MCP Server

    Treat Postgres roles and privileges as the baseline, then add statement-level enforcement on every MCP tool call. Parse each SQL payload into a syntax tree, block DROP and TRUNCATE, block any DELETE without a real search condition, and record identity, statement, matched policy, decision and timestamp for each call. Route legitimate but risky operations to human approval instead of allowing them silently.

    How destructive SQL reaches Postgres through an MCP server

    A Postgres MCP server exposes database access to an agent as one or more tools. The agent decides what SQL to send, and the server executes it using whatever credentials it was configured with. If those credentials are broad, the agent inherits that breadth. Nothing in the path asks whether this particular statement was something the agent was meant to run.

    Three statement shapes matter most. DROP removes objects such as tables and schemas. TRUNCATE empties a table. DELETE is the subtle case: it is a routine operation, but in standard SQL a searched DELETE with no search condition deletes all rows of the target table. The verb is the same whether the statement removes one row or every row, so a policy has to treat the missing WHERE clause as its own condition.

    Agent-generated SQL also differs from human-written SQL in a practical way. A person types a statement deliberately. An agent can produce one as a side effect of a vague instruction, a misread schema, or text injected into its context. That is why controls should assume arbitrary SQL can arrive, not only the queries the integration was designed around.

    Comparing the enforcement layers

    No single layer covers every case. The table summarizes what each one is good at and where it stops. Check each row against your own MCP server and database configuration before relying on it.

    • Postgres roles and privileges: Strong baseline. Grants decide which roles may delete from or alter which objects, and a least-privilege role removes whole classes of statements. Limits: privileges attach to roles and objects, not to the shape of a statement, and a shared broad credential defeats the model.
    • MCP server configuration: Read-only modes or restricted tools, where the server offers them, are cheap to apply. Limits: behavior varies by implementation, and a mode that only changes the session setting is a different guarantee from one that inspects statements.
    • SQL parsing at the tool call: Inspects the statement itself, so it can distinguish DELETE with a condition from DELETE without one. Limits: it is only as good as the parser and the rules written on top of it.
    • Runtime policy in the execution path: Evaluates every tool call as it happens, applies a decision, and records it. Limits: it adds a component to operate and must be tested like any control.

    Why parsing beats keyword matching

    A regex that looks for DROP, TRUNCATE or a DELETE without WHERE is the first thing most teams try, and it fails in predictable ways. Matching on keywords flags harmless text, such as a string literal containing the word DROP, and misses statements that are written differently from what the pattern expects.

    A policy that relies on pattern matching has to survive at least these cases:

    Multi-statement payloads. A single tool-call argument can carry several statements separated by semicolons. A harmless SELECT followed by a TRUNCATE passes a check that only reads the first statement. The policy must evaluate every statement and block the whole call if any one is denied.

    Comments and formatting. Comments, unusual whitespace and mixed case can hide a keyword from a pattern or split it. A parser discards these before the rules run.

    CTE-wrapped deletes. SQL allows data-modifying statements inside a WITH clause, so a DELETE can sit beneath a statement that begins with WITH or SELECT. Only a syntax tree exposes the nested node.

    Trivially true predicates. A DELETE carrying WHERE 1=1 or WHERE true has a search condition in the syntactic sense but removes every row. Treat constant-true predicates as unscoped. For predicates too complex to evaluate with confidence, block or escalate rather than allow.

    A parser also fails closed more naturally. If a payload cannot be parsed, the policy can deny it, instead of letting an unrecognized statement through.

    Defining the policy

    Write the policy as rules over attributes of each request. NIST describes attribute-based access control as evaluating rules against the attributes of the subject, the object, the operation and the environment. That maps neatly onto SQL: the caller's identity, the target schema or table, the statement type, and the environment such as production or staging. This is a way to organize the rules, not a Postgres-specific procedure.

    • Parse every statement in every tool call into a syntax tree; deny the call if parsing fails.
    • Block DROP statements by default in every environment, with exceptions defined per identity and per object.
    • Block TRUNCATE by default on the same basis.
    • Block DELETE when the tree has no search condition, or when the condition is constant-true.
    • Escalate scoped DELETEs against designated sensitive tables for human approval, with a named approver group.
    • Apply the rules to nested statements, including those inside CTEs, and to every statement in a multi-statement payload.

    Verifying the policy and the evidence it should produce

    Verification starts with a test suite that runs against the enforcement layer, not only against the database. Include a DELETE with no condition, DROP and TRUNCATE on a test table, a multi-statement payload that hides a destructive statement behind a SELECT, a CTE-wrapped delete, and a DELETE with an always-true predicate. Each should be blocked or escalated. Add allowed cases too, such as a scoped DELETE on a non-sensitive table, so the policy is not simply denying everything. Re-run the suite whenever rules, parsers or server versions change, and keep the results.

    Each decision should also leave a record. A practical minimum is the identity of the caller, the statement as received, the policy rule matched, the decision (allowed, blocked or escalated), and a timestamp. For escalations, add the approver and the outcome. This is recommended practice rather than a published requirement, but it answers the question an auditor is likely to ask: what was this agent allowed to do, and what did it try to do?

    Recording allowed calls matters as much as blocked ones. A log that only shows denials cannot demonstrate that the policy was evaluated on every call.

    Where runtime policy enforcement fits

    Native database controls and statement-level policy answer different questions. Privileges determine what a role may do. Runtime policy determines whether this specific tool call, from this identity, should proceed. NIST describes access decisions as least-privilege, per-request and context-based, and evaluating each MCP tool call individually follows that principle.

    Trussed is one way to enforce policy on MCP tool calls at runtime. It sits in the execution path across models, agents, tools and MCP servers, and applies a decision to each tool call: allow, redact, block, rate limit, or escalate for human approval. Policy is enforced at runtime, not reviewed after the fact. Each decision generates audit-ready evidence, and Trussed can be deployed self-managed in the customer cloud with configurable retention and data residency, which matters to a regulated enterprise that must show what its AI was allowed to do.

    The layering still applies. Keep least-privilege roles and any read-only configuration in place, and use runtime policy for the statements those controls cannot distinguish.

    Where a destructive statement can be stopped

    Agent generates SQL

    The model composes a statement the agent was never meant to run.

    MCP tool call

    The statement arrives as a tool-call argument, before it reaches the database.

    Policy decision

    Each call is parsed and evaluated: allow, block, or escalate.

    Postgres

    Roles and privileges remain the last line of defense.

    Enforce statement-level policy on MCP tool calls

    See how runtime enforcement and audit-ready evidence apply to agent-generated SQL in your environment.

    Request a Demo
    Next step

    How does your AI governance program compare?

    See where your program has gaps in less than 2 minutes.