Use case

Guarding database agents before their SQL runs

Agents that write SQL can answer questions from your data in seconds, and can also drop a table in the same amount of time. A pre-execution guard checks each statement against the user’s request and the environment before it reaches the database.

2 min readLast updated

Checks run on our GPUs in Switzerland. Request payloads are not stored.Security

In 30 seconds

  • The risky statements are rare but costly: DROP, TRUNCATE, DELETE or UPDATE without a WHERE clause, bulk exports.
  • Send the statement, the user’s question and the target environment to the guard.
  • Allow reads that match the question; ask or block writes to production and large exports.
  • Keep read-only roles, replicas and backups; the guard does not replace them.

Code and dense tables are folded away. Open any of them on demand.

Example check

A user asked for last month’s cancelled orders; the agent proposed a DELETE on production.

POST https://api.mcp-guard.ai/v1/guardjson
{
  "action": {
    "tool": "sql.execute",
    "args": {
      "database": "prod",
      "sql": "DELETE FROM orders WHERE status = 'cancelled'"
    }
  },
  "intent": "How many orders were cancelled last month?",
  "trigger": "user_request",
  "constraints": [
    "Read-only on prod unless the user explicitly asks for a change"
  ],
  "context": "Analytics agent for acme.test; connected to the production Postgres cluster"
}
Show it as a curl command· shell
Terminalbash
curl https://api.mcp-guard.ai/v1/guard \
  -H "Authorization: Bearer $MCP_GUARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action":{"tool":"sql.execute","args":{"database":"prod","sql":"DELETE FROM orders WHERE status = '\''cancelled'\''"}},"intent":"How many orders were cancelled last month?","trigger":"user_request","constraints":["Read-only on prod unless the user explicitly asks for a change"],"context":"Analytics agent for acme.test; connected to the production Postgres cluster"}'

What can go wrong with a database agent

A text-to-SQL or data-ops agent turns a request into statements and runs them. The failure modes are well known:

  • DROP TABLE or TRUNCATE while "cleaning up" a schema.
  • DELETE FROM orders or UPDATE users SET plan = 'free' with a missing or too-broad WHERE clause.
  • The right statement against the wrong database: production instead of staging.
  • SELECT email, phone FROM customers with no limit, followed by a tool call that writes the result to a file or sends it somewhere (data exfiltration).
  • Statements proposed after reading a cell or a comment that contained instructions (indirect prompt injection).

What to send the guard

Check each statement before execution:

  • action: { tool: "sql.execute", args: { database: "prod", sql: "DELETE FROM orders" } }.
  • intent: the user’s question or task.
  • constraints: rules such as "read-only on prod" or "never export more than 100 rows of customer data".
  • context: the environment, the role the agent connects with and what the tables contain.
  • trigger: tool_result when the statement was proposed after reading query results.

Including the environment in context matters: the same TRUNCATE is routine on a scratch schema and serious on production.

Acting on the verdict

By default the server returns block when P(unsafe) is at least 0.8 or the approval_policy head says reject, ask when P(unsafe) is at least 0.3, the policy head says require_human, or args_grounded is below 0.5, and allow otherwise. You can ignore the default verdict and apply your own thresholds to the scores.

For database agents the useful scores are destructive (data removed or overwritten), blast_radius (read-only, local or reversible write, or production-mutating), exfiltration (personal data leaving where it should), scope_violation via the violation head (acting outside the stated task) and args_grounded (a table name or id the user never mentioned). A common policy: auto-run reads that match the question, ask a human before any production write, block schema drops outright.

Controls to keep alongside the guard

Database permissions are the strongest control you have, and they are deterministic:

  • Connect the agent with a read-only role by default; grant write roles per task.
  • Point analytical questions at a read replica.
  • Keep point-in-time recovery and tested backups.
  • Enforce a statement timeout and row limits at the connection level.

The guard reduces risk in the gap between what the role allows and what the user actually asked for. It does not guarantee a bad statement is caught, so the recovery path still matters. See blast radius and reversibility.

Frequently asked questions

Should I send query results to the guard?
Not whole result sets. Send the statement and a short description of the data. If the next action uses results (for example emailing them), describe that in the check for the next action.
Can the guard parse SQL exactly?
It is a model that judges the action in context, not a SQL parser. Use a parser or database permissions for rules that must hold every time, such as "no DROP on prod".

Sources

Last updated . MCP Guard reduces the risk of harmful agent actions; it does not guarantee safety. Keep deterministic controls in place alongside it. Third-party names are used only to describe their products; we are not affiliated with them.