An admin sits in the Velocity X dashboard and asks Brain: "Show me the top 5 reps by deals closed last month." Brain doesn't return canned advice — it reads the question, translates it into a live Postgres query, executes it (respecting RLS), and renders a table with the answer. No dropdown filters, no "advanced search" modal, no switching tools. One question. One result. 2 seconds.
Why Filter UIs Suck
Every CRM and business tool ships with a filter panel: date range, status, owner, territory, deal stage — check a few boxes, click "Apply". For the hundredth time this week. Natural language flips that: you ask a real question in plain English and the system figures out the WHERE clause. "Which deals have been stuck in Proposal for over 45 days?" becomes a Postgres query with `stage = 'Proposal' AND last_update < NOW() - INTERVAL '45 days'`. No clicking. No "did I click the right checkbox?" doubt. Brain reads intent, not checkboxes.
The Architecture: Claude + Schema Context + RLS Execution
Brain's NL-to-SQL pipeline has three layers. First, Claude receives your question plus a constrained schema snapshot (table names, column types, descriptions) baked into the system prompt. Second, Claude generates a Postgres query it thinks answers your question. Third, we execute that query with RLS enabled — the Postgres session runs as the logged-in user, so deals a rep can't see are invisible to the query, even if Claude wrote a query that nominally asks for them. Results come back. Brain formats them as a table (or chart, or raw JSON). All read-only by default; writes need explicit confirmation.
{`// System prompt excerpt fed to Claude
const schemaContext = \`
You are a SQL expert. Given a user's question, generate a single valid
PostgreSQL query that answers it. Use ONLY these tables and columns:
TABLE: deals (id, owner_id, stage, value, created_at, last_activity)
TABLE: users (id, name, email, region)
TABLE: activities (id, deal_id, user_id, type, created_at)
Respond with ONLY the SELECT statement. No explanation. No comments.
\`;
// User asks:
// "Show me the top 5 reps by deals closed last month"
// Claude outputs:
// SELECT u.name, COUNT(d.id) as closed_count, SUM(d.value) as total_value
// FROM deals d
// JOIN users u ON d.owner_id = u.id
// WHERE d.stage = 'Closed' AND d.last_activity > NOW() - INTERVAL '1 month'
// GROUP BY u.id, u.name
// ORDER BY closed_count DESC
// LIMIT 5;
`}
RLS Makes It Safe
Claude might hallucinate a column name or generate SQL that asks for data it shouldn't. But RLS is the failsafe. Execute the query as `authenticated_user_123` and Postgres applies RLS policies — if the user isn't an admin, the WHERE clause is silently AND'd with `owner_id = 123`. Claude sees nothing it shouldn't. An admin asking "which deals has my whole team?" gets the right answer because their RLS role permits team-wide visibility. A rep asking the same question gets filtered to their own deals. Same query. Different results. No code changes.
Read-Only, Write-Confirm
By design, Brain's generated queries are SELECT-only. No UPDATE, DELETE, or CREATE. If you want Brain to take action — update a deal stage, send an email, log a callback — Brain generates the query and suggests the action, but you confirm. "Shall I log a callback for Esteem Clinic in 3 days?" You click "Yes", Brain executes. This is the safety guardrail: AI advises and executes reads; humans approve writes. It's boring, but it works.
Six FAQs
Can Brain learn my schema automatically?
Partially. We query `information_schema.tables` and `information_schema.columns` to scrape table/column names, then you add human-readable descriptions in `brand.json` (e.g., "opportunities with expected close dates"). Claude sees names + descriptions; that's enough for 80% of queries.
What if Claude generates invalid SQL?
The query fails at execution. We catch the Postgres error, log it, and ask Claude to revise. On the frontend, Brain shows "Hmm, that didn't work. Let me try a different approach…" and fires a second attempt with the error message as context. Retry once; if it fails again, show the error to the user.
Does this work with complex joins?
Yes, but with caveats. Claude handles 2–3 table joins reliably. Beyond that, it's guessing. For truly complex analytics (rolling 90-day cohorts, recursive CTEs), write a named VIEW in Postgres and Brain queries it like a table. The view absorbs complexity; Claude just joins to it.
Can Brain combine NL queries with filters?
Absolutely. You ask "deals closed last month" and Brain generates the SQL. Then you add a manual filter "in the East region" and Brain re-runs with AND region = 'East'. Hybrid approach: AI handles the hard semantic part, filters handle the manual refinement.
What about performance on large tables?
Claude often forgets to add indexes. We coach it via system prompt: "Add LIMIT clauses where possible" and "prefer indexed columns in WHERE". For queries on tables with 100K+ rows, index `owner_id`, `stage`, `created_at`, and you're fine. Execution time is <2 seconds for most questions.
Can Brain write reports or exports?
Not yet. Right now it's query-and-render. But you can ask Brain to "show me monthly deals by region" and get a table, then export to CSV manually. Piping results to a report-generator is the next iteration.
The Verdict
Natural language queries are the death of filter dropdowns. You stop building UI for every possible question and let Claude handle the semantics. RLS makes it safe; read-only defaults make it sane. For the full streaming + tool setup, read Brain Chat Assistant. Ready to ship? Check out pricing and plan your Brain instance.