MCP and the tool-interface layer: what standardization changed, and the security surface it opened
The interesting thing about the Model Context Protocol is not that it standardizes tool calls. It is that it standardizes a trust boundary. Most MCP servers are not remote at all; they are local stdio subprocesses the host spawns. Local or remote, every tool is now an endpoint you didn't write and can't audit, whose description, results, and behavior are controlled by someone the model has no reason to trust.
Before MCP, tool plumbing was bespoke: every agent had its own adapter, its own schema, its own failure modes. MCP changed that into a shared protocol, and with it came a shared attack surface. The security literature that followed is unusually coherent, because it all attacks the same structural fact: the tool-interface layer must feed the model untrusted data and simultaneously keep that data from becoming instructions.
What the protocol standardizes
The MCP specification defines JSON-RPC 2.0 messages between hosts, clients, and servers. Hosts are the LLM applications; servers provide capabilities. Servers offer resources (context data), prompts (templated workflows), and tools (functions the model can invoke). Clients can offer sampling (server-initiated LLM calls), roots (filesystem boundaries), and elicitation. Capability negotiation happens at connect time.
What changed in practice is that tool discovery, description, and invocation are now uniform and machine-readable across the ecosystem. That is the same move the Language Server Protocol made for editors, and it has the same payoff and the same cost: a single format means a single class of vulnerabilities, and the ecosystem grows faster than the security practice of the average server author.
The spec's own security posture
The security section of the spec reads like a threat model written by people who know they cannot enforce it. Three claims matter:
- Tools represent arbitrary code execution and must be treated with caution.
- Tool descriptions and annotations are untrusted unless the server is trusted.
- Hosts must obtain explicit user consent before invoking a tool, and sampling requests need explicit user approval because the protocol intentionally limits server visibility into prompts.
Then the kicker: the protocol cannot enforce these principles at the protocol level. Implementation is left to each host. So MCP standardizes the contract but not the security model. That gap is where the attack literature lands.
Close read: InjecAgent, injection through tool results
InjecAgent (Zhan et al., Findings of ACL 2024) made indirect prompt injection through tool results measurable. The benchmark holds 1,054 test cases across 17 user tools and 62 attacker tools, with two attack intents: direct harm to the user and exfiltration of private data. Across 30 evaluated agents, ReAct-prompted GPT-4, the strongest model available when the benchmark was built in March 2024, was vulnerable 24% of the time, and reinforcing the injected content with a hacking prompt nearly doubled the success rate. The 24% is a 2024 measurement of a 2024 model; the benchmark, not the number, is the takeaway.
The design choice worth copying is where the attack lives. The attacker's content is not in the user turn; it is in the tool result, a web page or an email or an API response that the agent must process as part of doing its job. That is the asymmetry at the heart of the tool-interface layer: results are both the agent's data and the attacker's delivery vehicle.
Close read: MCPTox, poisoning the metadata itself
InjecAgent attacks through results. MCPTox (Sun et al., 2025) attacks through the tool definition, with no execution at all: malicious instructions embedded in tool metadata. It is built on 45 live real-world MCP servers and 353 tools, generating 1,312 malicious test cases across ten risk categories. Across 20 agents, o1-mini hit a 72.8% attack success rate, and refusal rates stayed under 3% even for the most aligned model. The uncomfortable finding is that more capable models are often more susceptible, because the attack exploits instruction following.
The practical demonstration came from Invariant Labs' tool poisoning analysis: a poisoned add tool whose description told the model to read ~/.cursor/mcp.json and ~/.ssh/id_rsa and pass the contents along as arguments; a rug pull, where a server changes its tool description after the user approved it; and shadowing, where a poisoned description rewrites the behavior of a trusted tool, demonstrated by an agent sending every email to the attacker's address even when the user specified a different recipient. The MCP safety audit (Radosevich and Halloran) found the same class of outcome, coerced malicious code execution, remote access control, and credential theft, and shipped MCPSafetyScanner, an agentic tool that audits an arbitrary MCP server before deployment.
The trust boundary, mapped
Every tool call has three untrusted channels, all inside the model's context:
tool description -> model calls tool -> server executes -> tool result -> next decision
^ ^ | ^
attacker's user approves? attacker's code attacker's data
instructions
The description channel is poisoned before the call. The result channel after it. The execution channel is the server's own code, free to do anything the server's privileges allow. Rug pulls mean even an approved description is not stable over time.
Hardening the host: five rules
- Treat every MCP server as an untrusted principal. Per-server allowlists for tools and data, per-server capability scoping, and no implicit sharing between servers. Shadowing only works when instructions from one server can modify behavior toward another; a per-server context boundary kills it structurally.
- Pin tool definitions. Hash the description and schema at approval time, diff on every connection, and refuse or re-approve on change. That directly answers rug pulls.
- Show the user the model-visible description, not a summary. Invariant's demo worked because the user saw add two numbers while the model saw read my SSH keys. The gap between what the model sees and what the user sees is the attack.
- Gate privileged tools behind explicit human approval with arguments rendered in full, and sanitize results before they re-enter the context: strip markup, truncate, and keep harness policy unreachable from result content. Approval only works while it is rare; an agent that asks on every call trains the user to rubber-stamp, and the consent gate collapses under prompt volume. Gating has to stay reserved for genuinely privileged tools.
- Run server code with least privilege, in an isolated runtime. The execution side is the topic of my note on sandboxing agents.
These defenses map cleanly onto the commit flow I work with at PiPlan.ai, where mutations are proposed before they are applied and the review surface renders the model's full arguments at commit, not a summary of them. Pinning is the proposal recording which tool definition it was built against; diffing is the commit-time check that the definition has not changed since the proposal; rendering is the review surface itself. A rug pull lives exactly in the gap between proposal and commit, which is why the diff belongs at commit time rather than only at install time.
MCP is a good protocol with a bad default trust model. The standardization win is real, but it bought the ecosystem a uniform interface, not uniform safety. The security model has to be built per host, and the honest position is that it is an enforcement problem, not a protocol problem: descriptions are untrusted input, results are untrusted input, and the only defenses are scoping, transparency, and gates.
Sources linked in this post were fetched and verified.