Supply Chain · npm Security
MCP server npm publish security
Publishing an MCP server to npm gives every developer who installs it a copy of your code. It also creates a high-value target: whoever can publish a new version under your package name can inject code into every system that auto-updates your package. npm account takeover is the most direct path to a supply chain attack on an MCP server with an active user base.
How npm account takeover enables malicious version injection
The attack chain is straightforward: phish or credential-stuff a maintainer's npm account → log in → npm publish a new patch version with injected malicious code → every consumer with ^ or ~ version ranges pulls the compromised version on next install. The attack is especially effective against MCP servers because:
- Users install MCP servers with trust in the author, not in a security review team.
- Many MCP servers use
^version ranges in dependencies, meaning auto-update is the default. - The installed code runs in an LLM agent process with access to all credentials in
process.env. - Consumers rarely audit installed package code before running it.
The ua-parser-js incident (2021) saw a maintainer's account phished and three versions published with malicious postinstall scripts that installed cryptominer and password-stealing software. The package had 7 million weekly downloads. Compromised MCP servers with similar reach could exfiltrate API keys from every agent that installs them.
Defense 1: Enable 2FA on your npm account
npm supports two 2FA modes: auth-only (login requires 2FA) and auth-and-writes (login AND publish/unpublish require 2FA). For MCP server maintainers, use auth-and-writes — it means even an attacker with your stolen password cannot publish a new version without your TOTP code:
# Enable 2FA for auth and write operations npm profile enable-2fa auth-and-writes # Verify 2FA is enabled npm profile get 2fa
npm now also supports passkeys as a 2FA method, which are phishing-resistant (unlike TOTP codes, which can be stolen by real-time phishing proxies). Prefer a passkey or hardware security key over TOTP for highest protection.
Defense 2: Use granular automation tokens for CI
Many MCP server maintainers store a legacy npm token in their CI environment to enable automated publishing. Legacy tokens have full account permissions — they can publish, unpublish, and change account settings. A leaked CI token (from a public repository, a logging misconfiguration, or a compromised CI environment) gives an attacker full npm account control.
Replace legacy tokens with granular automation tokens scoped to specific packages and operations:
# Create a granular token via npm website:
# npmjs.com → Account Settings → Access Tokens → Generate New Token → Granular Access Token
# Settings:
# - Packages: select only YOUR_PACKAGE_NAME
# - Permissions: read and write (publish only)
# - No org permissions
# - Expiration: 90 days (rotate on each release)
# Use in GitHub Actions as a secret
# In .github/workflows/release.yml:
- name: Publish to npm
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm publish --access public
Granular tokens limit blast radius: a leaked token can only publish to the specific package it was scoped to, and cannot modify account settings or access other packages.
Defense 3: Enable npm provenance attestation
npm provenance (available since npm 9.5, supported on GitHub Actions, GitLab CI, and other OIDC-supporting CI providers) links your published package to the specific CI run that built it, via a Sigstore signature. Consumers can verify that the version they install was built by your CI pipeline, not by someone who gained access to your npm token:
# In GitHub Actions, add --provenance flag to npm publish
- name: Publish to npm with provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm publish --provenance --access public
# The published package will show attestation data
# Consumers can verify with:
npm info YOUR_PACKAGE_NAME dist.attestations
When provenance is enabled, the npm registry page for your package displays a "Provenance" section linking to the exact GitHub Actions workflow run, commit SHA, and repository that published the version. This provides consumers with tamper-evident evidence of build provenance.
Defense 4: Configure package access settings
# Restrict who can publish (team accounts) npm access set status=public @your-org/your-mcp-server npm access set mfa=require @your-org/your-mcp-server # List all maintainers — remove any you don't recognize npm owner ls your-mcp-server # Remove a maintainer npm owner rm suspicious-username your-mcp-server
Defense 5: Monitor your package for unexpected new versions
Set up monitoring to alert you when a new version of your package is published — so you know immediately if someone publishes a version you didn't authorize:
# Simple GitHub Actions scheduled check
name: Monitor npm package versions
on:
schedule:
- cron: '0 */6 * * *' # every 6 hours
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Check latest version
run: |
LATEST=$(npm info your-mcp-server dist-tags.latest)
EXPECTED="1.2.3" # update this on each release
if [ "$LATEST" != "$EXPECTED" ]; then
echo "ALERT: Unexpected version on npm: $LATEST (expected $EXPECTED)"
exit 1
fi
What consumers can do
As a consumer of MCP servers published to npm, you can reduce your exposure by:
- Pin exact versions in your
package.json("1.2.3", not"^1.2.3"). No automatic updates, no surprise new versions. - Check for provenance attestations before installing:
npm info PACKAGE_NAME dist.attestations - Run
npm auditregularly to catch known compromise incidents that have been reported. - Use a lock manager (Dependabot or Renovate) with required PR reviews for dependency updates, so no version bump goes to production without human review.
SkillAudit findings
Run a SkillAudit scan on your published MCP server to check for provenance attestation, 2FA status (via npm API), and token scope signals. SkillAudit's supply chain axis also checks for lockfile presence and npm audit findings. See also: MCP server supply chain risk deep-dive and MCP server SBOM security.