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

ProjectProject IDPurposeEnv Prefix
Internal AI CorekxzonslnqjsceiublxkfClients, teams, platform dataBF_SUPABASE_*
Zoom SupabaseviqeppmsqvwpslpvttkkMeeting transcripts, recordings, embeddingsZOOM_SUPABASE_*
Slack MessagesoqtkgsndvitzyfzwcdozSlack channel history, content blocks, embeddingsSLACK_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

Uses REST/PostgREST. Works with URL + service key from local env. No Supabase CLI required.

Required env vars (from .env or .env.local):

ProjectURLKey
Internal AI CoreBF_SUPABASE_URLBF_SUPABASE_SERVICE_KEY
Zoom SupabaseZOOM_SUPABASE_URLZOOM_SUPABASE_SERVICE_KEY
Slack MessagesSLACK_SUPABASE_URLSLACK_SUPABASE_SERVICE_KEY

Smoke test (from apps/platform):

cd apps/platform && npx tsx scripts/generate-db-docs.ts

Expected: “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/.env or require("dotenv").config({ path: "…" }) when validating BF_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’s ENVIRONMENT.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 Typeproject_id
Internal AI Core (clients, team, platform tables, assistant audit log, etc.)kxzonslnqjsceiublxkf
Slack messages, content blocks, embeddingsoqtkgsndvitzyfzwcdoz
Zoom transcripts, meetings, embeddingsviqeppmsqvwpslpvttkk

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

  1. Verify env: Ensure BF_SUPABASE_URL, BF_SUPABASE_SERVICE_KEY, ZOOM_SUPABASE_URL, ZOOM_SUPABASE_SERVICE_KEY are set in .env or .env.local. For Slack from platform code, add SLACK_SUPABASE_URL and SLACK_SUPABASE_SERVICE_KEY (or use SLACK_SUPABASE_CONNECTION_STRING for direct Postgres).
  2. Run smoke test: cd apps/platform && npx tsx scripts/generate-db-docs.ts to confirm both projects respond.
  3. Prefer read-only: Use SELECT only when exploring; avoid INSERT/UPDATE/DELETE unless the user explicitly requests it.
  4. Use existing scripts: scripts/generate-db-docs.ts, scripts/backfill-transcripts-to-vault.ts, scripts/assign-meeting-clients.ts already 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

IssueCheck
”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 emptyMonorepo .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 MCPEnsure Supabase account has access to Slack project (oqtkgsndvitzyfzwcdoz); if in different org, add project to account or link in dashboard
RLS / permission errorsService key bypasses RLS; anon key respects RLS. Use service key for backend/agent queries
Script runs from wrong dirRun from apps/platform so dotenv finds .env
Direct Postgres failsVerify 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):

ItemVault (typical)Field for service role JWTDatabase
Supabase Zoom Database API KeyEmployeepasswordZoom Supabase
Supabase Internal AI Core Database Secret KeyEmployeenotesPlain (JWT)Internal AI Core
Supabase Slack Database API Key (or equivalent)Employee / team vaultper itemSlack 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.