OSV Dependency Vulnerability Check MCP Server
by fairquery
README.md
# Dependency Vulnerability Check for Agents
**Fairquery** Apify Actor that checks open-source dependencies against the free **[OSV.dev](https://osv.dev)** vulnerability database and returns agent-ready rows: one per package (vulnerable or not) and one per vulnerability (CVE, CVSS, severity, affected/fixed versions, references).
## When to use this Actor (LLM / tool selection)
Use it when an agent needs to:
- Answer "is `lodash@4.17.20` / `django==3.2.0` vulnerable, and to what version should I upgrade?"
- Audit a pasted **package.json, package-lock.json, requirements.txt, or go.mod**
- Filter to **HIGH/CRITICAL** issues only for a CI gate or PR comment
- Map OSV / GHSA / PYSEC / GO IDs to **CVE IDs** and CVSS scores
Supported ecosystems: everything OSV supports (npm, PyPI, Go, Maven, crates.io, RubyGems, NuGet, Packagist, Pub, Hex, …). Aliases like `pip`, `golang`, `cargo`, `composer` are accepted.
Do **not** use it for: reachability analysis, license checks, container/OS image scanning, or unpinned version ranges (it checks exact versions).
## Data sources
| Resource | URL |
| --- | --- |
| Batch query | `POST https://api.osv.dev/v1/querybatch` |
| Vulnerability detail | `GET https://api.osv.dev/v1/vulns/{id}` |
Free, no API key. Retries on 429/5xx with exponential backoff; detail fetches are cached and run 8 in parallel.
## Input
| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `packages` | `{ecosystem,name,version}[]` | `[]` | Exact package versions to check |
| `manifestText` | `string` | — | Raw package.json / package-lock.json (v1–v3) / requirements.txt / go.mod |
| `manifestType` | enum | `auto` | Force the manifest format |
| `minSeverity` | `ALL`\|`LOW`\|`MEDIUM`\|`HIGH`\|`CRITICAL` | `ALL` | Minimum severity of emitted vulnerability rows |
| `includeUnknownSeverity` | `bool` | `true` | Keep vulns with no severity data when filtering |
| `includeDevDependencies` | `bool` | `true` | Include dev deps from npm manifests |
| `dedupeAliases` | `bool` | `true` | Collapse GHSA/PYSEC/GO records describing the same issue into one row (GHSA preferred) |
| `maxPackages` | `int` | `1000` | Safety cap on unique packages |
Manifest notes: `package.json` ranges are approximated to their lower bound (`^4.17.20` → `4.17.20`) and a `RANGE_APPROXIMATED` warning row is emitted — prefer the lockfile. `requirements.txt` lines without `==` pins are skipped with a `VERSION_NOT_PINNED` warning (use `pip freeze`).
### Example input
```json
{
"packages": [
{ "ecosystem": "npm", "name": "lodash", "version": "4.17.20" },
{ "ecosystem": "PyPI", "name": "django", "version": "3.2.0" }
],
"minSeverity": "HIGH"
}
```
## Output
Every row has `schema_version` (`1.0.0`), `type` (`package` | `vulnerability` | `error`), `source_url` (OSV API URL), and `checked_at`.
### Package row
```json
{
"type": "package", "ecosystem": "npm", "name": "lodash", "version": "4.17.20",
"purl": "pkg:npm/lodash@4.17.20", "vulnerable": true, "vuln_count": 3, "vuln_count_total": 5,
"vuln_ids": ["GHSA-r5fr-rjxr-66jc", "GHSA-35jh-r3h4-6jhm", "GHSA-29mw-wpgm-hmr9"],
"max_severity": "HIGH", "max_cvss_score": 8.1,
"fixed_versions": ["4.18.0", "4.17.21"], "recommended_version": "4.18.0"
}
```
`vuln_count` is after the severity filter and alias de-duplication; `vuln_count_total` is the raw OSV count. `recommended_version` is the smallest version that fixes every emitted vuln (best-effort, null if some vuln has no fix).
### Vulnerability row
Fields: `id`, `package_name`, `package_version`, `aliases`, `cve_ids`, `summary`, `details` (≤2000 chars), `severity`, `cvss_score` (computed CVSS v3 base score), `cvss_vector`, `cvss_version`, `severity_source` (`cvss` | `database_specific` | `alias` | `none`), `severity_alias_id`, `cwe_ids`, `affected_ranges[]` (`introduced`/`fixed`/`last_affected`), `affected_versions_sample`, `fixed_versions`, `published`, `modified`, `references[]`, `osv_url`.
Severity: CVSS v3 base score → NONE/LOW/MEDIUM/HIGH/CRITICAL; otherwise the advisory's label (GHSA `MODERATE` → `MEDIUM`); otherwise borrowed from a GHSA alias; otherwise `UNKNOWN` (common for some Go advisories).
### Error / warning rows (machine-readable)
```json
{ "type": "error", "code": "OSV_VULN_FETCH_FAILED", "message": "OSV /vulns/GHSA-… -> HTTP 503", "retryable": true, "vuln_id": "GHSA-…" }
```
Codes: `INPUT_EMPTY`, `INVALID_PACKAGE`, `MANIFEST_UNDETECTED`, `MANIFEST_PARSE_FAILED`, `MAX_PACKAGES_EXCEEDED`, `OSV_QUERYBATCH_FAILED`, `OSV_VULN_FETCH_FAILED`; warnings (with `context.severity = "warning"`): `RANGE_APPROXIMATED`, `VERSION_NOT_PINNABLE`, `VERSION_NOT_PINNED`.
### Run summary
Key-value store record **`RUN_SUMMARY`**: packages checked/vulnerable, vulnerabilities emitted/filtered/deduplicated, severity counts, OSV request count, echoed input.
## Pricing (pay per event)
| Event | Price | When |
| --- | --- | --- |
| `apify-actor-start` | $0.00005 | Once per run (Apify default start event) |
| `package-checked` | $0.0009 | Each package row (vulnerable or clean) |
| `vulnerability-found` | $0.0028 | Each emitted vulnerability row (after severity filter & alias de-dup) |
Error and warning rows are **never** charged. Example: lodash + django (2 packages, 41 vulnerabilities) ≈ $0.117. Set a max charge per run in run options to cap spend; the Actor stops cleanly at the limit.
## Memory / runtime
Default **256 MB**, timeout 600 s. HTTP only (no browser). A 2-package run finishes in ~5–10 s.
## MCP / Apify tools
Call from agents via [Apify MCP](https://mcp.apify.com):
1. Connect an MCP client to `https://mcp.apify.com` with your Apify token.
2. Run actor `fairquery/osv-dependency-vulnerability-check`.
3. Read the default dataset (filter `type == "vulnerability"`) and the `RUN_SUMMARY` record.
```json
{
"actor": "fairquery/osv-dependency-vulnerability-check",
"input": { "manifestText": "django==3.2.0\nrequests==2.25.0", "minSeverity": "HIGH" }
}
```
## Local stdio MCP server
This repo also ships a **stdio MCP server** (TypeScript, `@modelcontextprotocol/sdk`) under [`mcp/`](./mcp). It exposes one tool, `check_dependency_vulnerabilities`, which runs this Actor through the Apify API. `initialize` and `tools/list` work **without** a token; tool calls require `APIFY_TOKEN` (calls are billed per the pricing above).
```bash
cd mcp && npm install && npm run build
node scripts/smoke-test.mjs # initialize + tools/list, no token
APIFY_TOKEN=... node scripts/smoke-test.mjs --call # real tool call
```
Claude Desktop / Cursor config:
```json
{
"mcpServers": {
"fairquery-osv": {
"command": "node",
"args": ["/path/to/osv-dependency-vulnerability-check/mcp/dist/index.js"],
"env": { "APIFY_TOKEN": "<your Apify token>" }
}
}
}
```
Docker: `docker build -f Dockerfile.mcp -t fairquery-osv-mcp .` (build context = repo root; also the Dockerfile to paste into Glama). `glama.json` lists `fairquery` as maintainer.
## Links
- Apify Store: https://apify.com/fairquery/osv-dependency-vulnerability-check
- GitHub: https://github.com/fairquery/osv-dependency-vulnerability-check
- License: MIT
## Local development
```bash
npm install
# put input in storage/key_value_stores/default/INPUT.json
npx apify run
npm test
```
## Disclaimer
Data comes from OSV.dev (aggregating GitHub Advisory Database, PyPA, Go Vulnerability DB and others) under their respective licenses. No warranty of completeness; absence of results does not prove a package is safe.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues