The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Whichlib listing page.
The dependency picker for coding agents. Ask which library to use and get a scored, verified answer instead of a guess.
whichlib is an MCP server with three tools (recommend_repos,
compare_repos, trending_repos) and a free dashboard, Fresh Repos, that
shows the most-starred GitHub repositories created in the last day, week and
month. Every repository gets a transparent 0–100 score from momentum,
maintenance, adoption (stars, forks, npm and PyPI downloads) and license,
plus a one-line verdict.
Agents: see MCP server below for the one-line install.
Dashboard:
whichlib/dashboard/index.html.That is all. The page is a single HTML file that calls the GitHub Search API straight from your browser. No build step, no server, no account.
Optional: paste a GitHub token under Settings on the page to raise the API limit from 10 to 30 requests per minute. A fine-grained token with no permissions is enough. It stays in your browser's local storage.
Every repo gets a score from 0 to 100, a tier and a one-line verdict. The
breakdown is always returned so a person or an agent can see why. The same
file, whichlib/lib/score.js, runs in the dashboard and in Node, so
the two can never disagree.
| Part | Weight | Signal |
|---|---|---|
| Momentum | 40% | Stars gained over the last 7 days from our snapshots. Without history, stars per day since creation times 7, with age floored at one day. Log scale: 50 a week is already good, 5,000 is the max. |
| Maintenance | 25% | Days since last push: full marks up to 30 days, zero at 365, linear between. Minus 0.2 when open issues exceed a tenth of the stars. Stability guard: a repo with 10k+ stars or 100k+ weekly downloads, pushed within the last year and not archived, never drops below 0.5 here. Heavy use plus silence is stability, not decay. |
| Adoption | 25% | With weekly downloads known: 50% stars (max 100k), 20% forks (max 20k), 30% downloads (max 1M). Otherwise 70% stars, 30% forks. All log scale. |
| License | 10% | Permissive 1.0, weak copyleft 0.75, strong copyleft 0.5, unrecognised 0.5, none 0. |
Tiers: Strong 75 and above, Solid 50, Watch 25, Avoid below 25. The names are chosen to read correctly for a six-week-old project and a six-year-old library alike. Archived repos are capped at 20 and get the verdict "Archived, avoid." A missing license is always named in the verdict.
Verdicts read like "Rising fast, 10.6k downloads/wk, pushed 2 days ago, MIT", "Gaining steadily, 145M downloads/wk, quiet for 6 months, widely used, BSD-3-CLAUSE" or "Slow growth, no push in 60 days, GPL-3.0".
GitHub has no download count for repositories, but package registries do. After each snapshot, the enrich step maps JavaScript and TypeScript repos to npm and Python repos to PyPI, then fetches last week's downloads:
github.com/<owner>/<repo>. A matching name alone is never enough,
so a new repo called widget is not credited with the downloads of an
unrelated widget package.<repo> and @<owner>/<repo> on npm, <repo> on PyPI.registry-map.json on the data branch. Negatives
are re-checked after 7 days, positives kept, downloads refreshed daily.Other languages (Rust, Go, Java...) are skipped for now. Cargo, Go and Maven can follow the same pattern.
Caveat: opened from disk, the dashboard has no snapshot history, so momentum uses the fallback. Scores on the Today tab are therefore provisional; the report and the MCP server use real stars-gained figures once there are two or more days of snapshots.
The same score, served to coding agents. Three tools over stdio:
| Tool | Input | What it returns |
|---|---|---|
recommend_repos | need in plain words, optional language, limit (1–10, default 5) | The best repositories for the need, ranked by fit (score × relevance), with npm/PyPI downloads and a verdict each. Candidates come from GitHub's relevance order, its stars order and a topic query; see "How recommend finds and ranks candidates" below. |
compare_repos | repos: 2–10 names as owner/repo | The repositories side by side, best first, same breakdown. |
trending_repos | period day/week/month, optional language, limit (default 20), withDownloads | Most-starred repos created in the period, scored. |
Every result carries readable text and structuredContent (JSON) with the
score, tier, verdict, the four subscores, flags, packages and downloads.
Requires Node 22 or newer. Install into Claude Code (-s user makes it
available in every project):
Or as a Claude Code plugin, which adds a skill that makes Claude check a library with whichlib before adding it:
Cursor, Windsurf, Claude Desktop and others take the same command in their MCP config:
To run from a clone instead: node whichlib/mcp/server.mjs.
Environment variables, both optional:
GITHUB_TOKEN raises GitHub's limits (search 10 to 30 per minute). A
fine-grained token with no permissions is enough. Recommend makes three
searches per call, so without a token it allows about three recommendations
per minute.FRESH_REPOS_DATA_DIR points at a folder of daily snapshots. The default is
whichlib/data/snapshots, filled by npm run pull-data. With two or
more days present, momentum uses real 7-day stars gained.WHICHLIB_TELEMETRY=off or DO_NOT_TRACK=1 disables anonymous call
counting. What is counted: tool name, a random install id, version,
platform and Node major version. Never queries, repository names or
results. The collector is a small Cloudflare Worker in telemetry/, and
its aggregate numbers are public at
https://whichlib-telemetry.todorovskijosif.workers.dev/stats.Try it without a client:
Known bias, reduced: maintenance used to drop to zero at 90 days without a push, which put httpx (145M weekly downloads, six quiet months) in "Watch". The curve now runs to a year and the stability guard keeps widely used repos at 0.5 or better; httpx lands in "Solid". Release cadence from the GitHub releases API is the proper long-term signal and is still to come.
mcp/eval/needs.json holds 20 needs ("pdf parser" in Python, "state
management" in TypeScript, ...) each with a set of accepted answers a senior
engineer would consider reasonable. npm run eval runs them through
recommend_repos live and reports how often an accepted repo appears at
rank 1, 3 and 5, for our ranking and for baselines built from the same
candidate pool. Reports land in mcp/eval/results/.
Result on 2026-09-27, after query expansion (second report in results/):
| Ranking | hit@1 | hit@3 | hit@5 | MRR |
|---|---|---|---|---|
| ours (fit, see below) | 75% | 95% | 100% | 0.85 |
| GitHub relevance order | 65% | 80% | 95% | 0.76 |
| stars order | 45% | 65% | 75% | 0.56 |
| score only, no relevance | 30% | 65% | 70% | 0.46 |
The first report, before expansion, had the same hit rates for our ranking (75 / 95 / 100, MRR 0.86) on a smaller pool. Expansion raised recall from 53 to 74 accepted repos across the 20 pools, never fewer on any need, and the baselines fell on that noisier pool while ours held. The fit rules are what keep the noise out.
Retrieval, three GitHub searches per need:
async OR asynchronous runtime), so vocabulary differences stop hiding
libraries like tokio.topic:cli, topic:image-processing),
which surfaces what maintainers tagged themselves. The head word is used
when it is specific (pdf, cli, orm) and the hyphenated phrase when it is
broad (image-processing, state-management). GitHub rejects OR between
topics, so it is one per request.Language filters use families: JavaScript includes TypeScript and Python includes Jupyter, because many libraries moved to TypeScript.
Ranking key is fit = score × relevance:
Both score and fit are returned, with the relevance rank, the sources the
repo came from and the two signals, so an agent can see why.
node mcp/eval/inspect.mjs "<need>" [language] [wanted/repo ...] prints the
whole candidate pool for one need with these values.
whichlib/snapshot/ is a zero-dependency Node 22 script that stores the
top 100 repos for 3 periods times 9 languages into
whichlib/data/snapshots/YYYY-MM-DD.json. Consecutive snapshots are
what a momentum score needs.
A GitHub Actions workflow (.github/workflows/snapshot.yml)
runs the job every day at 06:17 UTC and commits the result to the data
branch, so history accumulates without bloating main. Trigger it by hand
from the Actions tab or with gh workflow run snapshot. Bring the files down
locally with:
A Windows Task Scheduler alternative is in
whichlib/README.md.
server.json for the MCP registry and the
call counter are ready; remaining: deploy the counter, make the
repository public, npm publish, mcp-publisher publish, list in the
Claude Code plugin marketplace and the awesome-mcp lists.MIT.