RowShield
Guides

Claude Code + Supabase MCP: fast schema, slow policy

Wiring Claude Code to a Supabase project through MCP changes the tempo of backend work. A schema change that once meant writing a migration, checking it, applying it becomes a sentence and an executed tool call. Structure moves at conversation speed. Policies do not, because a policy is a decision — who owns which rows, what a stranger may read — and decisions resist being appended to the end of an unrelated prompt.

That asymmetry has a name in monitoring: drift. Not a mistake anyone made, but distance growing between the state you believe the database is in and the state it reached while you were looking elsewhere. This page walks through what specifically drifts in MCP-driven projects, how to detect it with snapshots and transitions rather than memory, and the small rituals that keep a fast loop honest. The engine angle matters here: catching drift is a job for scheduled comparison, not for remembering to look.

Rules that check this

Why the speeds differ

Applying DDL through the MCP is a completed action: the table exists, the tool reports success, the conversation continues. Creating a policy requires answers the schema cannot supply — which column identifies the owner, whether teammates share access, what should happen to rows when users leave. Those are product questions wearing SQL clothing, and they slow every generator down, including a capable one mid-session.

So the practical sequence drifts toward structure-first, policy-later, and later stretches across session boundaries. A session ends; context evaporates; the next session begins from the live database rather than from intentions. Whatever lacked policies yesterday starts today as an invisible baseline — not broken, not flagged in the conversation, simply waiting for the first request that cares.

What actually drifts

Four movements account for most of it. A new table appears without RLS, or with RLS and no policies. An existing table is dropped and recreated during iteration — MCP-assisted refactors favour drop-and-recreate — and its policies die with the old table, leaving the replacement naked. A policy is rewritten to widen access while debugging some unrelated denial and stays widened. RLS itself is toggled off to "fix" a failing write and stays off.

Each of these is individually reasonable inside the session where it happened. Drift is what they look like from outside: the diff between last-known-good and now. Which is why the detection question is never "is the schema correct" in the abstract but "what changed since the last time we checked" — a question with a mechanical answer.

Catching drift: snapshots and transitions

The current state is always one query away, and capturing it at the end of every session gives tomorrow a baseline to argue with. Run it yourself in the SQL editor, or ask the session to run it while wrapping up — either way the output is short enough to read on the spot and precise enough to compare against next week:

Save the output somewhere dated — a gist, a checked-in file, even a ticket comment. The snapshot costs seconds and earns its keep the moment something looks wrong later: comparing two dated snapshots converts vague unease into an explicit diff, and the diff names the object involved, which is most of the diagnosis delivered before anyone starts digging.

SELECT c.relname AS table_name,
       c.relrowsecurity AS rls_enabled,
       coalesce(
         string_agg(p.policyname || ' :: ' || p.cmd, E'\n' ORDER BY p.policyname),
         '<no policies>'
       ) AS policies
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN pg_catalog.pg_policies p
       ON p.schemaname = n.nspname AND p.tablename = c.relname
WHERE c.relkind IN ('r', 'p')
  AND n.nspname = 'public'
GROUP BY c.relname, c.relrowsecurity
ORDER BY c.relname;

Transitions, not lists

Reading that output by hand works exactly twice; the durable version compares snapshots automatically and reports only differences — appeared, came back, disappeared. That transition vocabulary carries information a flat list cannot: a policyless table appearing after weeks clean says your process sprang a leak this week, and a previously fixed state returning says the fix regressed, which is a different and more urgent signal than a fresh problem.

This is the drift engine RowShield runs on every scan: a catalog snapshot taken on each run, diffed against the previous one, with alerts raised on transitions only so a project sitting broken pages once rather than hourly. Rescans are idempotent — a retried scan re-reads recorded history and emits nothing new. The rules feeding the diff include the ones that matter most in MCP workflows: RLS-disabled and enabled-with-no-policies among them.

Working habits that help

None of this argues against the MCP — the speed is real and mostly spent well. Two habits contain the specific risk. End significant sessions by running the snapshot query and skimming it; thirty seconds of catalog beats a week of assumptions. And treat any debugging that ends in widened access as a flagged change to revisit, because debugging-shaped permissions are the ones nobody reviews.

Scheduled monitoring turns both habits into defaults: RowShield connects with a catalog-read credential, reads pg_catalog and bucket metadata only, never selects from your tables, and is an independent product unaffiliated with and not endorsed by Supabase. Plans differ in frequency — daily on Free, hourly on Indie, fifteen minutes on Team — while findings and remediation SQL are never withheld on any tier.

Frequently asked

Is the Supabase MCP unsafe for schema work?
The tool executes what it is asked competently; the risk is temporal, not mechanical. Schema moves at conversation speed while policy decisions lag, and the gap between them is drift. Speed plus scheduled comparison of live state against last-known-good is the combination that keeps fast loops trustworthy.
What does a drift alert actually contain?
A named transition — something appeared, came back, or disappeared — with the object involved: the table that gained or lost RLS, the policy that vanished with a recreated table. Alerts fire on the change rather than on every rescan, so silence means unchanged, and noise stays reserved for news.
Does the dashboard advisor cover drift?
The advisor is a point-in-time check, good at what it examines when examined. Drift is definitionally about the interval between examinations — what changed while nobody was querying. Pairing the two works: advisor for depth on demand, transition alerts for the time in between.

Check your project in about ten seconds

Paste a URL. No signup, no writes, nothing stored.

Run the free audit
claude code supabase mcp securitysupabase mcp schema driftclaude code supabase policiessupabase policy drift detection