Skip to main content
Supabase gives you four connection strings for the same database, and they are not interchangeable. Two are IPv6-only unless you’ve bought an add-on, one drops session features, and one needs a username you won’t guess. This page picks the right one and hands you a least-privilege role to go with it. Once connected, your agent reaches Supabase only through Midplane: every statement is parsed, checked against policy, and written to the audit trail before it runs.

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:
It’s the right default for Midplane because it’s the only option that’s IPv4 on every plan, and it supports the full Postgres feature set. Here’s how the four compare:
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

Port 6543 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:
So the 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:
That error means the username, not the password. On the direct connection the plain role name is correct — which is exactly why a string that works in psql against db.<ref>.supabase.co fails when you move it to the pooler.

Create a least-privilege role

Don’t hand Midplane the postgres role. In the Supabase SQL editor:
Then assemble the connection string with the pooler host and the dotted username:
Paste that into Connect Postgres in the dashboard, leave the default access level at 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’s SELECT 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 midplane the reads it needs. RLS stays a real boundary for the agent, and Midplane layers on top of it.
  • Grant BYPASSRLS so 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.
Neither is wrong — but pick one on purpose. An agent silently reading empty tables is the failure mode worth avoiding.

Mask what the agent shouldn’t see

A Supabase project usually holds auth.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.