npmguard
The npmguard server evaluates the security risk of npm packages before installation, returning a structured verdict with detailed signals to guide AI agents.
Risk-score any npm package by name (including scoped packages like
@scope/name), optionally pinning a specific version (defaults to latest)Receive a structured verdict at one of three levels:
ok— package appears safe, proceedwarn— suspicious signals, user should reviewblock— dangerous, do NOT install without explicit user approval
Get detailed risk signals explaining the score, including:
KnownCve— confirmed malicious packages (e.g., OSVMAL-*advisories)Typosquat— name is one edit away from a popular packageLifecycleScripts— definespreinstall/install/postinstallscriptsPackageAge— very recently publishedMaintainerChurn— dormant package recently resurrectedSoleMaintainer— only one maintainerRepoHealth— linked GitHub repo is archived or inactiveReleaseAnomaly— version differs from predecessor in takeover-shaped waysDeprecated— package version is marked deprecated on npm
Integrate with AI coding assistants (Claude Code, Cursor, Codex) via the MCP protocol, acting as a security gate before
npm installcommands are executed
Allows AI agents to assess the risk of npm packages before installation, checking for typosquats, known vulnerabilities, lifecycle scripts, and other signals.
npmguard
I built this because it's wild how many vibe-coders just run the AI on auto-mode and let it install whatever it wants. npmguard screens them before a single install script runs.
Claude Code, Cursor, and Codex run npm install autonomously. Nobody pauses to ask "wait, is supabase-mcp-helper even real?" I built npmguard as an MCP server and CLI that scores every package (OSV malware data, typosquat/slopsquat, install-script analysis) and returns a verdict before lifecycle scripts fire.
It ships as one Rust binary, outside npm. That matters: the gate cant be poisoned by the supply chain its guarding.

npmguard install lodahs is a real typosquat of lodash, refused before lifecycle scripts can run:

Also available as GIF or MP4. Live verdict against the npm registry. lodahs is in OSV's malware namespace (MAL-2025-25502). Theme: atlas-ragnarok. Regenerate with sh docs/_theme/gen.sh (needs vhs, ffmpeg, webp, pillow, numpy).
Why
Between 2025 and 2026 the npm ecosystem got hit by a string of worm-style supply chain attacks: Shai-Hulud (Sep 2025), SHA1-Hulud (Nov 2025), the Axios compromise (Mar 2026), Mini Shai-Hulud (May 2026). All of them ran inside preinstall / install / postinstall scripts the moment someone ran npm install. You win or lose before the install completes, not after.
The existing tools dont cover the gap I was staring at:
npq,safe-npm/socket npm,npm-riskship as npm packages. They run on the exact thing they're supposed to protect you from. If the wrapper gets compromised, you've made the situation worse.pnpm v10+, Bun disable lifecycle scripts by default, but only if you've already moved offnpm. The npm majority is still exposed.npm audit, Snyk, Dependabot,lavamoat/allow-scriptsdo CVE scanning and allow-list management well. What they dont do is act as a pre-install heuristic gate, and none of them intercept AI coding assistants like Claude Code, Cursor, or Codex when they runnpm installon your behalf.
That last one is the specific thing I wanted to fix: pre-install risk scoring that also gates AI agents, shipped as a single binary outside npm. Zero runtime dependencies beyond the registries it queries.
Related MCP server: npm-guardian
Install
curl -L -o npmguard.tar.gz \
https://github.com/AyoubTadlaoui/npmguard/releases/latest/download/npmguard-v0.1.8-aarch64-apple-darwin.tar.gz
tar -xzf npmguard.tar.gz
# Edit the directory name below if the version has changed.
sudo mv npmguard-v0.1.8-aarch64-apple-darwin/npmguard-cli /usr/local/bin/npmguard
sudo mv npmguard-v0.1.8-aarch64-apple-darwin/npmguard-mcp /usr/local/bin/npmguard-mcpQuarantine note: if you downloaded through a browser rather than
curl, macOS may flag the binary as untrusted ("cannot be opened…"orKilled: 9). Clear the attribute after extracting:xattr -dr com.apple.quarantine /usr/local/bin/npmguard /usr/local/bin/npmguard-mcpThe
curlpath above avoids this;curldoes not set the quarantine flag.
curl -L -o npmguard.tar.gz \
https://github.com/AyoubTadlaoui/npmguard/releases/latest/download/npmguard-v0.1.8-x86_64-apple-darwin.tar.gz
tar -xzf npmguard.tar.gz
# Edit the directory name below if the version has changed.
sudo mv npmguard-v0.1.8-x86_64-apple-darwin/npmguard-cli /usr/local/bin/npmguard
sudo mv npmguard-v0.1.8-x86_64-apple-darwin/npmguard-mcp /usr/local/bin/npmguard-mcpcurl -L -o npmguard.tar.gz \
https://github.com/AyoubTadlaoui/npmguard/releases/latest/download/npmguard-v0.1.8-x86_64-unknown-linux-gnu.tar.gz
tar -xzf npmguard.tar.gz
# Edit the directory name below if the version has changed.
sudo mv npmguard-v0.1.8-x86_64-unknown-linux-gnu/npmguard-cli /usr/local/bin/npmguard
sudo mv npmguard-v0.1.8-x86_64-unknown-linux-gnu/npmguard-mcp /usr/local/bin/npmguard-mcpcurl -L -o npmguard.tar.gz \
https://github.com/AyoubTadlaoui/npmguard/releases/latest/download/npmguard-v0.1.8-aarch64-unknown-linux-gnu.tar.gz
tar -xzf npmguard.tar.gz
# Edit the directory name below if the version has changed.
sudo mv npmguard-v0.1.8-aarch64-unknown-linux-gnu/npmguard-cli /usr/local/bin/npmguard
sudo mv npmguard-v0.1.8-aarch64-unknown-linux-gnu/npmguard-mcp /usr/local/bin/npmguard-mcpDownload the zip from:
https://github.com/AyoubTadlaoui/npmguard/releases/latest/download/npmguard-v0.1.8-x86_64-pc-windows-msvc.zipExtract and place npmguard-cli.exe and npmguard-mcp.exe somewhere on your %PATH%, e.g. C:\tools\.
Edit the version string in the URL above if you want a specific release.
Requires Rust 1.75 or later.
cargo install --git https://github.com/AyoubTadlaoui/npmguard --bin npmguard-cli
cargo install --git https://github.com/AyoubTadlaoui/npmguard --bin npmguard-mcpAfter install, the CLI binary is named npmguard-cli; alias it if you prefer npmguard:
# bash / zsh
echo 'alias npmguard=npmguard-cli' >> ~/.bashrc # or ~/.zshrcVerify the download against the published checksums before extracting:
# Replace the filename to match your platform.
shasum -a 256 -c <(grep npmguard-v0.1.8-aarch64-apple-darwin.tar.gz SHA256SUMS.txt)All prebuilt binaries are listed on the Releases page. Homebrew tap, Scoop bucket, and curl … | sh installer land with v0.2 alongside the sandbox layer.
Docker (npmguard-mcp only)
docker pull ghcr.io/ayoubtadlaoui/npmguard-mcp:latest
docker run --rm -i ghcr.io/ayoubtadlaoui/npmguard-mcp:latestThe Docker image is mainly there for MCP catalogs and CI. For local use, install the native binary. The image ships only npmguard-mcp (not the CLI), runs as a non-root user, and has no exposed ports. MCP hosts communicate over stdio.
Quickstart: CLI
# Evaluate the latest version, no install.
npmguard check axios
# Pinned version, JSON output (machine-readable for CI).
npmguard check --json @ctrl/tinycolor@4.1.1
# Install path: v0.1 prints the verdict and stops; v0.2 will run `npm install`
# inside the sandbox layer if the verdict allows it.
npmguard install lodash@4.17.21
# Auto-accept warn-level (still refuses block-level).
npmguard install --yes some-fresh-packageSample output, real live verdict against the registry:
npmguard lodahs@0.0.1-security → score 115 / 200 (block, thresholds warn=30 block=70)
10 pts SoleMaintainer single maintainer: adam_baldwin
25 pts Typosquat name 'lodahs' is 1 edit away from popular package 'lodash'
80 pts KnownCve 1 CONFIRMED MALICIOUS by OSV for this version: MAL-2025-25502
blocked: refusing to install lodahs (score 115 ≥ block threshold 70)All flags:
Flag | Default | Meaning |
|
| Emit verdict as JSON instead of colored text |
|
| Disable ANSI color escapes |
|
| Skip the local SQLite verdict cache |
|
| Increase log verbosity |
|
| Auto-accept warn-level verdicts; block-level still refuses |
Quickstart: MCP
npmguard-mcp speaks the Model Context Protocol over stdio. Once the binary is on your PATH, register it with your agent using the instructions for your tool below.
Claude Code
Use the official claude mcp add CLI. No manual JSON editing required:
# Adds npmguard to your user-level config (~/.claude.json), available in all projects.
claude mcp add --transport stdio --scope user npmguard -- /usr/local/bin/npmguard-mcpVerify it registered:
claude mcp listTo scope it to a single project instead (stored in .mcp.json in the project root, safe to commit):
claude mcp add --transport stdio --scope project npmguard -- /usr/local/bin/npmguard-mcpReference: Claude Code MCP docs
Cursor
Add an mcpServers block to your config. Use the global file to enable it in every workspace, or the project file to scope it to one repo.
Global (~/.cursor/mcp.json):
{
"mcpServers": {
"npmguard": {
"command": "/usr/local/bin/npmguard-mcp"
}
}
}Project-scoped (.cursor/mcp.json in the repo root):
{
"mcpServers": {
"npmguard": {
"command": "/usr/local/bin/npmguard-mcp"
}
}
}Edit the
commandpath if you installed to a location other than/usr/local/bin/.Reference: Cursor MCP docs
Codex CLI
Codex supports stdio MCP servers via ~/.codex/config.toml. Add a [mcp_servers.npmguard] table:
[mcp_servers.npmguard]
command = "/usr/local/bin/npmguard-mcp"To scope it to a single project, create .codex/config.toml in the repo root with the same block (only works in projects Codex has marked as trusted).
Edit the
commandpath if you installed elsewhere.Reference: Codex MCP docs
What the MCP tool returns
npmguard-mcp exposes one tool, install_package(name, version?), returning a structured verdict the model can act on:
{
"package": "lodahs",
"resolved_version": "0.0.1-security",
"level": "block",
"score": 115,
"signals": [
{ "kind": "SoleMaintainer", "points": 10, "detail": "single maintainer: adam_baldwin" },
{ "kind": "Typosquat", "points": 25, "detail": "name 'lodahs' is 1 edit away from popular package 'lodash'" },
{ "kind": "KnownCve", "points": 80, "detail": "1 CONFIRMED MALICIOUS by OSV for this version: MAL-2025-25502" }
],
"recommendation": "Block: do NOT install this package without explicit user override. Present the signals and ask the user to confirm."
}The model gets the recommendation in its own message. So even if the user said "just install whatever you need", the assistant has the structured signal to stop and ask.
Deterministic enforcement (Claude Code hook)
MCP is advisory. The hook is not.
The MCP integration (npmguard-mcp) lets Claude Code ask npmguard before installing a package. It works well: the model sees the risk verdict and can act on it. But it is still advisory. The model decides whether to call the tool, and an inattentive or jailbroken model can skip it.
The hook subcommand is different. Claude Code's harness runs PreToolUse hooks automatically on every Bash tool call before the command executes. The model has no say. It cant skip, bypass, or talk its way around a hook. That's what makes the blocking real rather than aspirational.
Setup (one command)
# Installs into ~/.claude/settings.json, active in every Claude Code project.
npmguard hook install
# Or scope it to a single project (writes .claude/settings.json in the cwd).
npmguard hook install --scope projectThen restart Claude Code (or reload the window). From that point on, every Bash command Claude Code tries to run passes through npmguard hook handle first.
To remove the hook:
npmguard hook uninstall # user-level
npmguard hook uninstall --scope projectWhat the hook does
Claude Code wants to run a Bash command, e.g.
npm install some-package.The harness sends the command to
npmguard hook handlevia stdin (JSON).npmguard parses the command for package-install invocations. Non-install commands (
ls,git,npm run build, etc.) are passed through immediately with no network call.For each detected package, the existing risk engine runs (same signals, same scoring as
npmguard check).npmguard writes a decision JSON to stdout:
block verdict →
deny: Claude Code refuses the command and tells the model why.warn verdict →
ask: Claude Code surfaces the signals and asks the user to confirm.ok verdict →
allow: command proceeds silently.network error →
askwith a caution message. Never a silent allow on an unverified package, never a hard deny on an infra failure.
Honest limitations
Limitation | Status |
Claude Code only. Cursor and Codex do not yet ship a comparable pre-tool hook API. They remain advisory-MCP only until they do. | v1 |
Bare | v1 |
Parser-level evasion is possible. A determined or obfuscated command ( | v1 |
Only npm / yarn / pnpm / bun are recognised. Installs ( | v1 |
Risk signals
Signal | Points | Triggered when |
| 30 | Package defines |
| 25 / 10 | Version published < 7 / 30 days ago |
| 20 | Version published after a > 180-day publish gap (dormant package resurrection) |
| 10 | Package has exactly one maintainer |
| 15 / 10 | Linked GitHub repo is archived / has zero stars and no commits in 6 months |
| 25 | Name is one Damerau-Levenshtein edit from a popular package (catches char swaps like |
| 80 / 50 / 20 / 10 / 5 | OSV.dev advisory present. 80 if it's a |
| 10 | npm registry marks this version deprecated |
| 40 / 30 / 25 / 15 | This version differs from its predecessor in a takeover-shaped way: a newly-added install script (40), an obfuscated high-entropy payload inside an install script (30), a new top-level dependency (25), or > 50% dependency-count growth (15). |
Composite score is the sum (capped at 200). Default thresholds: warn ≥ 30, block ≥ 70. Tunable per project via config (planned for v0.2).
Weights are starting values, not science. They'll be tuned against corpus/ and published as part of each release. PRs welcome.
Versioning & releases
npmguard follows Semantic Versioning. While the project is 0.x:
The CLI is treated as stable for end users. Flag names and exit codes wont change without a major bump.
The MCP tool surface (
install_packageinput/output schema) may make minor additive changes between0.xreleases. Anything breaking is called out in CHANGELOG.md.
Tagged releases publish prebuilt binaries to the Releases page (macOS x86_64 + arm64, Linux x86_64 + arm64, Windows x86_64) via a cross-platform GitHub Actions matrix.
v0.1 is a risk checker + MCP verdict gate. It is not yet a real npm wrapper, installer, or sandbox. npmguard check and npmguard install both produce a verdict; the actual npm install subprocess execution and cross-platform sandbox land in v0.2. See the roadmap below.
Roadmap (full reasoning + considered-and-rejected scope in ROADMAP.md):
Phase | Headline |
v0.1 (shipped) | Risk engine + CLI + MCP server + SQLite cache + GitHub Releases |
v0.2 | Real |
v0.3 | Provenance / signature verification + scanner adapters ( |
v0.4 |
|
v0.5 | Organization presets + MCP marketplace placement (Claude Code, Cursor, Smithery) |
v1.0 | Frozen Rust API, frozen MCP tool schema, frozen JSON output, integration into AI-assistant default docs |
Runtime sandboxing (npmguard run npm test) is deliberately out of scope. See ROADMAP.md § Considered and kept out of scope for the reasoning.
Honesty is the contract.
npmguard cannot prove packages are safe. It can stop known-malicious packages (OSV
MAL-*), flag typosquats, surface lifecycle scripts / package age / maintainer churn, and route AI coding agents through the same verdict. That's it. Everything below is what v1.0 will eventually add. See ROADMAP.md.
What's still missing in v0.1:
Doesn't catch attacks that pass all heuristic checks. The
ReleaseAnomalysignal now diffs each version against its predecessor and flags the common takeover fingerprint: a newly-added install script, a new dependency, or an obfuscated install-script payload. But a takeover that subtly edits the contents of an existing install script without an obvious high-entropy blob, or one that ships malice in plain imported code, can still slip through. The remaining v0.2 answer is wrapping the realnpm installand sandboxing the scripts that do run.Doesn't yet verify npm provenance / package signatures. A historical provenance attestation suddenly missing is a strong takeover signal. That lands in v0.3.
Doesn't yet sandbox lifecycle scripts or wrap the real
npm installsubprocess. v0.1 surfaces the verdict and stops; v0.2 ships the sandbox (landlock / sandbox-exec / Job Object) +--ignore-scriptsenforcement + per-script allow-list.Doesn't protect against malicious code that runs at import time, not install time. If a package is benign at install but evil when imported, this tool isn't in the path. That's a runtime-sandbox problem and is deliberately out of scope. See ROADMAP.md.
Doesn't replace
npm audit, Snyk, Socket, Dependabot, or code review. It's an additional layer. v0.3 adds--with npm-audit/--with osv-lockfile/--with socketadapters so npmguard becomes the policy gate on top of them rather than a redundant scanner.Is not a guarantee. Any tool claiming "secure" is lying. This one says "reduces blast radius" and stops there.
Contributing
PRs and issues welcome. The short version:
cargo fmt --all
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
NPMGUARD_CORPUS=1 cargo test --test corpus -- --nocapture # live network testOpen the PR once that's clean.
License
MIT. See LICENSE.
About the author
Ayoub Tadlaoui (Atlas Kaisar). From Morocco, building software since 2016.
"High performance knows no part-time commitment."
Available Tools
1 toolinstall_packageA
Evaluate the risk of an npm package before installing it. Returns a structured verdict (ok/warn/block) with the signals that triggered. Use this BEFORE calling npm install for any package. If level is 'block', do not install without explicit user approval.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | npm package name. Scoped packages like `@scope/name` are supported. | |
| version | No | Optional pinned version. If omitted, the latest version is evaluated. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the burden. It describes the return value (structured verdict with signals) and implies read-only behavior by stating it evaluates risk. It lacks details like registry interaction or latency, but is otherwise transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise (two sentences plus a conditional instruction) with no redundant information. Every sentence adds value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity (2 parameters, no complex nesting, no output schema), the description adequately covers purpose, usage, and behavior. It could mention the exact structure of 'signals' but is complete enough for an agent to use correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so both parameters are described in the input schema. The description adds minimal value beyond repeating the schema's parameter descriptions. Thus a baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states that the tool evaluates npm package risk and returns a structured verdict (ok/warn/block). It specifies the action (evaluate before installing) and distinguishes itself from any potential installation tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs to use before 'npm install' and provides a conditional rule: if level is 'block', do not install without user approval. This provides clear when-to-use and follow-up guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
v0.1.7- Added
install_package
1 tool update
v0.1.3- Removed
install_package
1 tool update
v0.1.2- First observed
install_package
TDQS
Scored across 1 tool
There is only one tool, so there is zero possibility of selecting the wrong tool. Its purpose is clearly defined and distinct from any nonexistent alternatives.
The single tool name 'install_package' follows a clean verb_noun pattern. Although the name implies installation rather than evaluation, it is consistent and predictable within this one-tool set.
The server has just one tool, which feels thin for a typical server and borders on the minimum viable scope. While the tool covers a specific need, it does not reach the 3-15 tool range that indicates a well-populated set.
For the stated purpose of evaluating npm package risk, the tool fully covers the single required action. Minor gaps include lack of support for batch evaluation or detailed policy management, but the core lifecycle of 'check before install' is complete.
Maintenance
Related MCP Connectors
Blocks typosquatted or hallucinated npm/PyPI packages before an AI agent installs them.
check-package: block malicious npm/PyPI deps before your AI agent installs them. Free, no key.
x402-gated safety checker for npm/PyPI packages before you npm install / pip install.
Protects AI coding agents from installing malicious open source packages. Every npm and PyPI package is checked against SafeDep’s real-time threat intelligence before installation.
Related MCP Servers
- AlicenseAqualityDmaintenanceMCP server to search npm packages, view details, compare, check downloads, and inspect dependencies.621MIT
- AlicenseNot gradedqualityCmaintenanceAudits npm packages for supply-chain attacks (typosquatting, malicious install scripts, credential exfiltration) before installation, returning a SAFE/SUSPICIOUS/DANGEROUS verdict.MIT
- AlicenseNot gradedqualityCmaintenanceScans generated JavaScript/TypeScript code to detect AI-hallucinated or suspicious npm package imports before installation, providing a verdict of CLEAN, REVIEW, or BLOCK.41MIT
- AlicenseNot gradedqualityDmaintenanceAudits your package-lock.json for supply-chain attacks before install. Cross-checks every resolved entry against the live npm registry to detect integrity mismatches, new install scripts, and other malicious signals.MIT