DEV Community

Cover image for MCP Supply Chain Attacks: Poisoned npm and PyPI Dependencies (Security 1)
Hu Jin
Hu Jin

Posted on

MCP Supply Chain Attacks: Poisoned npm and PyPI Dependencies (Security 1)

An MCP server is just another process that installs packages. If one of those packages is malicious, the attacker inherits every secret, tool, and network path the server has. Supply-chain attacks against npm and PyPI are not theoretical anymore, and MCP makes the blast radius larger because the model can call whatever the compromised server exposes.

This article walks through how those attacks land on an MCP host, then the controls that actually reduce risk.

How a poisoned dependency reaches an MCP server

Why MCP servers are attractive targets

MCP servers often hold more than a typical microservice:

  • Secrets: API keys, database credentials, OAuth client secrets, and cloud identity tokens.
  • Privileged tools: file reads, SQL, shell-ish helpers, ticket systems, email.
  • Trust from the agent: tool results are fed straight into the model context.

A malicious postinstall script, a trojaned transitive dependency, or a typosquat of a popular MCP helper can run with the same privileges as your server process. From there it can exfiltrate tokens, wrap legitimate tools, or open a reverse channel while looking "healthy" to the agent.

How the attack usually unfolds

  1. Publish or hijack. The attacker publishes a package with a confusing name, compromises a maintainer account, or slips malware into a popular dependency via a PR or stolen token.
  2. Land in your install. npm install / pip install pulls it in—often as a transitive dependency you never named yourself.
  3. Execute early. Lifecycle scripts (preinstall, postinstall) or import-time code run before your own main starts.
  4. Abuse trust. The malware reads env vars, steals tokens from disk, phones home, or patches tool handlers so every agent call is logged or altered.

Because agents retry tools and dump results into prompts, even subtle tampering (injecting extra rows, rewriting URLs) can steer the model without crashing anything.

Defenses that work in practice

Hardening the MCP dependency pipeline

Pin and hash everything. Commit lockfiles (package-lock.json, pnpm-lock.yaml, poetry.lock, uv.lock). Prefer installs that verify hashes (npm ci, pip install --require-hashes, Poetry/uv with locked hashes) so a silent upstream replace fails the build.

Audit before you ship. Run npm audit / pip-audit / OSV in CI, and fail on high severity for anything that ends up in the MCP image. Treat "dev only" deps carefully—build tooling still runs on your CI runners.

Prefer provenance and signed releases. Where available, require packages with attestations (npm provenance, Sigstore) and pin to a known publisher. Maintain a short allowlist of packages your MCP servers may depend on; new names need a review.

Disable install scripts when you can. For Node, --ignore-scripts during CI installs (then build only what you trust) removes a common execution path. For Python, avoid packages that require arbitrary setup hooks when a wheel will do.

Separate build from runtime. Build the image in CI from a locked lockfile, scan it, then run the MCP server from that immutable image. Do not npm install or pip install on the production host at startup.

Runtime controls after install

Even a clean install today can be compromised tomorrow via a dependency update. Assume breach at the process boundary:

Defense layers for MCP supply-chain risk

  • Verify the publisher before adding a new MCP SDK, transport, or helper library.
  • Generate an SBOM for every release and keep it with the image digest.
  • Install and run in a sandbox (container, non-root, read-only root FS)—see the sandbox article in this series.
  • Allowlist egress. An MCP server that only needs one API should not be able to POST to arbitrary hosts. Block unexpected DNS and destinations.

Quick checklist

  • Lockfile committed; CI installs with hash verification.
  • Dependency audit gates the merge.
  • Allowlist (or careful review) for new packages and major upgrades.
  • No production install at boot; images are immutable.
  • Non-root process, read-only filesystem, egress allowlist.
  • Secrets from a vault or managed identity—not baked into the image layers that deps can scrape casually.

Your MCP server's security starts before the first tool call: it starts at install. Treat every dependency as code you chose to run with your credentials.

Related reading


About the author

I write about Applied AI, MCP, Azure AI Search, and RAG. Follow me on Dev.to and Hashnode for more.

Top comments (0)