On the evening of August 10, 2026, a single GitHub account opened 23 pull requests against unrelated AI and developer-tool projects in 74 minutes. Each one added an MCP server called productivity-suite to the project's config. It offered text formatting and summarization, and for the first three tool calls that is all it did. After the third call it changed the instructions it sent back to the AI agent, telling it to look for SSH keys, AWS credentials, shell history and Kubernetes config, and to keep quiet about it. Pillar Security published the details on August 12 and named the campaign Deadbugz.
We run AI coding agents with a stack of MCP servers every day, so we did the obvious thing and audited our own setup. It did not pass. This post covers why this kind of attack is different from the npm and PyPI incidents most teams already plan for, what our audit found, and the checklist we are now working through.
Why an MCP server is a different kind of dependency
A normal library does what its code says. You can read it, pin it, scan it. An MCP server does two things: it runs code on your machine or on someone else's, and it hands your AI agent text that the agent treats as instructions. Tool names, tool descriptions and prompt templates all come from the server, and the model reads them the same way it reads your request.
That second channel is what Deadbugz abused. According to Pillar, the server kept a per-client counter. Once a client had made three tools/call requests, later tools/list and prompts/get responses carried the new instructions. No new tool appeared, and nothing in the tool list looked wrong at install time. Someone trying the server for a minute would see a harmless text formatter. The payload arrived through metadata the agent was already trusting.
Pillar's advice to people building MCP clients is the part worth repeating: a change in the tool definitions of a server you already approved should be treated as a security event and need approval again. Most clients today do not do that. You approve a server once, and whatever it says afterwards goes straight into the model's context.
How exposed is everyone else?
This is not only a problem on developer laptops. Censys counted 12,520 MCP services reachable from the internet on April 28, 2026, spread across 8,758 IP addresses, and more than 21,000 by May 6. The servers in their report were reachable without authentication. The largest groups were data and knowledge tools (1,776, many of them direct database query interfaces) and infrastructure tools (1,549). Censys did not call the tools, so these counts show exposure, not confirmed compromise.
The vendors are reacting. On August 14 Cloudflare added MCP traffic detection to its Gateway, keyed on the MCP-Protocol-Version header, so a company can at least see which machines talk MCP and block direct connections that skip an approved portal. The protocol's own security best practices page is blunt about local servers: they run with the same privileges as the client, and a client that offers one-click setup must show the exact command, without truncation, before running it. It also recommends sandboxing servers with minimal default access to the file system and network.
Network visibility helps a security team. It does nothing for the question a developer should ask first, which is: what did I actually install, and what can it reach?
What we found in our own setup
Our development host runs Claude Code with seven MCP servers configured globally and two more scoped to single projects. We wrote about how we split work between agents in our RPI workflow post; this audit was about the plumbing under that workflow. Four findings mattered.
Three servers had no version pin. One was started with npx -y and a bare package name, one with @latest, and one straight from a Git repository's default branch through uvx. Each of those downloads and runs whatever is newest every time a session starts. For a Deadbugz-style change to reach us, nobody would need to send us a pull request. The upstream package would only need to change, through a hijacked maintainer account like the September 2025 npm phishing attack, or through a maintainer who changes their mind.
Four servers were remote HTTP endpoints. Their tool definitions live on someone else's server and can change at any moment without any update on our side. Pinning does not help there. The only defense is to notice when the definitions change, and we had no way to notice.
The biggest blast radius was not the shell. Our browser-automation server connects over the Chrome DevTools Protocol to a long-running browser profile that stays logged in to several of our business accounts. Anything that can steer that server can act as us on all of them. We had been thinking about SSH keys. The browser session was the more valuable target.
We had already been bitten by tool-name confusion. Earlier this month two Playwright MCP servers were active at once with overlapping tool names, one attached to that shared browser and one launching its own sandboxed Chromium. The agent kept picking the wrong one and we spent a long debugging session assuming the browser had crashed. Nothing malicious happened, but it showed us that the model chooses tools by name and description, and nothing in the client warned us that two servers were claiming the same names. A hostile server could do the same on purpose.
A practical MCP audit checklist
This is the checklist we are working through on every machine where an agent has MCP servers. Our own config failed items 2 and 3 when we first checked it.
- Inventory everything. Global config, per-project config files, plugin-provided servers, and anything a teammate added in a pull request. Look at the full command and arguments for each one. If you cannot say why a server is there, remove it.
-
Pin every locally launched server. Use an exact version number for npm packages (such as
1.4.2, never@latest) and a commit hash for Git sources. Update on purpose, after reading the changelog. -
Snapshot tool definitions. Save the output of
tools/listandprompts/listfor each server and compare it on a schedule, including after several calls in one session, since Deadbugz only changed after the third call. Any diff in a description should be read by a person. - Treat remote servers as third parties. Only connect ones run by a vendor you would give the same data to directly, and check what scopes the token they get actually has.
-
Keep secrets out of reach. Run agents as a user that cannot read
~/.ssh, cloud credential files or production kubeconfigs. If an agent needs a logged-in browser, give it a separate profile with only the accounts that task needs. - Scope servers to projects. A database server that one project needs should not be loaded in every session on the machine.
- Restrict outbound traffic where you can. An allow-listed egress proxy turns quiet exfiltration into a blocked request you can see in a log.
-
Check the published indicators. Pillar lists the remote endpoint
productivity-suite-mcp.onrender.comand a hidden local file at~/.config/.cache/.sys/.deadbug-mcp.py. A quickgrepover your MCP config files and afindfor that path take seconds.
For the first pass, one line finds the unpinned launches in a Claude Code config: grep -nE '@latest|"npx"|git\+https' ~/.claude.json .mcp.json. It will flag some safe entries too. That is fine, the point is to look at each one.
The same rule applies to AI features you build
Deadbugz is a supply-chain story, but the lesson underneath is older: anything that lands in a model's context can act as an instruction, so the model should never hold more power than the person it is working for. That is why we design in-house assistants to read and cite rather than act, and why access checks belong in the retrieval layer instead of in the prompt. We covered the access side in scoping an internal AI assistant to the person asking, and the approval side in governing agentic AI in a helpdesk.
In FanMind, our document assistant, every outbound call goes through one allow-listed tunnel, web search is limited to domains an admin approves, and confidential documents are excluded from AI indexing by default. None of that makes prompt injection impossible. It limits what a successful injection can reach, and after an audit like ours that is the question that matters most.
If you only do one thing this week, open your MCP config and read every command in it. Ours turned up four problems we had not thought about.
Originally published on fanpino.com.
Top comments (0)