An MCP server, an agent skill, or a coding-assistant plugin is an npm package before it is anything else — with an ordinary, often large, often transitive dependency tree underneath it. MCP security: a practical guide to tool poisoning and rug pulls covers the first question: is this server's tool description safe to hand to your agent's context window. This post covers the second, mostly separate question: is the code underneath that description safe to run at all — and a run of self-replicating npm worms since September 2025 says the answer increasingly depends on whether the worm has decided your dependency name looks like AI tooling.
TL;DR
| Question | Answer |
|---|---|
| What changed? | npm worms went from generic credential theft to deliberately targeting packages named like MCP servers and AI frameworks |
| Is this an MCP protocol flaw? | No — it's an ordinary npm supply-chain attack that happens to run underneath MCP servers, skills, and coding-agent plugins |
| How do these worms spread? | Steal a maintainer's or CI environment's publish token, then auto-publish a malicious version of a widely depended-upon package |
| What actually executes the payload? | Usually a postinstall script, run automatically on npm install before you've reviewed anything |
| Does pinning versions help? | Yes, against a new malicious version landing in your tree silently — not against a version you already pinned that was already bad |
| Does Beam block this? | No. Beam's scanner and collector observe and flag; they don't audit npm's dependency graph or stop an install |
The mechanism: how these worms actually propagate
Every incident below follows a close variant of the same chain:
- An attacker gets publish access. Usually by stealing a maintainer's npm token or CI secret — via a compromised developer machine, a leaked credential, a hijacked expired domain used for account recovery, or a prompt-injection payload that tricks an automated bot with that access.
- They publish a malicious version of a package many other packages depend on. No code review gate stands between a stolen token and the registry; a
npm publishfrom a legitimate maintainer's account looks legitimate. - The payload runs on install, not on use. A
postinstall(orpreinstall) script inpackage.jsonexecutes automatically the momentnpm installresolves that version — before a developer has opened a single file to review it. - If the worm is self-replicating, it doesn't stop there. It harvests whatever npm tokens, GitHub tokens, or cloud credentials it can find in the infected environment and uses them to publish malicious versions of other packages the compromised account can touch, so the infection tree grows without further attacker action.
That fourth step is what separates a worm like Shai-Hulud from a one-off compromise like the 2025 postmark-mcp incident covered in the tool-poisoning guide. A worm doesn't need the attacker to individually target you; it needs your dependency tree to intersect with an already-infected package.
A timeline of AI-tooling-adjacent npm attacks
| Date | Incident | What made it notable |
|---|---|---|
| September 2025 | Shai-Hulud npm worm | First observed self-replicating npm worm; harvested and re-used credentials to spread automatically |
| November 2025 | Shai-Hulud 2.0 | 796 packages, ~132 million monthly downloads combined, and deliberately targeted packages named mcp-server |
| February 2026 | Cline CLI / "Clinejection" | Prompt injection in a GitHub issue title hijacked Cline's own issue-triage bot into leaking a publish token, used to ship a malicious [email protected] |
| March-April 2026 | axios RAT | Compromised maintainer account published backdoored [email protected] and [email protected]; CISA issued a public advisory |
| April-May 2026 | Mini Shai-Hulud | Cross-ecosystem wave hitting PyPI, npm, and other registries — including PyTorch Lightning, and reported to reach SAP-linked and RubyGems packages |
| May 2026 | node-ipc | Malicious versions published via a hijacked maintainer inbox (recovered through an expired, re-registered domain), appending obfuscated credential-theft and DNS-exfiltration code |
| June 2026 | Mastra AI | North Korea-attributed group Sapphire Sleet republished 144 packages in Mastra AI's npm scope in an automated 88-minute window |
Shai-Hulud and Shai-Hulud 2.0: the MCP-naming detail that matters
Shai-Hulud's first wave, identified in September 2025, established the self-replicating pattern: compromise one account, harvest its tokens, publish to the next package, repeat. The November 2025 second wave, Shai-Hulud 2.0, hit 796 packages with a combined roughly 132 million monthly downloads — and, critically, it specifically went after packages named mcp-server.
That detail is the core editorial point of this post. The worm's targeting logic didn't care whether an MCP server's tool descriptions were clean, whether it had been reviewed, or whether Anthropic's protocol spec was sound. It cared that the package name looked like AI agent tooling, because AI agent tooling is now widely depended-upon, often installed with elevated trust, and often installed by developers who are focused on reviewing prompts and tool schemas — not postinstall scripts.
Cline / Clinejection: the coding agent's own pipeline as the entry point
The February 2026 Cline CLI incident is worth flagging separately because it isn't just an AI-adjacent dependency getting hit — the coding-agent's own release pipeline was the target. A prompt-injection payload hidden in a GitHub issue title manipulated Cline's automated issue-triage bot, and Cline's own post-mortem confirms the resulting stolen token was used to publish [email protected] with a postinstall script that silently ran npm install -g openclaw@latest. Around 4,000 installs happened during the roughly eight-hour window before the token was revoked.
This is the same "trust the tool description, forget the pipeline underneath it" gap as the MCP-naming targeting above, just one layer further back: the attacker didn't need to compromise a developer's machine at all, only the automation the coding-agent's own maintainers already trusted with publish-adjacent access. Security researcher Adnan Khan's disclosure is the primary technical writeup of the "Clinejection" chain.
axios, node-ipc, Mini Shai-Hulud, and Mastra AI: it isn't only MCP-named packages
The other four incidents show the same mechanism reaching well past anything with "mcp" or "agent" in its name. The axios RAT compromise hit one of the most widely depended-upon HTTP clients in the npm ecosystem via a compromised maintainer account; CISA's April 2026 advisory recommended downgrading to verified-safe versions and rotating any credentials that may have touched an infected environment. The node-ipc incident in May 2026 traced back to an attacker re-registering an expired domain tied to the maintainer's account-recovery email — no phishing or password guessing required, just abandoned infrastructure. Mini Shai-Hulud in April-May 2026 showed the pattern crossing ecosystem boundaries, landing in Python's PyPI (PyTorch Lightning) alongside npm and reportedly reaching SAP-linked and RubyGems packages in the same wave. And Mastra AI in June 2026, attributed to the North Korea-linked Sapphire Sleet group, showed how fast the whole chain can move once a single maintainer account is captured: 144 packages republished in 88 minutes.
Every MCP server, agent skill, or coding-assistant plugin sits inside a dependency tree that includes packages exactly like these. axios and node-ipc are unglamorous, deeply transitive dependencies — the kind that show up several layers down in npm ls, not the kind a developer reviewing an MCP server's tool schema thinks to check.
Why this is a separate attack surface from tool poisoning
MCP security: a practical guide to tool poisoning and rug pulls walks through tool poisoning, tool shadowing, rug pulls, and toxic flows — all attacks on what an MCP server tells your agent to do through its tool descriptions and schemas. Those attacks live entirely in text handed to the model's context window.
Supply-chain worms live somewhere else: in the install-time execution of the package's own code, before the MCP protocol layer even starts. A postinstall script doesn't need to poison a tool description to do damage — it runs with the same filesystem and network access as any other script on your machine, the moment npm install resolves it. That post already flags this distinction directly: "Check the package, not just the protocol. postmark-mcp was an npm supply-chain attack that happened to speak MCP." This post is the deeper version of that one line — the mechanism, the timeline, and what to actually check.
The practical implication: scanning an MCP server's tool descriptions for injected instructions, and auditing what its dependency tree executes on install, are two separate jobs. Passing one tells you nothing about the other.
A practical checklist
- Pin dependency versions, including transitive ones where your lockfile allows it. An unpinned range means a compromised maintainer's next publish reaches you on your very next install — that's the exact propagation step every worm above relies on.
- Audit
postinstall/preinstallscripts before installing anything new, especially for a freshly added MCP server, skill, or coding-agent plugin dependency.npm show <package> scriptssurfaces them without installing. - Treat an MCP server or skill dependency updating as an event, not background noise. If your lockfile diff shows an MCP-related dependency moving versions and you didn't ask for it, check why before running
npm installagain. - Use a dependency scanner as a first layer, not the only layer.
npm auditand dependency-scanning tools in the Socket/Snyk category catch known-malicious package versions and flagged advisories; none of them are semantic guarantees against a not-yet-reported compromise. - Watch what actually executes on your machine, not just what's declared in
package.json. A scanner reviews declared dependencies; it does not tell you that apostinstallscript quietly rancurl | bashduring your last install. That's a runtime question, not a manifest question.
That last point is where dependency scanning and local activity recording are complementary rather than redundant. Per apps/sentinel-collector/README.md, Beam's collector can record shell and file events — including what a Claude Code hook reports for PreToolUse/PostToolUse calls — into one local, inspectable timeline. That's a different layer than reading package.json: it's evidence of what actually ran, which is exactly the gap a postinstall-script payload exploits. Beam v1 does not block an install or replace a dependency scanner; it gives you something to inspect after the fact, stored locally under apps/sentinel-collector/.data with known credential formats redacted before persistence, not sent off-device by default.
Frequently asked questions
What is the Shai-Hulud npm worm?
Shai-Hulud is a self-replicating npm supply-chain worm first identified in September 2025. Once it compromises one maintainer's publishing access, it automatically harvests credentials from the infected environment and uses them to publish malicious versions of other packages that depend on it, spreading without further attacker action.
Did Shai-Hulud specifically target MCP servers?
Yes. The November 2025 Shai-Hulud 2.0 wave hit 796 packages with a combined roughly 132 million monthly downloads, and deliberately targeted packages named mcp-server. That means an MCP server dependency is exposed to this attack class regardless of whether its MCP tool descriptions are safe.
How did the Mastra AI compromise happen so fast?
Attackers attributed to the North Korea-linked group Sapphire Sleet compromised a single maintainer account with publishing rights across Mastra AI's npm scope and republished more than 140 packages in an automated 88-minute window on June 17, 2026, injecting a malicious typosquat dependency that ran a postinstall payload.
Is Clinejection the same kind of attack?
Clinejection is related but distinct: a prompt-injection payload planted in a GitHub issue title manipulated Cline's own automated issue-triage bot into leaking a publish token, which was then used to push a malicious [email protected] to npm. It matters here because it shows the coding-agent's release pipeline itself, not just a dependency, can be the entry point.
Does pinning dependency versions stop these worms?
Pinning stops your tree from silently pulling a newly published malicious version, which is how most of these worms spread. It does not help if the version you already pinned was compromised before you pinned it, so pinning complements auditing rather than replacing it.
Does Beam scan npm dependencies for this?
No. Beam's scanner checks a SKILL.md or MCP config's own text against 11 heuristic patterns plus a version-pin check; it does not audit the npm dependency tree underneath an MCP server. Beam's collector can record what a postinstall script or agent process actually does at runtime, as a complementary layer to dependency scanners like npm audit, Socket, or Snyk.
Related reading
- What is AI agent monitoring?
- MCP security: a practical guide to tool poisoning and rug pulls
- AI agent security: securing coding assistants
- AI agent security incidents: a timeline
- CISA advisory: supply chain compromise impacts axios npm package
- Datadog Security Labs: the Shai-Hulud 2.0 npm worm
- Cline's post-mortem on the unauthorized CLI npm publish
Incident details are as reported at the dates cited, primarily from CISA, Datadog Security Labs, and the affected projects' own post-mortems. Beam scanning and collector behavior reflects the Sentinel collector v0.1 README as of September 9, 2026.
