Also, you can totally have an MCP for a database that doesn't provide any SQL functionality. It might not be as flexible or useful, but you can still constrain it by design.
I think this "S in MCP" stuff is a really handy indicator for when people have missed the underlying security issue, and substituted some superficial thing instead.
Also, psql doesn’t automatically provide its caller with full access to a database server—or any access at all, for that matter. You still have to authenticate yourself somehow, even if you’re it’s just local peer authentication.
If this MCP server is running with your own credentials, and your credentials give you full access to the database, then the fact that the service can be used to make arbitrary queries to the database is not remarkable: It’s literally your agent. We’d call it a bug, not necessarily a security risk. However, if it’s running with credentials that aren’t yours that provide full access, and your own credentials don’t, then this bug becomes a privilege escalation attack vector. It’s a classic confused deputy problem.
The situation with MCP today reminds me of the 1990s when everyone ran open SMTP servers. It wasn’t a big deal at first, but once the abuse became bad enough, we had to do something about it. SMTP didn’t have any security facilities in it, so we had to experiment with patchy solutions and ended up with a in-band solution involving the AUTH extension and SASL.
Something similar is going on with MCP right now. It doesn’t offer an in-band generic authentication support (hence the missing “S”). There’s no way I’m aware of to pass arbitrary application credentials to an MCP server so it can act as a database query agent that can do only as much as your credentials permit. There seems to be limited support for bearer tokens and OAuth, but neither of those directly translate to database credentials.
(permalink: https://github.com/supabase-community/supabase-mcp/blob/2ef1...)
Actually, in my experience doing software security assessments on all kinds of random stuff, it's remarkable how often the "web security model" (by which I mean not so much "same origin" and all that stuff, but just the space of attacks and countermeasures) maps to other unrelated domains. We spent a lot of time working out that security model; it's probably our most advanced/sophisticated space of attack/defense research.
(That claim would make a lot of vuln researchers recoil, but reminds me of something Dan Bernstein once said on Usenet, about how mathematics is actually one of the easiest and most accessible sciences, but that ease allowed the state of the art to get pushed much further than other sciences. You might need to be in my head right now to see how this is all fitting together for me.)
In a REPL, the output is printed. In a LLM interface w/ MCP, the output is, for all intents and purposes, evaluated. These are pretty fundamentally different; you're not doing "random" stuff with a REPL, you're evaluating a command and _only_ printing the output. This would be like someone copying the output from their SQL query back into the prompt, which is of course a bad idea.
Sqlite is a replacement for fopen(). Its security model is inherited from the filesystem itself; it doesn't have any authentication or authorization model to speak of. What we're talking about here though is Postgres, which does have those things.
Similarly, I wouldn't be going "Jesus H. Christ" if their MCP server ran `cat /path/to/foo.csv` (symlink attacks aside), but I would be if it run `cat /etc/shadow`.