npm supply-chain attacks and MCP: why your install command is the weak link
Installing an MCP server almost always means running npx or uvx and pulling a transitive dependency tree from a public registry. That means MCP users inherit the full npm supply-chain blast radius — and 2026 has been a bad year for npm. This is the calm version: a short tour of the verified attack pattern, why MCP installs are exposed, and an eleven-item checklist for installing servers without handing your machine to whoever published a package three hops down the tree. Pairs with our MCP security explainer.

On this page · 8 sections▾
TL;DR — why this matters for MCP
When you install an MCP server, you are almost never installing one thing. The typical command — npx -y some-mcp-server or uvx some-mcp-server — resolves that package plus its entire transitive dependency tree from a public registry and runs the result on your machine. The MCP server’s author is one trust decision; every maintainer in the tree below them is another. In 2026 several of those lower maintainers had a very bad year.
Three things to take away. One: the npm supply chain was hit repeatedly in 2026 with self-replicating worms and account takeovers, including compromises of packages with nine-figure weekly download counts. Two: the delivery mechanism in most of these was an install-time lifecycle script — code that runs the moment the package is fetched, before any of your own code imports it. Three: because MCP installs run exactly that fetch-and-execute path, an MCP server you fully trust can still ship you malware through a dependency three hops away. The fix is boring and effective: pin versions, disable install scripts by default, use lockfiles, vet the publisher, and sandbox what you run. Jump to the safe-install checklist if that is all you came for.
The 2026 npm attack pattern, verified
Four well-documented 2026 incidents (and one late-2025 worm that seeded them) describe the shape of the threat. None of these are MCP-specific — that is the point. They are the registry your MCP installs draw from.
The self-replicating worm: Shai-Hulud
Palo Alto Networks’ Unit 42 documented Shai-Hulud in September 2025 — a worm that executes during package installation, scans the host for credentials (npm tokens, GitHub PATs, cloud keys), exfiltrates them, and then uses any stolen npm token to inject itself into other packages the victim maintains and republish them. No human operator in the loop. A larger wave in early November 2025, tracked as Shai-Hulud 2.0, affected over 25,000 malicious repositories across roughly 350 unique users, per Wiz Research. The worm’s code was later open-sourced, which is why its DNA shows up in the 2026 attacks below.
The hundred-million-download takeover: axios
On March 30–31, 2026, an attacker who had hijacked the lead maintainer’s account published two malicious axios versions — 1.14.1 (tagged latest) and 0.30.4 (tagged legacy). axios has over 100 million weekly downloads. Per Huntress’ analysis, the releases introduced a phantom dependency, [email protected] — a package that had not existed before that day and is never imported by axios’s actual code — whose postinstall hook detected the OS and dropped a remote-access trojan on macOS, Windows, and Linux, then deleted itself to cover its tracks. The lesson for MCP: the package you chose can be honest while a dependency it pulls is not.
The cloud-credential variant: Miasma
On June 1, 2026, Wiz Research disclosed a compromise of at least 32 package releases under the @redhat-cloud-services npm namespace. The root cause was a compromised employee GitHub account that pushed orphan commits; the malicious releases carried a preinstall script that harvested developer and cloud credentials and tried to spread to other packages the victim could publish. Wiz describes the payload as a lightly reskinned descendant of the Mini Shai-Hulud worm, with a notable upgrade: new collectors for GCP and Azure that enumerate every identity the infected host can assume. Wiz’s framing of the shift:
“This variant suggests an increased attacker focus on gaining and leveraging access to the cloud itself.”
Wiz Research · Blog
Wiz Research on the Miasma campaign (June 1, 2026), which compromised at least 32 releases in the @redhat-cloud-services npm namespace and added GCP/Azure identity collectors to a Shai-Hulud-derived worm.
The AI-framework typosquat: Mastra
On June 16–17, 2026, Microsoft documented a compromise of the @mastra npm organization — an AI agent framework — across 140-plus packages. The attacker added easy-day-js, a typosquat of the popular dayjs date library, as a dependency. Its postinstall dropper disabled TLS certificate verification, contacted attacker command-and-control servers, and installed a second-stage Node.js implant with persistence. Crucially, the version range ^1.11.21 resolved to the malicious 1.11.22, so — in Microsoft’s words — “any developer workstation or… CI/CD pipeline that ran npm install or npm update…was potentially exposed,” whether or not the package was ever imported. That a compromise of an AI-agent framework is in this list is not a coincidence: AI tooling is now a target category, and MCP servers live in the same registry.
The common thread: install scripts
One mechanism recurs across all of these — the lifecycle script. By default, npm install runs preinstall, install, and postinstall scripts from the package and every dependency in its tree. A single compromised package anywhere below you can therefore run arbitrary code on your machine the instant it is fetched. The npm team now agrees this is the core problem. On June 11, 2026, GitHub announced that npm 12 will disable install scripts by default, calling install-time lifecycle scripts the “single largest code-execution surface in the npm ecosystem.” That change has not shipped yet, so the burden is still on you — which is what the checklist is for.
Why MCP installs inherit the blast radius
MCP did not invent any of this. It just routes you straight through it. Three properties of how MCP servers are installed put you in the line of fire.
npx and uvx pull “latest” by default. The canonical install snippet you copy from a README is usually npx -y some-mcp-server with no version pin. That fetches whatever was published most recently. The axios and Mastra compromises both lived in the window between a malicious release going up and the registry pulling it — a window measured in hours. An unpinned install is a bet that you are not running during one of those windows.
The dependency tree is the real surface. The MCP server’s own code might be a few hundred lines you could read. Its dependency tree is hundreds of packages you will never read, maintained by hundreds of people you will never vet. axios’s malware did not live in axios — it lived in a phantom dependency. Your MCP server’s malware, if it ever comes, will most likely live the same way: somewhere you weren’t looking.
Install scripts run before your code does. The MCP threat conversation focuses heavily on what a server does once it is running — what tools it exposes, what it returns. The supply-chain risk fires earlier than that. A malicious postinstall runs as your user during npx, before the MCP client ever speaks JSON-RPC to the server, before any tool is listed, before you approve anything. Tool allow-lists and approval prompts — the standard MCP controls — do nothing against it.
The uncomfortable summary: when you connect an MCP server, you are extending trust to its entire transitive dependency graph and running that graph’s install scripts with your own privileges. That is a much larger trust decision than “do I trust this server’s author,” and it is the one most install instructions quietly skip over.
The safe-install checklist for MCP servers
This is the evergreen part. Every item maps to one of the verified incidents above. You will not do all eleven for a throwaway experiment — but the more of your machine and credentials are reachable from where you install, the further down this list you should go.
Pin the exact version — never install bare latest
Replace `npx -y some-mcp-server` with a pinned version, e.g. `npx -y [email protected]`. The axios and Mastra compromises both lived in the short window after a malicious release. A pin means you only ever run a version that has had time to be scrutinized.
Commit a lockfile so the whole tree is reproducible
A package-lock.json or uv.lock freezes every transitive dependency to an exact resolved version and integrity hash. Without it, the same install on a different day can resolve a different — possibly compromised — tree. Pinning the top package is not enough; the lockfile pins everything below it.
Disable install scripts by default
Run `npm install --ignore-scripts`, or set `ignore-scripts=true` in a project .npmrc, and re-enable only for packages whose postinstall you have read and need. This removes the single mechanism behind axios's plain-crypto-js, the Mastra dropper, and the Miasma preinstall. npm 12 will make this the default; until then it is on you.
Vet the publisher and the download history
Before installing, look at who publishes the package, how long it has existed, and its weekly downloads. A brand-new package with a name one character off a popular one (easy-day-js vs dayjs) is the typosquat pattern. A sudden version jump to something like 1.0.7265 is the inflated-version pattern. Both were used in 2026.
Read the dependency tree before you trust it
`npm why <pkg>` and `npm ls` show what a server actually pulls in. A 'simple' MCP server that drags in a hundred dependencies is a hundred maintainers you are trusting. Fewer, well-known dependencies is a better signal than a polished README.
Watch for typosquats of the server name itself
The same lookalike-naming trick used on libraries works on MCP servers. Confirm the exact package name from the project's own canonical source — its repository or its listing here on the directory — not from a screenshot, a chat message, or an LLM that may hallucinate a near-miss name.
Prefer a vetted registry or curated listing
Installing from a directory that tracks the canonical source, publisher, and repository for each server narrows the typosquat surface to almost nothing — you copy the command from a maintained record instead of guessing the package name. That is the whole point of a curated MCP catalog.
Prefer remote/HTTP MCP where it exists
A remote server you connect to over Streamable HTTP and OAuth never runs its package — or its dependency tree, or its install scripts — on your machine. That removes the entire npm/PyPI supply-chain class from your client. See the next section for when this applies.
Run local stdio servers in a sandbox or devcontainer
If you must execute a server locally, do it in a container or devcontainer with no standing cloud credentials, no SSH keys, and a constrained filesystem mount. Shai-Hulud and Miasma both hunt for exactly those credentials during install. A sandbox means a malicious postinstall finds an empty room.
Keep secrets out of the install environment
Don't run `npx` to install MCP servers in the same shell that holds your AWS keys, npm publish token, and GitHub PAT. The 2026 worms are credential harvesters first; the less your install environment can reach, the less a successful compromise yields. Scope tokens narrowly and rotate them.
Subscribe to advisories and rebuild on patch
Run `npm audit` (or your scanner of choice) on the resolved tree, and follow the security feeds — Wiz, Unit 42, Microsoft, Socket, StepSecurity — that broke every 2026 incident here within hours. When a package in your tree is flagged, bump the pin and rebuild; don't wait for the next time you happen to reinstall.
If you do only three of these, do the first three: pin the version, commit a lockfile, and disable install scripts. They cover the mechanism behind every 2026 incident above and cost you almost nothing.
When to prefer a remote MCP server
The cleanest way to dodge the npm supply chain is to not run the package at all. A growing share of MCP servers are offered as remote endpoints you connect to over HTTP rather than executables you install locally. When that option exists, it changes your risk profile in a specific way: the server’s code and its whole dependency tree run on the operator’s infrastructure, not yours, so the install-time code-execution class simply doesn’t apply to your machine.
It is not a free lunch — it trades one risk for another. You now trust the operator to run a clean server, and you authenticate with an OAuth token that needs to be tightly scoped. The mechanics of doing that safely — Streamable HTTP transport, OAuth 2.1, token scoping — are their own topic, covered in our OAuth 2.1 for remote MCP servers explainer, and for self-hosted tunnels in the secure MCP tunnels guide. The rule of thumb: for a server that has a reputable hosted option, prefer it; for one that only ships as a local stdio package, fall back to the checklist above and the sandbox.
The overlap with tool poisoning — two different risks
It is worth being precise, because these two risks get blurred. The supply-chain risk on this page is about code that runs when you install a server — a malicious package executing its lifecycle script as your user. Tool poisoning, the other big MCP risk, is about instructions the model reads once the server is running — malicious text hidden in a tool description or schema that steers the agent. They share a root cause (you are trusting code and content authored by someone else) but they fire at different moments and need different defenses.
A complete posture covers both. The checklist above handles the install-time class. Tool poisoning, indirect injection, scope creep, and the rest of the run-time class are covered in depth in our MCP security explainer, which walks the OWASP MCP Top 10 and the real CVEs. If MCP is new to you, start with what MCP is and come back — the supply-chain story makes more sense once you know that an MCP server is, mechanically, just a package you run.
The honest closing note: none of this should stop you using MCP servers. The npm ecosystem is also where most of the software you already run comes from, and it has not stopped you using that. What the 2026 incidents change is the default. “Copy the npx line from the README and run it” is no longer a reasonable default. “Pin it, lock it, disable scripts, and know what it pulls” is. That is a small tax for staying off the next disclosure’s victim list.
Frequently asked questions
Are MCP servers safe to install?
Most published MCP servers are exactly what they claim to be. The risk is not the typical server — it is that installing one with npx or uvx pulls an entire transitive dependency tree, and any single package in that tree can run code on your machine during install via a lifecycle script. In 2026 several npm packages with tens of millions of weekly downloads were briefly compromised (axios, the @mastra and @redhat-cloud-services namespaces), so the question is less 'is this MCP server safe' and more 'do I trust every package its install pulls, and have I disabled install scripts and pinned versions so a bad release cannot run silently.' The checklist on this page is the practical answer.
Can npx install malware?
Yes, indirectly. npx resolves, downloads, and runs a package in one step, and by default npm executes preinstall/install/postinstall lifecycle scripts from the package and every dependency in its tree. If any of those packages has been compromised — as axios, easy-day-js, and the @redhat-cloud-services packages were in 2026 — its postinstall hook runs arbitrary code as your user the moment npx fetches it, before any of your own code imports it. npx itself is not malware; it is a delivery mechanism that inherits whatever the npm registry served you that day. Running with --ignore-scripts and pinning exact versions removes most of that exposure.
How do I install MCP servers securely?
Pin the exact version rather than letting npx pull latest; commit a lockfile so the resolved dependency tree is reproducible; run installs with scripts disabled (npm install --ignore-scripts, or set ignore-scripts=true in .npmrc) and only enable scripts for packages you have reviewed; vet the publisher and download count before installing; prefer a remote/HTTP MCP server you connect to over OAuth instead of one you execute locally where that option exists; and run local stdio servers inside a sandbox or devcontainer with no standing credentials. The full eleven-item checklist is in the body of this article.
What is the Shai-Hulud npm worm?
Shai-Hulud is a self-replicating worm targeting the npm ecosystem, first investigated by Palo Alto Networks' Unit 42 in September 2025. It executes during package installation, scans the machine for credentials (npm tokens, GitHub PATs, AWS/GCP/Azure keys), exfiltrates them, and uses any stolen npm token to inject itself into other packages the victim maintains and republish them — spreading without a human operator. A larger wave reported in early November 2025, tracked as Shai-Hulud 2.0, affected over 25,000 malicious repositories across roughly 350 users. Later 2026 variants (Mini Shai-Hulud, Miasma) reuse its open-sourced code.
Why are MCP installs exposed to npm supply-chain attacks?
Because the dominant install path for an MCP server is npx (Node) or uvx (Python), and both resolve a package plus its full transitive dependency tree from a public registry, then run that code on your machine. You are trusting not just the MCP server's author but every maintainer in the tree below it. When npx pulls 'latest' you also inherit whatever version was published moments ago, which is exactly the window the 2026 axios and Mastra compromises exploited. MCP also adds its own prompt-layer risk (tool poisoning), but the package-install risk is plain old npm/PyPI supply chain.
Does --ignore-scripts make npm install safe?
It removes the largest and most automatic attack vector — install-time lifecycle scripts — but it is not a complete shield. It stops preinstall/install/postinstall hooks from running, which is how axios's plain-crypto-js dependency and the Mastra easy-day-js dependency delivered their payloads. It does not stop malicious code that runs when you later import and execute the package, and it does not stop a package that compiles native addons in postinstall from simply failing to work. Treat it as a strong default you relax deliberately, per package, after review — not as a guarantee.
What was the axios npm compromise in 2026?
On March 30-31, 2026, an attacker who had hijacked the lead maintainer's account published two malicious axios versions, 1.14.1 (tagged latest) and 0.30.4 (tagged legacy). axios has over 100 million weekly downloads. The malicious releases introduced a phantom dependency, [email protected] — a package that had not existed before that day and is never imported by axios code — whose postinstall hook delivered an OS-specific remote-access trojan to macOS, Windows, and Linux, then deleted itself to cover its tracks. It is the cleanest illustration of why an MCP server you trust can still ship you malware through a transitive dependency.
Should I use remote/HTTP MCP servers instead of local npx ones?
Where the option exists, a remote MCP server you connect to over Streamable HTTP and OAuth shifts the install-time code execution off your machine entirely — you never run the server's package locally, so its dependency tree never touches your environment. That removes the npm/PyPI supply-chain class from your client. It does not remove the prompt-layer risks (tool poisoning, indirect injection), and it introduces a trust relationship with the host instead, so vet the operator and scope the OAuth token. For local stdio servers that have no hosted equivalent, sandbox them.
Sources
Primary disclosures and analysis
- Palo Alto Networks Unit 42 — “Shai-Hulud” Worm Compromises npm Ecosystem (September 2025, updated)
- Wiz Research — Shai-Hulud 2.0 Ongoing Supply Chain Attack (November 2025)
- Huntress — Supply Chain Compromise of the axios npm Package (March 2026)
- Wiz Research — Miasma: Supply Chain Attack Targeting Red Hat npm Packages (June 1, 2026)
- Microsoft Security — From package to postinstall payload: Inside the Mastra npm Supply Chain Compromise (June 17, 2026)
- Microsoft Security — Typosquatted npm Packages Used to Steal Cloud and CI/CD Secrets (May 28, 2026)
- The Hacker News — GitHub to Disable npm Install Scripts by Default (npm 12) (June 11, 2026)