Back to Blog

Multi-Tenant Databases and AI: Don't Forget org_id

AI SQL on a multi-tenant Postgres DB often drops the tenant filter. How to prompt for org_id/workspace_id, use views, and avoid cross-tenant counts in Claude or Cursor.

SaaS Postgres is usually multi-tenant: org_id, workspace_id, or account_id on the important rows.

Ask “how many orders last week?” without naming the org, and you may get every tenant summed. Valid SQL. Wrong answer. Sometimes a privacy issue.

Live access doesn’t fix it. Soft deletes and status enums are in metric traps; this is the tenant filter.

The failure mode

You meant: orders for org_id = 42.
The model ran: SELECT COUNT(*) FROM orders WHERE created_at > ….

Or it joined users across all orgs while you were debugging one customer ticket.

Support and admin-UI gaps make this worse — people paste one email and forget the rest of the tenant key.

Prompt rules that help

Put this above tenant-aware questions:

This database is multi-tenant.
Every query on tenant data MUST filter by org_id (or workspace_id —
confirm the column name from the schema).
If I don't give an org id, ask for it — don't count all tenants.
Show SQL every time.

Ticket-shaped ask:

org_id: 42
(or: resolve org from email <x> then filter everything by that org)
Count open subscriptions. Filter by org_id. Show SQL.

Exploration:

List tables that have org_id / workspace_id / account_id.
For orders, show an example COUNT grouped by org_id (top 5)
so we can see the grain — then stop.

Stronger than prompts: views

Create a view (or use staging data) that already encodes the tenant:

CREATE VIEW support_orders_org_42 AS
SELECT * FROM orders WHERE org_id = 42;

Point the allowlist at the view, not orders. Clumsy for many orgs; fine for a support break-glass or a demo tenant.

Better long-term: SELECT-only user that only has access to tenant-scoped views you control.

RLS caveat (Supabase especially)

Service role bypasses RLS. Connecting Claude with the service role means “see all tenants” unless you filter in SQL. Details: Connect Claude to Supabase.

If you need RLS to apply, you need a role and session setup that actually sets the tenant — not the bypass key.

Verify

  1. Known count for org A
  2. Ask AI for that count with org A in the prompt
  3. Ask again without org id — model should ask, not return the global total
  4. Read the SQL

Known-answer checks.

When global counts are OK

Internal analytics on purpose: “signups across all orgs this week.” Say all tenants in the prompt so it’s intentional. Don’t mix that thread with a single-customer support lookup.


Make the model ask for org_id, or don’t hand it the raw table.

Staging + allowlist: mcpserver.design (7-day Solo trial).

Related reading: