A moment vs a stream
When you approve a tool, you approve what it looked like that day. But your agent doesn't read the tool once. It reads the tool's description, its outputs and whatever ships in its next update, every time it runs. MCP servers are the plug-ins agents use to get tools, so this applies to every one you add.
The Deadbugz case
A defender-level summary, no attack details:
- How it spread: public GitHub pull requests that added or changed MCP configuration in unrelated AI, MCP and developer-tool projects. Pillar identified 23 campaign pull requests; at review time 19 were closed and four were still open.
- How it hid: the server behaved like an ordinary text-formatting utility for its first three tool calls. It kept a per-client count of calls, and once that reached three, its tool metadata changed.
- What it did next: the new text pushed the agent to look for sensitive information and to hide that from the user.
Pillar calls it runtime-gated MCP metadata poisoning, and the Cloud Security Alliance's Sep 2 research note adds that it isn't a straightforward rug pull: the switch is tied to how many times the tool was called. A one-time look at install time only ever sees the clean version. OWASP's Top 10 for Agentic Applications (Dec 2025) already lists Tool Misuse and Agentic Supply Chain Vulnerabilities among the top risks.
What the researchers recommend
"Clients should treat a change in the tool definition of an already-approved server as a meaningful security event, make that change visible to the operator, and require renewed approval before the changed tool can influence sensitive actions."Pillar Security, Aug 12, 2026
- Keep a known-good fingerprint of each approved server's tool schemas and descriptions (CSA).
- Pin tool definitions by hash, and allowlist the servers you use (CSA).
- Ask a human again before the agent acts on a changed tool for sensitive operations (CSA).
- Keep sensitive actions like credential access and running code policy-enforced, not something tool text can switch on (Pillar).
Checks that fail closed
The other half is the guard itself. Claude Code's Oct 8 update (2.1.295) added an opt-in onFailure: "block" setting for command and HTTP hooks. With it set, a hook that can't start, times out or exits with an unexpected code blocks the action instead of letting it through. Leave it unset and the action usually still goes ahead, so it's worth turning on for the checks that matter.
The same release fixed a case where a mod's hook (mods are Claude Code add-ons) was handed a deeply nested tool input cut short with no error, so a guard could pass content it never saw. A check that quietly sees less is a check that waves things through.
My setup
In my personal setup, I turned off whole connectors I don't use, so the agent sees 286 tools instead of 446. Fewer tools, fewer descriptions that can change under you. Pinning tool descriptions is my next step. The CSA idea fits in a few lines:
# sketch of the idea, not a real library approved = load("approved_tools.json") # tool name -> hash, saved when you said yes for tool in server.list_tools(): if sha256(tool.description + tool.schema) != approved.get(tool.name): pause_and_ask_a_human(tool.name) # changed since approval
The short list
- Turn off what you don't need, so it isn't running at all.
- A broken check should block, not wave through.
- Treat tool text as input, not orders.
- Look again after updates.
Saying yes takes a second. Staying sure takes a habit. Do you look at a tool again after you approve it, or only once? Tell me on LinkedIn.
Sources and further reading
Defender-level only. The tool counts are from my own setup.