Supabase Query Access for Cursor AI Agents
Version: 1.1
Date: April 2, 2026
Owner: AI Team
1. Purpose
This guide enables Cursor AI agents (and developers) to query Brainforge Supabase databases using credentials from local .env. Supabase is Postgres under the hood; you can query via the Supabase JS client (REST/PostgREST) or direct Postgres if you have the DB connection string.
2. Projects Overview
| Project | Project ID | Purpose | Env Prefix |
|---|---|---|---|
| Internal AI Core | kxzonslnqjsceiublxkf | Clients, teams, platform data | BF_SUPABASE_* |
| Zoom Supabase | viqeppmsqvwpslpvttkk | Meeting transcripts, recordings, embeddings | ZOOM_SUPABASE_* |
| Slack Messages | oqtkgsndvitzyfzwcdoz | Slack channel history, content blocks, embeddings | SLACK_SUPABASE_* |
See databases.md for table schemas and data flow.
Optional server-side audit inserts (PostgREST, fire-and-forget): optional-postgrest-audit-logging.md.
3. Query Methods
3.1 Supabase JS Client (Recommended)
Uses REST/PostgREST. Works with URL + service key from local env. No Supabase CLI required.
Required env vars (from .env or .env.local):
| Project | URL | Key |
|---|---|---|
| Internal AI Core | BF_SUPABASE_URL | BF_SUPABASE_SERVICE_KEY |
| Zoom Supabase | ZOOM_SUPABASE_URL | ZOOM_SUPABASE_SERVICE_KEY |
| Slack Messages | SLACK_SUPABASE_URL | SLACK_SUPABASE_SERVICE_KEY |
Smoke test (from apps/platform):
cd apps/platform && npx tsx scripts/generate-db-docs.tsExpected: “Found N tables” (Zoom) and “Found N clients” (Internal AI Core).
Minimal Node/TS query example:
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(
process.env.BF_SUPABASE_URL!,
process.env.BF_SUPABASE_SERVICE_KEY!
);
const { data, error } = await supabase.from("clients").select("id, name").limit(5);3.1a Loading a monorepo .env (agents and scripts)
The repo root .env can be large and syntactically rich. Bash set -a; source .env may silently leave variables unset if an earlier line breaks POSIX shell parsing (unclosed quotes, etc.), while Node often still loads the same file.
- Prefer
node --env-file=/path/to/brainforge-platform/.envorrequire("dotenv").config({ path: "…" })when validatingBF_SUPABASE_*or running one-off API smoke tests. - Service-specific Supabase vars (e.g.
ASSISTANT_INTERACTION_LOG_SUPABASE_*on Railway) may duplicate Internal AI Core values; document the mapping in that service’sENVIRONMENT.md/.env.example.
Platform-wide convention: BF_SUPABASE_URL + BF_SUPABASE_SERVICE_KEY = Internal AI Core for Forge and most internal tools. Other apps may define their own prefixed vars when they need a different project or clearer deploy boundaries.
3.2 Direct Postgres (Optional)
For raw SQL, psql, or tools that need a Postgres connection string. Requires the database password (different from API keys), from Supabase Dashboard → Project Settings → Database.
Connection string format:
postgresql://postgres.[project-ref]:[PASSWORD]@aws-0-[region].pooler.supabase.com:6543/postgres
To enable: Add to .env (do not commit):
BF_DATABASE_URL=postgresql://postgres.kxzonslnqjsceiublxkf:[PASSWORD]@aws-0-us-east-1.pooler.supabase.com:6543/postgres
ZOOM_DATABASE_URL=postgresql://postgres.viqeppmsqvwpslpvttkk:[PASSWORD]@aws-0-us-east-1.pooler.supabase.com:6543/postgres
SLACK_SUPABASE_CONNECTION_STRING=postgresql://postgres.oqtkgsndvitzyfzwcdoz:[PASSWORD]@aws-0-us-east-1.pooler.supabase.com:6543/postgres
Replace [PASSWORD] and [region] with values from the Supabase dashboard. Credentials can also be fetched via 1Password (see 1password-cli-setup.md).
psql example:
psql "$BF_DATABASE_URL" -c "SELECT id, name FROM clients LIMIT 5;"3.3 Supabase MCP (Cursor)
The user-supabase MCP provides execute_sql and list_tables tools that accept a project_id parameter. Use the same MCP for both Slack and Zoom; pass the correct project ID on each call.
| Data Type | project_id |
|---|---|
| Internal AI Core (clients, team, platform tables, assistant audit log, etc.) | kxzonslnqjsceiublxkf |
| Slack messages, content blocks, embeddings | oqtkgsndvitzyfzwcdoz |
| Zoom transcripts, meetings, embeddings | viqeppmsqvwpslpvttkk |
Requirements: The Supabase account used by the MCP must have access to both projects. If the Slack project is in a different Supabase org, add that project to your account or link it in the Supabase dashboard so list_projects includes it.
Smoke test: Call execute_sql with project_id: "oqtkgsndvitzyfzwcdoz" and:
SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_name LIKE 'client_%_messages' LIMIT 3;For Cursor, no separate connection string is needed; the MCP uses Supabase account auth. See supabase-context-agent.md for query patterns and schema-reference.md for table schemas.
3.4 Supabase CLI
For migrations, local dev, or project management. Not required for basic querying. See Supabase CLI docs.
4. Agent-Safe Workflow
- Verify env: Ensure
BF_SUPABASE_URL,BF_SUPABASE_SERVICE_KEY,ZOOM_SUPABASE_URL,ZOOM_SUPABASE_SERVICE_KEYare set in.envor.env.local. For Slack from platform code, addSLACK_SUPABASE_URLandSLACK_SUPABASE_SERVICE_KEY(or useSLACK_SUPABASE_CONNECTION_STRINGfor direct Postgres). - Run smoke test:
cd apps/platform && npx tsx scripts/generate-db-docs.tsto confirm both projects respond. - Prefer read-only: Use
SELECTonly when exploring; avoidINSERT/UPDATE/DELETEunless the user explicitly requests it. - Use existing scripts:
scripts/generate-db-docs.ts,scripts/backfill-transcripts-to-vault.ts,scripts/assign-meeting-clients.tsalready query Supabase.
5. Common Operations
Internal AI Core (clients, teams)
const { data } = await supabase.from("clients").select("id, name").order("name");Zoom Supabase (meetings, transcripts)
const { data } = await supabase
.from("zoom_meeting_recording_files")
.select("id, folder, meeting_date, summary")
.limit(10);Raw SQL via RPC (if available)
const { data } = await supabase.rpc("get_public_tables");6. Troubleshooting
| Issue | Check |
|---|---|
| ”Missing Zoom/BF Supabase credentials” | Ensure ZOOM_SUPABASE_URL, ZOOM_SUPABASE_SERVICE_KEY, BF_SUPABASE_URL, BF_SUPABASE_SERVICE_KEY in .env or .env.local |
Bash source .env but BF_SUPABASE_URL empty | Monorepo .env may break shell parsing; verify with node --env-file=.env -e "console.log(process.env.BF_SUPABASE_URL?.length)" from repo root |
| Slack project not visible in MCP | Ensure Supabase account has access to Slack project (oqtkgsndvitzyfzwcdoz); if in different org, add project to account or link in dashboard |
| RLS / permission errors | Service key bypasses RLS; anon key respects RLS. Use service key for backend/agent queries |
| Script runs from wrong dir | Run from apps/platform so dotenv finds .env |
| Direct Postgres fails | Verify BF_DATABASE_URL / ZOOM_DATABASE_URL format and password from Supabase dashboard |
7. Credentials Storage
API keys (service keys) for Zoom and Internal AI Core live in 1Password (see also databases.md):
| Item | Vault (typical) | Field for service role JWT | Database |
|---|---|---|---|
Supabase Zoom Database API Key | Employee | password | Zoom Supabase |
Supabase Internal AI Core Database Secret Key | Employee | notesPlain (JWT) | Internal AI Core |
Supabase Slack Database API Key (or equivalent) | Employee / team vault | per item | Slack Messages |
Internal AI Core — CLI example (after op signin; use --account if you have multiple accounts):
op read "op://Employee/Supabase Internal AI Core Database Secret Key/notesPlain"If your item name or vault differs, run op item list and locate the Internal AI Core service key item, then use the correct op://Vault/Item/field path.
Database passwords for direct Postgres are in Supabase Dashboard → Project Settings → Database.
Cursor MCP: No separate 1Password entry is required when the MCP uses Supabase account/dashboard auth and both Slack and Zoom projects are visible in that account. The MCP runs in the user’s Cursor environment and uses project_id per call. 1Password is for platform env and direct Postgres only.
Security: No secrets in repo. Use 1Password CLI (op) or copy from 1Password into local env for platform/direct DB use. See 1password-cli-setup.md.