# What happens between your AI chat and your Fishbowl database

> When an AI assistant asks your Fishbowl a question through ILC MCP, the request passes six checks and then runs under row, time and size limits. Here's each one, what it stops, and what you control.

The first question I get from a Fishbowl admin about AI is some version of "what can it actually do to my database?" It's the right question. Fishbowl is the system the warehouse is picking from right now, and nobody wants a chatbot near it without knowing the rules.

So here is what happens when someone asks their AI assistant a question and it runs a query through ILC MCP. Our goal was as much protection as we could add without making it harder to use or less useful. We added each layer as we built ILC MCP, from colleague feedback, industry standards, and testing against AI models built to get in when we want them out.

<figure>
  <video data-play-once controls muted playsinline preload="metadata" src="/images/mcp-security/query-checks.mp4" poster="/images/mcp-security/query-checks.jpg" aria-label="One query pipeline, four runs. A normal question passes six checks and returns five rows. A user deactivated in Fishbowl is stopped at the fourth check. A query that tries to change data is stopped at the fifth check, with a note that changes go through the Fishbowl API with a preview you confirm. A heavy query passes the checks and the database stops it at 30 seconds while Fishbowl keeps running."></video>
  <figcaption>One query, four ways it can go. The SQL and rows in the first run are from Fishbowl's demo data.</figcaption>
</figure>

## The example

Someone types "Which ingredients are we lowest on?" The AI writes a query against Fishbowl's tables, something like this:

```sql
SELECT p.num, SUM(q.qtyOnHand) AS onHand
FROM part p
JOIN qtyinventorytotals q ON q.partId = p.id
GROUP BY p.id
HAVING onHand > 0
ORDER BY onHand
LIMIT 5
```

Before that touches the database, it goes through six checks, in this order. The cheap ones run first, and once we know who's asking, every refusal is logged.

## 1. Signed in

Every request carries a token tied to a person. We store only a one-way hash of it, never the token itself, so even our own records can't be used to sign in as you.

Most people connect through their AI app's normal sign-in screen. That gives the app a short-lived token it refreshes in the background. If you revoke a connection from the admin console, that assistant stops working on its next request.

## 2. Allowed network

Next, where the request is coming from. We check the address the request actually arrived from, not anything the caller claims about itself. If no rule allows it, it's refused.

You choose how to allow access: by AI platform (we match the provider's published network ranges or connector certificate), by your own office or VPN ranges, or by letting people register their own address when they sign in, with your approval if you want it. A refused user sees "This network is not allowed to use this MCP server" and knows who to ask.

## 3. Allowed to use this tool

Each tool needs a permission. Reading and writing are separate permissions, and some tools are for admins only. The AI only sees the tools the person is allowed to use, so a read-only user's assistant doesn't even know the order tools exist.

## 4. Fishbowl user still active

This is the one admins like most. On every call, we ask Fishbowl whether the person behind the token is still an active Fishbowl user. Not when the token was made: every call.

So when someone leaves and you deactivate them in Fishbowl, the way you already do, their AI access stops on the next request, even if their tokens haven't expired. You can still revoke their tokens in the admin console as well, but nothing depends on remembering to. If Fishbowl can't be reached to answer, the call is refused.

## 5. One SELECT only

Now the query itself. We parse it and require exactly one SELECT statement. Anything that writes, or a second statement tacked on the end, is refused. Then we rebuild the query from the parsed version and run that, so comments and quoting tricks don't get carried along.

Some data can never be read this way: login credentials, payment details and stored secrets are blocked no matter how the query is written. On top of that you can switch on rules like "suppress personal information," which refuses `SELECT *` on tables with personal fields and asks for named columns instead. Functions that pause, lock, read files or describe the server are blocked too.

When a query is refused for one of these reasons, the AI is told not to try another way to the same data.

## 6. Read-only database user

The query then runs as a MySQL user that can only read. That's enforced by MySQL itself, as a second line under check 5.

Which database user it runs as is decided on our side, from the person, then their group, then a default. Nobody can pick their own. That's also where you can narrow things down: give the Sales group a database user that can't see the financial tables, and everyone in Sales gets it automatically.

## Then the limits

A query can pass every check and still be a bad idea on a production system. So it runs under limits:

- **Rows:** 100 by default. The AI can ask for up to 1,000. Past that, the results are cut off and the AI is told there's more, rather than getting an error.
- **Time:** 30 seconds. A query still running at 30 seconds is stopped by the database server, not just abandoned by our side. We tested this on the demo database with a deliberately slow query, and the server killed it at about 30 seconds.
- **Size:** a statement can be up to 20,000 characters, and you can set a cap on how much data a single answer returns.

You can set tighter limits per person. Exports to a spreadsheet are separate. They're meant to be big, so they get a longer time limit and go to a file instead of into the chat.

## What about changing data?

None of the above can write. Changes, like creating a sales order or importing parts, don't use SQL at all. They go through Fishbowl's own API as the signed-in person, in two steps: the AI prepares the change and shows a preview, and nothing happens until the person confirms it. A preview works once and expires after 30 minutes, and the person's permissions are checked again when they confirm.

## The rest of the plumbing

- Every call is logged: who, which tool, the result and how long it took. Tokens, passwords and query results are not logged. Queries get their own log with what was asked and the SQL that ran.
- Download links for exports last about five minutes, and the files are deleted afterward.
- Each customer runs as its own deployment, with its own storage.

## Why we are sharing this

The internet is a scary place, and connecting an AI to the database that runs your business should make you ask questions. A lot of products answer them with "enterprise secure" and stop there. That doesn't tell you what's checked, what gets refused, or what you control.

You should be able to know exactly how a system like this works before you connect it to your Fishbowl. This is ours.

---

*From Israel Lopez Consulting (ILC). Custom Fishbowl Inventory integrations and software, and Fishbowl experts since 2015. Written by Israel Lopez, published October 27, 2026. Questions about this topic, or want to reach the people who wrote it? Visit https://israellopezconsulting.com/contact*
