Playbook

Playbook: Put Guardrails on Warehouse Queries

Your operations and finance teams finally have a way to ask the data warehouse questions without filing a ticket. They chat with an AI, it writes the query, ...

Your operations and finance teams finally have a way to ask the data warehouse questions without filing a ticket. They chat with an AI, it writes the query, and the answer comes back in seconds. Then someone asks it to "clean up the old test orders", and you realize the same tool that reads the warehouse can also change it. This playbook walks you through giving your team open access to read data while making sure nobody, and no agent, can delete rows, touch payroll tables, or pull a million rows by accident.

What you will build

By the end of this playbook, you will have:

  • A custom tool that runs queries against your data warehouse
  • A rule that blocks statements that change or delete data
  • A rule that keeps restricted schemas, such as payroll, off limits
  • A rule that stops oversized result sets before they run
  • An exception that lets your data engineering group run the statements everyone else cannot
  • A message for each rule that tells the AI what to do instead, so conversations keep moving
  • A tested rule list and a routine for reviewing what gets blocked

What you need before you start

  • An Assist workspace where you are a workspace admin.
  • An AI client connected to your workspace through an MCP server, or the chat in Assist.
  • Read access credentials for your data warehouse. Use a service account, not a personal login.
  • A short list of what should never happen: which statements, which schemas, how many rows is too many.
  • A group in your workspace for the people who are allowed to make changes, such as Data Engineering. Create it under Permissions on the Groups tab if it does not exist.

Step 1: Build the query tool

Start in your AI client. Describe the warehouse and what you want the tool to do:

"I want to build a tool called 'warehouse_query' that runs SQL against our data warehouse. It should take a 'sql' parameter with the statement, an optional 'schema' parameter, and a 'max_rows' parameter that defaults to 500. It should return the rows and the column names. The connection details are in the workspace secrets."

Let the AI ask questions. It will want to know how to handle errors, how long a query may run, and what to return when there are no rows. Answer these now, because the tool's parameters are what your rules will look at.

Then test it with a harmless query:

"Run warehouse_query with 'select count(*) from orders where created_at > current_date - 7'."

Confirm the number makes sense. Ask a few more questions the way your team would, so you can see the kinds of statements the AI writes. Pay attention to how it formats them. Queries often span several lines, and keywords may be uppercase or lowercase. Both matter when you write rules.

Step 2: Decide what should never happen

Before you open the rule editor, write the list down. A typical list for a warehouse:

  1. Nobody except data engineering may run a statement that changes data: insert, update, delete, merge, truncate, drop, alter, create, grant.
  2. Nobody may query the payroll or hr_private schemas through this tool.
  3. No query may ask for more than 10,000 rows.

Keep the list short. Each item becomes one rule, and a short list is easier to reason about than a long one.

Your warehouse credentials should already be read-only. Rules are a second layer. If the service account cannot write, the rule that blocks writes still earns its place: the AI gets a clear message instead of a database error, and the attempt is recorded where you can see it.

Step 3: Block statements that change data

In Assist, open Permissions and select the Tool Policies tab. Click New Rule.

Set up the rule:

  1. Under Action, select Deny.

  2. Under Applies to, leave Workspace.

  3. In Tool, type warehouse and press Tab. The name completes to warehouse_query and the tool pack fills in.

  4. Click Add condition.

  5. Click Path and press Tab. Choose sql.

  6. Set Operator to matches.

  7. In Pattern, enter:

    \b(insert|update|delete|merge|truncate|drop|alter|create|grant)\b
    
  8. Tick ignore case.

The \b on each side means the word must stand alone. Without it, a query that reads a column called updated_at would be blocked, because update appears inside it.

Fill in the two text fields:

  • Reason: "Only data engineering may change warehouse data."
  • Message returned to the model on deny: "This tool is read-only for you. Write a select statement instead, or ask the data engineering team to make the change."

Now test it. Under Test conditions, click Fill from tool, then set the sql value to a statement that should be blocked:

delete from orders where status = 'test'

Click Run test. The result should say the conditions match. Change the statement to select updated_at from orders and run it again. This time it should not match.

Leave Placement at Bottom of chain and click Create Rule.

Step 4: Add the exception for data engineering

The rule you just made applies to everyone. Your data engineers need to get through. Because the first matching rule decides, the exception goes above the block.

Click New Rule:

  1. Under Action, select Allow.
  2. Under Applies to, choose Group, then pick Data Engineering.
  3. In Tool, enter warehouse_query.
  4. Add no conditions.
  5. Reason: "Data engineering may run any statement."
  6. Under Placement, choose Top of chain.

Click Create Rule. Your list now reads, from the top: allow data engineering, then block changes for everyone.

Step 5: Keep restricted schemas off limits

This rule should apply to everyone, including data engineering, so it goes at the very top.

Click New Rule:

  1. Action: Deny.

  2. Applies to: Workspace.

  3. Tool: warehouse_query.

  4. Add a condition on sql with matches and this pattern, with ignore case ticked:

    \b(payroll|hr_private)\.
    
  5. Reason: "Payroll and HR schemas are not available through the warehouse tool."

  6. Message returned to the model on deny: "Payroll and HR data cannot be queried here. Tell the user to request it from the finance systems team."

  7. Placement: Top of chain.

The tool also takes a schema parameter, and a query could name the schema there instead of in the statement. Add a second rule for it:

  1. Action: Deny, Applies to: Workspace, Tool: warehouse_query.
  2. Add a condition on schema with in, and enter payroll, hr_private. Tick ignore case.
  3. Use the same reason and message.
  4. Placement: Top of chain.

This is the habit to build: for each thing you want to stop, ask every way the call could express it, and cover each one.

Step 6: Stop oversized results

Click New Rule:

  1. Action: Deny, Applies to: Workspace, Tool: warehouse_query.
  2. Add a condition. Choose the path max_rows. Because the parameter is a number, the operator list shows number comparisons. Choose gt and enter 10000.
  3. Reason: "Result sets over 10,000 rows are not allowed."
  4. Message returned to the model on deny: "Ask for at most 10,000 rows. Summarize with group by, or narrow the date range."
  5. Placement: after the schema rules.

Step 7: Test the whole list

Rules interact, so test them together. Select the Simulator tab and switch to Tool call.

Run these checks, changing User and Params JSON each time:

PersonStatementExpected
A member of operationsselect * from orders limit 10Allowed by the workspace default
A member of operationsdelete from ordersBlocked by the change rule
A data engineerdelete from ordersAllowed by the group exception
A data engineerselect * from payroll.salariesBlocked by the schema rule
Anyonemax_rows set to 50000Blocked by the size rule

For each one, read the Evaluation trace. It shows which rule decided and why the others were skipped. If a check gives the wrong answer, the trace tells you which rule to fix or move.

Step 8: Try it for real

Go back to your AI client and ask for something that should be blocked:

"Delete the test orders from last week."

The AI calls the tool, the rule blocks it, and the AI tells you it cannot make changes and suggests what to do instead. That is the message you wrote doing its job.

In Assist, open Tool History and select Blocked. The call is there. Click it to see which rule stopped it and the statement that was attempted.

Step 9: Review and adjust

For the first two weeks, check Tool History every few days with the Blocked filter on. You are looking for two things:

  • Calls that should have been allowed. A legitimate query blocked because a column name happened to match. Tighten the pattern.
  • Calls that reveal a gap. Someone tried something you had not thought of. Click Create policy rule from this call in Execution Details to start a rule from it.

On the Tool Policies tab, the Blocked 7d column shows how often each rule fires. A rule that never fires may be unnecessary, or it may be misspelled. A rule that fires constantly may be too broad.

What you built

Your team can ask the warehouse anything, and the answers come back in seconds. The statements that could damage data are blocked for everyone except the people whose job it is to make changes. Restricted schemas are off limits to everyone. Oversized queries are stopped before they run. Every blocked attempt is recorded with the rule that stopped it, and the AI explains what happened instead of failing.

You did this without changing the tool. When your list of restrictions changes, you edit a rule, and it applies to every chat, agent, and app that uses the tool.

Where to go next

  • Limit agents differently from people. Add a rule with Sources set to Subagent so that scheduled agents may only query a reporting schema.
  • Catch what patterns miss. Patterns cannot tell whether a query is dangerous in context. See Playbook: Build a query intent classifier.
  • Cover the other tools. If you have tools for your ERP or order system, give them the same treatment. A tool pack pattern covers every tool in a pack with one rule.
  • Build a review app. Ask your AI client to build a sandcastle app that lists blocked queries by person and by rule, so team leads can see what their people are trying to do and whether they need more access.