My contention is that "lethal trifecta" is the AI equivalent of self-XSS. It's not apparent yet, because all this stuff is just months old, but a year from now we'll be floored by the fact that people just aimed Cursor or Claude Code at a prod database.
To my lights, the core security issue with tool/function calling in agents isn't MCP; it's context hygiene. Because people aren't writing their own agents, they're convinced that the single-visible-context-window idiom of tools like Cursor are just how these systems work. But a context is just a list of strings. You can have as any of them in an agent as you want.
Once you've got untrusted data hitting one context window, and sensitive tool calls isolated in another context window, the problem of securing the system isn't much different than it is with a traditional web application; some deterministic code that a human reviewed and pentested mediates between those contexts, transforming untrusted inputs into trustable commands for the sensitive context.
That's not a trivial task, but it's the same task as we do now when, for instance, we need to generate a PDF invoice in an invoicing application. Pentesters find vulnerabilities in those apps! But it's not a news story when it happens, so much.
† More a note for other people who might thing I'm being irritable. :)
MCP is just a widely agreed upon abstraction over hooking an LLM up to some tools.
A significant potion of things people want to do with LLMs and with tools in general involve tasks where a malicious attacker taking control of those tools is a bad situation.
Is that what you mean by context hygiene? That end users need to assume that anything bad in the context can trigger unwanted actions, just like you shouldn't blindly copy and paste terminal commands from a web page into your shell (cough, curl https://.../install.sh | sh) or random chunks of JavaScript into the Firefox devtools console on Facebook.com ?
On the latter two paragraphs: my point is that there's nothing fundamental to the concept of an agent that requires you to mix untrusted content with sensitive tool calls. You can confine untrusted content to its own context window, and confine sensitive tool calls to "sandboxed" context windows; you can feed raw context from both to a third context window to summarize or synthesize; etc.
The challenge is that you have to implement really good taint tracking (as seen in old school Perl) - you need to make sure that the output of a model that was exposed to untrusted data never gets fed into some other model that has access potentially harmful tool calls.
I think that is possible to build, but I haven't seen any convincing implementation of the pattern yet. Hopefully soon!
It's not a trivial boundary, but it's one we have a very good handle on.
Since the decision to delete a message is downstream from its untrusted text, I can’t think of an arrangement that works here, can you? I’m not sure whether to read you as saying that you have one in mind or as saying that it obviously can’t be done.
You mentioned the boundary between GET/POST args in a web app and a SQL query...but we have a system that is (by nature) mingling all of the parameters and execution together. It would be as if everyone's web server had a first line of their handler function that said something like "params = eval(user_based_params)", and you couldn't remove it...
In the context of web-request -> validator -> db query, the purpose of the validator is only to ensure that the request is safe, it doesn't care what the user chose to do as long as it's a reasonable action in the context of the app.
In the context of user -> LLM -> validator -> tool, the validator has to ensure that the request is safe, but the users intention can be changed at the LLM stage. If the user wanted to update a record, but the LLM decides to destroy it, the validator now has to have some way to understand the users initial intention to know whether or not the request is sane.
It turns out Simon Willison has been saying this for some time now; he calls it the "dual LLM" design, I think? (For me, both terms are a little broken; you can have way more than 2, and it's "contexts" you're multiplying, not LLMs.)
Imagine an LLM with the ability to read emails, update database records, and destroy database records.
The user instructs the LLM to update a database record, but a malicious injection from one of those emails overrides that with a directive to destroy the database record. Unless the validator understands the users intent somehow, the destructive action would appear perfectly reasonable.
For me the number one problem with MCP security is the lethal trifecta - the fact that it's so easy to combine MCPs from different vendors (or even from the same vectors) that provide exposure to potentially untrusted/malicious instructions in a way that can then trigger exfiltration of private data.
https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/
https://simonwillison.net/2025/Aug/9/bay-area-ai/
I don't know how we can solve this with more technology - it seems to me to be baked into the very concept of how MCP works.