The “Supabase MCP leaks your entire database” story is a demonstration, not a breach. In July 2025, General Analysis showed on dummy data that a support ticket carrying instructions for a developer’s assistant could make an assistant using the Supabase MCP server, under a service_role credential, copy a secrets table into the ticket thread, where the attacker could read it.1 No customer data was involved.
The headline travelled further than the page behind it, so this post reads the primary sources only: General Analysis’s write-up, and Supabase’s own blog, changelog and docs. The point isn’t who was right. It’s the mechanism, which works the same way with any database tool an assistant can call.
The short answer
- What happened: a research team built a fake support app on Supabase, filed a support ticket with text addressed to the developer’s coding assistant, and watched the assistant follow it. The instance held dummy data only, and the authors say they have not rerun the demonstration against the current server.1
- Was it a Supabase bug? Not in the usual sense. Supabase’s response says there has been no reported incident of a customer data leak through MCP, that row-level security stays enforced, and that the server in this case ran at a higher privilege.2
- What changed: Supabase describes read-only mode, project scoping, feature groups and wrapping of SQL results with warnings, and says the wrapping reduced the risk without removing it.2 Its docs now put the security settings next to the connection instructions.4
Deedglass
A tool call meets your rules before it runs
Asks to run
DELETE FROM visits;
Deletes every row in visits
Deedglass checks your rules
- Reading dataAllowed
- Deleting a whole table (DROP, TRUNCATE)Never
- Changing or deleting rows without saying which (WHERE)This one
- Other changesWait for a person
What General Analysis set up
General Analysis describes the setup as mirroring a typical multi-tenant customer-support SaaS, seeded with dummy data, with row-level security on for every table, and assumes the developer uses Cursor with the Supabase MCP server connected.1 Three tables matter:
| Table | What it held | Who should read it |
|---|---|---|
support_tickets |
customer tickets | support staff and the developer |
support_messages |
each ticket’s conversation | support staff and the developer |
integration_tokens |
refresh tokens for other services | nobody in the support flow |
The support agent in the app had no access to sensitive tables such as integration_tokens.1 The MCP server, though, connected with service_role.1 Supabase’s own docs describe that role as one “used by the API (PostgREST) to bypass Row Level Security”.6 So the app’s protections were correct, and the assistant’s credential stepped around them.
What the assistant did, step by step
Code What the screen or file shows, copied as is.
attacker database developer + assistant
-------- -------- ---------------------
1. files a ticket ──────▶ support_messages
(a question, plus a (stored as a customer
block addressed to message, unfiltered)
the assistant)
2. "show me the latest
open ticket"
◀── reads schema, tickets ──── 3. assistant queries
──── ticket text, incl. ─────▶ via MCP (service_role)
the attacker's block
◀── SELECT * FROM ──────────── 4. assistant follows
integration_tokens the block
◀── INSERT the tokens as ───── 5. writes them back
a message on the ticket
6. refreshes the ◀────── support_messages
ticket page (tokens now visible)
Every arrow is an ordinary, permitted database call. Nothing was exploited in the sense of a memory bug or an authentication bypass. The assistant read text, treated part of it as instructions, and used a credential that could read anything and write anywhere.1 General Analysis describes the steps as the assistant loading the schema, listing and filtering tickets, fetching the latest ticket’s messages, and then generating two queries: one to read every row of integration_tokens, one to insert the result into the same thread.1
Why it worked
Simon Willison’s name for this shape is “the lethal trifecta”: access to your private data, exposure to untrusted content, and the ability to externally communicate.5 The demo has all three in one session:
- Private data: a credential that bypasses row-level security, so every table is readable.
- Untrusted content: customer-written ticket text, read by the same assistant.
- A way out: the same credential could write to a table the attacker can see. The exit wasn’t the web; it was the app’s own ticket thread.
That third point is the one people miss. “Our assistant has no internet access” doesn’t close the exit if the assistant can write somewhere an outsider reads: a ticket, a comment, a public profile field, a status page.
What Supabase said and shipped
Supabase answered in September 2025 with a post titled “Defense in Depth for MCP Servers”. Its main points, in its own framing:2
- No customer has reported a data leak through MCP, and row-level security is not bypassed by MCP itself; in this case the server ran with higher privileges.
- The defences are layered: read-only mode, a mode scoped to one project, feature groups that limit which tools the model sees, and wrapping SQL results with warnings not to follow instructions in the data.
- The wrapping “reduced risk but did not eliminate it”, and even in read-only mode prompt injection “remains the number one concern”.
- Its recommendations: use MCP with non-production data, keep manual approval on in the client and be wary of “Always Approve”, limit tools with feature groups, and log the queries.
In October 2025 Supabase launched a hosted MCP server at https://mcp.supabase.com/mcp, replacing an npx setup with a personal access token that, in Supabase’s words, had weaker security because it was easy to commit to source control.3 The current docs list the settings as URL parameters: project_ref scopes the server to one project and turns off account tools, read_only=true runs every query as a read-only Postgres user, and features enables only the tool groups you name.4 They also say prompt injection is the main risk, recommend reviewing each tool call in interactive work, and say not to give the server to your customers or end users.4
Credit where it’s due: these are the right controls, they’re documented next to the setup instructions, and Supabase is candid that none of them is complete on its own.
What each setting would have changed
This is our reading of the demo against today’s settings, not a rerun. General Analysis itself notes that read-only access still permits reads of data the database role can perform.1
| Setting | Step 4: read the tokens | Step 5: write them back | Notes |
|---|---|---|---|
read_only=true |
Still possible if the read-only user can read the table | Blocked | Closes the exit in this demo, not the read |
project_ref=<id> |
Still possible inside that project | Still possible | Limits damage to one project |
features=database,docs |
Still possible | Still possible | Fewer tools, same SQL |
| Manual approval of each call | A person sees SELECT * FROM integration_tokens |
A person sees the INSERT |
Works if the person reads the query |
A role that can’t read integration_tokens |
Refused by Postgres | Nothing to write | The only row that stops step 4 |
| Secrets outside the app database | Nothing to read | Nothing to write | Removes the target |
Two rows do the real work: a database role that matches the job, and keeping secrets somewhere the assistant can’t reach. The other settings shrink the blast radius or the exit. Our companion post, Is Supabase MCP safe?, turns this table into a setup checklist and shows a local reproduction of the read-versus-write difference.
What the demo doesn’t show
Be fair to everyone involved:
- It isn’t a breach. The data was dummy, made for the test.1
- It isn’t specific to Supabase. Any MCP server or database tool that runs untrusted text past a model with a broad credential has the same shape. The MCP security checklist covers the general case.
- It predates the current server. The authors say they haven’t rerun it against today’s hosted server.1 Don’t read it as a statement about current defaults.
- The fix isn’t a better filter. Supabase tried result wrapping and classifiers and concluded guardrails alone aren’t enough.2 Detecting “suspicious” text in customer messages is a losing game.
If you’re running an assistant against a database today, the question to ask isn’t “is this server vulnerable?” It’s “what could this session’s credential read, and where could it write?” To answer it for your own machine, npx deedglass scan reads the MCP configs on your machine and grades the setup locally (what the grade means); the playground shows the related failure, a database doing more than one statement said.
Questions people ask
Did Supabase MCP leak real customer data?
No. The demonstration used a project populated with dummy data only.1 Supabase says there has been no reported incident of a customer data leak through MCP.2
Does Supabase MCP bypass row-level security?
The server doesn’t switch row-level security off. It runs with whatever credential it’s given. In the demo that was service_role, which Supabase’s docs describe as the role the API uses to bypass Row Level Security.6
Would read-only mode have stopped the attack?
It would have blocked the write that put the tokens in the ticket, but not the read. General Analysis notes that read-only access still permits reads the database role can perform.1 The assistant could still have shown the tokens in the developer’s chat.
Is it safe to use Supabase MCP now?
It can be, on a development project, scoped and with a narrow role. Supabase itself says to connect to a production project only when the task needs production evidence, and to scope it, make it read-only and limit the tool groups when you do.4 The checklist is in Is Supabase MCP safe?.
Written with AI assistance and checked against our claims register.