npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dep-rec

v0.6.1

Published

Recommend the best-fit npm package for a described need, or check any arbitrary package's health/security live — as a CLI or an MCP server for AI assistants.

Readme

An assistant suggests a package; something has to check it against reality before it lands in your package.json. dep-rec answers from the live npm registry and OSV with a deterministic score — never from a model's memory of what was popular when it was trained.

Runs as an MCP server an assistant calls mid-conversation, and as a plain CLI for humans, scripts and CI.

Features

  • Catches hallucinated packages — a real registry 404, reported as such
  • Catches typosquats that resolvecsvparser vs csv-parser, the attack that a 404 check misses entirely
  • Version-scoped CVE checks — OSV queried against the current version, so long-patched advisories aren't reported as live ones
  • Flags install scriptspreinstall/install/postinstall, the event-stream attack vector
  • Refuses to recommend deprecated, barely-adopted, brand-new or unmaintained packages, and says which bar was missed
  • Pins a version, not just a name — the newest stable release that has been public 30+ days, so nothing installs a package published this morning
  • Says "unknown" instead of "clean" when a lookup fails
  • Covers all of npm — ten curated categories, everything else discovered by live search
  • Zero dependencies

Install

npm install --save-dev dep-rec

Node 18.3+. Zero dependencies, nothing to compile.

Quick start

npx dep-rec check csvparser
🚨 csvparser  score 45.1  [cache]
  TYPOSQUAT WARNING: Name is one character away from "csv-parser" (3,288,172 weekly downloads vs. this package's 86) — classic typosquat pattern. Verify this is really the package you meant before installing.
  license: unknown  maintainers: 1  deps: 0  weekly downloads: 86
  - no known vulnerabilities affecting the current version
  - no recent release relative to category peers
  - no ESM or bundled TS types detected
  - zero direct dependencies
  recommended version: 0.1.4 (= latest)
    0.1.4 was released 4562 days ago, past the 30-day observation window — no cooldown concern.
  NOT RECOMMENDATION-GRADE — this tool would not put this package forward:
    - only 86 weekly downloads (recommendation floor is 10,000)
    - no release in 1504 days (limit is 1000 at this adoption level)
npx dep-rec check left-pad              # is this safe to install?
npx dep-rec recommend "jwt library"     # what should I use?
npx dep-rec recommend "pdf" --limit 3   # works outside the curated set too
npx dep-rec categories

Scores move with live data, so your numbers won't match the sample exactly — and [cache] reads [live-fetch] the first time a package is seen.

Use with an AI assistant

Add to your assistant's MCP config — Claude Code's .mcp.json, Cursor's mcp.json, or equivalent:

{
  "mcpServers": {
    "dep-rec": {
      "command": "npx",
      "args": ["-y", "-p", "dep-rec", "dep-rec-mcp-server"]
    }
  }
}

The -p dep-rec matters: the binary is dep-rec-mcp-server but the package is dep-rec, so a bare npx -y dep-rec-mcp-server would look for a package by that name and 404. Already installed in the project? Use "command": "node" with ["node_modules/dep-rec/dist/server.js"] instead.

Then the conversation goes differently:

You: I need to parse CSV in this project.

Assistant (calls recommend_package): [email protected] — score 93.2, no known vulnerabilities affecting the current version, ships ESM and TypeScript types. Pinned to 7.0.0 rather than 7.0.1, which is 24 days old. 40 alternatives were excluded for failing the recommendation bar.

You: What about csvparser?

Assistant (calls check_package): Don't. It resolves, but it has 86 weekly downloads and its name is one character from csv-parser (3.3M/week) — that's the typosquat pattern.

Tools

| Tool | Answers | Inputs | |---|---|---| | check_package | "Is this specific package safe to install?" Any package on npm. | name | | recommend_package | "What should I use for X?" | need, runtime?, license?, limit? | | list_categories | Curated categories and how many packages clear the bar | — |

check_package is the stronger half and the one to reach for by default: verifying a name an assistant already produced is a well-defined question. "What should I use for X" is not.

What the model receives

Real output from check_package on csvparser, unedited:

{
  "name": "csvparser",
  "found": true,
  "inCuratedDataset": true,
  "servedFrom": "cache",
  "score": 45.1,
  "reasons": [
    "no known vulnerabilities affecting the current version",
    "no recent release relative to category peers",
    "no ESM or bundled TS types detected",
    "zero direct dependencies"
  ],
  "npm": {
    "license": null,
    "maintainers": 1,
    "latestVersion": "0.1.4",
    "dependenciesCount": 0,
    "deprecated": false
  },
  "weeklyDownloads": 86,
  "versionGuidance": {
    "recommendedVersion": "0.1.4",
    "reason": "0.1.4 was released 4562 days ago, past the 30-day observation window — no cooldown concern."
  },
  "typosquatWarning": {
    "targetPackage": "csv-parser",
    "targetWeeklyDownloads": 3288172,
    "reason": "Name is one character away from \"csv-parser\" (3,288,172 weekly downloads vs. this package's 86) — classic typosquat pattern. Verify this is really the package you meant before installing."
  },
  "recommendable": false,
  "notRecommendableBecause": [
    "only 86 weekly downloads (recommendation floor is 10,000)",
    "no release in 1504 days (limit is 1000 at this adoption level)"
  ],
  "note": "In a known category — this score is ranked against real category competitors (see recommend_package for the full comparison)."
}

Those two day counts disagree on purpose, and the disagreement is a known defect rather than a subtlety: versionGuidance reads the actual publish date of 0.1.4 (2014-01-28), while the eligibility gate reads the registry's modified timestamp (2022-06-14), which moves on any metadata change and is a poor proxy for "last release". The gate understates this package's staleness by eight years. It still excludes it, so the verdict is right for the wrong reason — tracked in reviews and hardening.

Note what's absent: no bare boolean verdict to misread. score never appears without reasons; recommendable: false never without notRecommendableBecause. And deprecated: false sits right next to recommendable: false, because those are different claims and the response shape shouldn't let a model merge them.

When a lookup fails it returns a reasonCode, and only one of them is an accusation:

| code | meaning | |---|---| | not-found | real 404 — treat as possibly hallucinated or typosquatted | | invalid-name | malformed; the registry was never asked | | registry-error | npm was unreachable; says nothing about the package | | no-manifest | resolves, but nothing installable |

CLI reference

dep-rec check <package-name> [--json] [--db <path>]
dep-rec recommend "<need>" [--runtime node|edge] [--license MIT] [--limit 1-20] [--json] [--db <path>]
dep-rec categories [--json] [--db <path>]

| Flag | Effect | |---|---| | --json | Machine-readable output instead of the human-readable default | | --limit <1-20> | How many candidates to return (default 5) | | --runtime node\|edge | Target runtime; edge attaches an explicit not-sandbox-tested caveat | | --license <id> | Exact license match, e.g. MIT | | --db <path> | Use a different store file (also DEP_REC_DB_PATH) |

check exits non-zero when a package can't be resolved, so a hallucinated name in a PR fails the build.

How it scores

Five weighted signals, all from live data:

| Signal | Weight | What it measures | |---|---|---| | Security | 25% | OSV vulnerabilities scoped to the current version | | Maintenance | 25% | Release recency and cadence | | | | (maintainer count is reported but deliberately not scored — see below) | | Compatibility | 20% | ESM and bundled TypeScript types | | Community | 20% | Downloads relative to real category competitors | | Supply chain | 10% | Dependency count, and install scripts weighted heavily |

Plus typosquat detection on names that resolve (edit-distance-1 against popular packages, restricted to typo-shaped edits so next and nuxt aren't accused of each other).

Which version you get

Both tools return a recommendedVersion, and it is not always latest.

The default is the newest stable release that has been public for at least 30 days. A version published this week has had nobody looking at it, and that window is exactly when a compromised publish is most likely to still be live — the event-stream and ua-parser-js payloads were both pulled within days. Waiting out the window costs a few patch releases and removes the part of the risk curve you can't inspect.

Three things keep that from becoming its own kind of bad advice:

  • A fresher release wins if it demonstrably fixes something. Not inferred from recency — both versions get a version-scoped OSV lookup, and latest is only preferred when the older one is confirmed affected and it isn't.
  • Prereleases are never the "safe" pick. Projects with an active rc train publish constantly, and the newest thing past a 30-day window is often a release candidate. Offering an rc as the battle-tested option inverts the whole idea.
  • A major-version gap is stated, not buried. On the 499-package corpus, 141 packages had a latest inside the window and 11 of those resolved to an older major — a different API, not just an older build. Those come back with an explicit caution that installing latest may be what you actually want.

Selection is by semver, not publish date. Walking the publish history backwards picks up backports: for undici that chose 6.27.0, a patch to the old line released after 8.x shipped, when 8.5.0 was both newer and past the window. Three of the 141 hit that.

The window is measured against today, not against whenever the data was collected. That matters more than it sounds: an answer resolved once and frozen keeps holding a release back long after that release has aged past 30 days by itself. What's stored is the decision and the candidates' release dates, not a rendered sentence, so a held-back package walks forward on its own as each version ages in and converges on latest:

today   [email protected]   (7.3.0 is 4 days old)
+10d    [email protected]   (7.1.6 has now cleared the window, and is higher)
+15d    [email protected]
+30d    [email protected]   ← latest cleared it too; nothing held back any more

When a version overtakes the pick purely by ageing, it says so rather than reusing the older version's OSV result — nothing checked the new one.

Of the 499 seeded packages, 131 currently resolve to something older than latest.

Maintainer count is reported, not scored. It used to be a third of the maintenance sub-score and printed "single maintainer — bus-factor risk". Measured across the corpus, that flagged 106 of 307 recommendable packages — including zod (239M weekly downloads), axios (118M), date-fns (94M), jose (102M) and hono (52M). The tool was recommending those and warning about them in the same breath. Median score by maintainer count wasn't even monotonic (1 → 71.9, 2 → 73.6, 3 → 80.5, 4 → 75.2, 5+ → 75.9): noise, not signal. The underlying reason is that npm's maintainers field counts publish rights, not people — an org releasing through one CI account reports one, and so can a project with hundreds of contributors. The count is still in npm.maintainers for anyone who wants to weigh it; it just isn't treated as a verdict. Real bus-factor analysis needs contributor distribution from the repository host, which is a documented gap rather than something to approximate badly.

A package can be answered for and still not be recommendable. Deprecated, under 10k weekly downloads, under 30 days old, or no release in ~1000 days disqualifies it from being put forward, while check_package still answers for it in full. Refusing to answer for obscure packages would defeat the point; recommending them would too.

Coverage

Ten curated categories — auth-jwt, csv-parsing, date-time, http-client, logging, orm-database, state-management, testing, validation, web-framework — are ranked by percentile against a real competitor set.

Anything else is discovered live: dep-rec searches npm, fetches each candidate, runs the identical checks, and returns what clears the bar, marked discoveredVia: "npm-search".

$ dep-rec recommend "pdf"
1. @react-pdf/pdfkit  81.4     2. @react-pdf/renderer  90.8
3. pdf-merger-js      80.8     4. ng2-pdf-viewer       69.6

Discovered results keep npm's relevance ordering rather than being re-ranked by score — sorting by health promoted internal sub-packages nobody installs above the obvious answer. Relevance is npm's job; screening for security, maintenance and adoption is this tool's. The first call for a new domain is slow; every candidate is stored, so the next one is served from disk.

Why "AI-first"

Not a security scanner with an MCP wrapper bolted on. The design assumes the caller is a model, and that changes most decisions:

  • It refuses rather than guesses. A plausible answer to a question the tool can't answer is worse than useless, because the model can't tell the difference. recommend "pdf" once returned vitest, jose, hono, xstate, zod — ranked, scored, explained, completely wrong. Now an uncovered need is either answered from live search or not answered.
  • Every miss says why it missed. Collapsing the four reasonCode values into one is how a tool tells a model that every real package is fake during an npm outage.
  • Scores are deterministic; the model only narrates. Both tool descriptions instruct it to cite the returned reasons and never invent a score.
  • Unverified is never reported as clean. A failed OSV lookup returns unknown, not "no known vulnerabilities". Same for adoption data.
  • Output is bounded. When the consumer is a context window, response size is a correctness property. One corrupt row produced a 216 KB response before that cap existed.
  • The tool descriptions are part of the product — long on purpose, and covered by tests.

Limitations

Documented rather than hidden, because a tool that overstates itself is the problem it's trying to solve:

  • recommend_package is weaker than check_package. Generic queries can surface vendor-internal libraries that score well but no human would pick (http clientgaxios). The category matcher is a keyword map, not semantic search.
  • Abandoned-but-popular packages slip through. 6 of 24 tested still clear the bar, including colors — sabotaged by its own maintainer in 2022 — because none are formally deprecated and all still have large download counts.
  • 24-hour cache window. A CVE disclosed in the last day can be masked on a cached lookup; servedFrom discloses the provenance.
  • No GitHub signals (stars, bus factor, archived status) and no transitive dependency analysis — direct dependency count only.
  • Edge Runtime fit is not sandbox-tested. runtime: "edge" returns an explicit caveat rather than answering from ESM support alone.
  • npm only. No PyPI, crates.io or Maven.
  • Concurrent writes serialise on one file lock. The store is a single JSON document replaced atomically, so simultaneous writers queue rather than merge. Measured at 32 concurrent writing processes: no failures, but p50 latency reaches ~11s at that level. Reads never block.

Documentation

  • Architecture — the persistent store, scoring internals, the eligibility gate
  • Development — running from source, npm link, packing
  • Reviews and hardening — every external review finding, the stress harness, and the bugs both found
  • Tests — 447 adversarial scenarios across seven suites

License

MIT © Ashish Singh · github.com/rigsutra/dep-rec