Claude Code security guide
How to use Claude Code with Supabase safely
Treat the Supabase MCP server the way Claude Code's own docs treat any server: trust it only as far as you must. Add it with claude mcp add or .mcp.json, sign in through /mcp, and keep it read-only on a single project. Because Claude Code runs commands, read each migration before it applies, test its policies as a real user, and check the names of your public env vars.
Last updated
How do I add the Supabase MCP server to Claude Code?
- Add https://mcp.supabase.com/mcp with a claude mcp add command or in .mcp.json. Supabase's MCP guide has the exact command.
- Run /mcp in Claude Code and sign in with OAuth.
- Scope the server to one project and turn on read-only mode.
- Use a development project. Connect production only for a task that needs it.
Why does prompt injection matter for a terminal agent?
Supabase's MCP guide names prompt injection as the primary LLM-specific attack. Claude Code's MCP docs give the same warning about any server that fetches external content. A row in your database can hold text that reads like an instruction.
A General Analysis research demo showed the pattern with another editor: an agent connected as service_role read a support ticket and copied a private table into it. No customer breach was reported.
- Keep the Supabase server read-only unless the current task writes.
- Never put a sb_secret_ or service_role key in .mcp.json.
- Read any SQL the agent wants to run while it is working with user data, before it runs.
How do I stop secret keys reaching the client?
- Publishable key (sb_publishable_): maps to the anon or authenticated role. It is the only key client code should hold.
- Secret key (sb_secret_) or legacy service_role key: maps to service_role, which has BYPASSRLS. Keep it on servers and in Edge Functions.
# Print the names (not values) of every client-exposed variable.
grep -hoE "^(NEXT_PUBLIC_|VITE_|EXPO_PUBLIC_)[A-Z0-9_]+" .env* 2>/dev/null | sort -u
# Then confirm no secret key reached the build.
grep -rlE "sb_secret_|service_role" .next/static/ dist/ build/ 2>/dev/nullWhat should I tell Claude Code when it writes a migration?
Ask for the RLS statement and the policies in the same migration as the table. Tables created with SQL do not get RLS on their own, and with RLS on but no policy, the API returns nothing, which tempts a too-broad fix later.
create table public.bookmarks (
id uuid primary key default gen_random_uuid(),
user_id uuid not null references auth.users (id) on delete cascade,
url text not null
);
alter table public.bookmarks enable row level security;
create policy "bookmarks: owner reads" on public.bookmarks
for select to authenticated
using ((select auth.uid()) = user_id);
create policy "bookmarks: owner inserts" on public.bookmarks
for insert to authenticated
with check ((select auth.uid()) = user_id);
create policy "bookmarks: owner deletes" on public.bookmarks
for delete to authenticated
using ((select auth.uid()) = user_id);How do I test a generated policy before applying it?
- Apply the migration to a development project.
- Run the test below as a real user ID and confirm it sees only that user's rows.
- Run the RLS-off query and expect zero rows.
- Open Security Advisor and fix every ERROR before you promote the migration.
begin;
set local role authenticated;
select set_config('request.jwt.claims', '{"sub":"00000000-0000-0000-0000-000000000001","role":"authenticated"}', true);
select count(*) from public.bookmarks; -- only this user's rows
rollback;-- Every table in public without RLS. Expect zero rows.
select c.relname
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public' and c.relkind = 'r' and not c.relrowsecurity;Frequently asked questions
- Can Claude Code read my Supabase data through MCP?
- Yes, if the server you add has access to the project. That is why Supabase recommends read-only mode and one project, and why Claude Code's docs ask you to trust each server before you add it.
- How do I find tables without RLS in a Claude Code project?
- Run the RLS-off query from this guide in the SQL Editor and expect zero rows, or open Security Advisor and look for lint 0013. Supahound's posture scan repeats that check every day.
- What if a secret key already shipped to the browser?
- Rotate it in the Supabase dashboard, update every server that used it, then check your logs for what the old key did. Supahound's service-key detector watches for a key used from a browser or a new network.