Use the session pooler
In the Supabase dashboard, open Connect and choose Session pooler. Copy the string it gives you rather than assembling one — the pooler host carries a region and an index that vary by project. It looks like this:The direct and dedicated endpoints publish an IPv6 (
AAAA) record by default. Supabase’s IPv4 add-on swaps that record for an IPv4 (A) one rather than serving both, so enabling it changes which networks can reach you. The shared pooler avoids the question entirely.Transaction mode
Port6543 works, with one caveat worth knowing: transaction mode reuses a backend connection per statement, so session state doesn’t persist and SQL-level PREPARE / EXECUTE aren’t supported. Your agent sends independent statements, so this rarely bites — but session mode costs nothing extra and has no such edge, so prefer 5432.
The username includes your project ref
This is the one that wastes an afternoon. On the shared pooler, Supavisor routes by tenant, and the tenant comes from the username — the role name and the project ref, joined by a dot:postgres role becomes postgres.abcdefghijklmnop, and the midplane role you’re about to create becomes midplane.abcdefghijklmnop. Get it wrong and Supabase rejects the connection with:
psql against db.<ref>.supabase.co fails when you move it to the pooler.
Create a least-privilege role
Don’t hand Midplane thepostgres role. In the Supabase SQL editor:
read, and connect your agent. See connect your agent for the client config.
Row Level Security still applies
A custom role is subject to RLS like any other non-superuser. On a table with RLS enabled and no policy matching your role, the agent’sSELECT succeeds and returns zero rows — which reads as an empty table rather than as a permission problem.
You have two honest options:
- Leave RLS in force and write a policy granting
midplanethe reads it needs. RLS stays a real boundary for the agent, and Midplane layers on top of it. - Grant
BYPASSRLSso the agent sees the underlying tables. Be deliberate about this: RLS is then no longer a boundary for the agent, and table access plus column masking become the boundary instead.
Mask what the agent shouldn’t see
A Supabase project usually holdsauth.users alongside your application tables, so it’s worth a pass through Exposure in the project workspace: scan for likely PII and mask the columns your agent has no reason to read in the clear. Masking happens before results leave Postgres, so the raw values never reach the model or the audit row.
Using this alongside the Supabase MCP server
They do different jobs and can coexist. Supabase’s own MCP server manages the project — branches, migrations, edge functions, config — and Supabase recommends scoping it to a development project. Midplane fronts the database with a SQL parser and an audit trail, which is what you want pointed at data you care about. A common split: the Supabase MCP server on a dev project for schema work, Midplane on the database that holds real rows.Next steps
Write a policy
Grant the reads and writes your agent actually needs.
Mask sensitive columns
Redact values before they leave the database.