CVE MCP Server
by Liohtml
README.md
<div align="center">
<img src="assets/banner.svg" alt="CVE MCP Server — security intelligence for Claude and every MCP client" width="100%">
### Ask *“Should we patch CVE-2024-3400?”* — get a scored, evidence-backed verdict in seconds.
[](https://github.com/Liohtml/cve-mcp-server/actions/workflows/ci.yml)
[](CHANGELOG.md)
[](https://www.rust-lang.org)
[](https://modelcontextprotocol.io)
[](LICENSE)
[](#-docker--http)
**[Quick start](#-quick-start)** · **[Demos](#-see-it-in-action)** · **[Tools](#-tools)** · **[Scoring](#-how-the-verdict-is-made)** · **[Configuration](#%EF%B8%8F-configuration)** · **[Architecture](#%EF%B8%8F-architecture)**
<img src="assets/demo-triage.gif" alt="Terminal recording: cve-mcp call triage_cve for CVE-2021-44228 returns a 100/100 CRITICAL verdict with CVSS, EPSS, KEV, PoC signals and an SSVC 'Act' decision" width="100%">
<sub>Real output of <code>cve-mcp call triage_cve</code> — exactly the text your MCP client receives.</sub>
</div>
---
**CVE MCP Server** is a [Model Context Protocol](https://modelcontextprotocol.io) server that turns Claude (or any MCP client) into a security analyst. Instead of juggling NVD, EPSS, CISA KEV, GitHub, Exploit-DB, Shodan and VirusTotal in fifteen browser tabs, you ask one question — the server fans out to every relevant source in parallel, correlates the evidence and answers with a prioritised recommendation.
It ships as **one self-contained Rust binary**: no Python, no Node, no runtime to install.
## ✨ Highlights
<table>
<tr>
<td width="33%" valign="top">
**🎯 One-call triage**<br>
`triage_cve` queries NVD, EPSS, CISA KEV and public exploit sources concurrently and returns a verdict — with a VulnCheck fallback when NVD is down.
</td>
<td width="33%" valign="top">
**🧮 Evidence over severity**<br>
A transparent 0–100 score weights *exploitation* (EPSS + KEV) above raw CVSS. KEV-listed CVEs are always **CRITICAL**. Optional SSVC decision.
</td>
<td width="33%" valign="top">
**🔍 29 tools · 23 sources**<br>
CVE intel, exploits & PoCs, ATT&CK, vendor advisories, IP / domain / hash / URL reputation, ransomware wallets, known-good files.
</td>
</tr>
<tr>
<td valign="top">
**📦 Dependency scanning**<br>
Paste a `requirements.txt`, `package.json`, `pom.xml` or OS package list — get the most severe CVEs per package with real severities.
</td>
<td valign="top">
**🦀 Fast & lean**<br>
Single ~11 MB binary, async Tokio core, SQLite cache, starts instantly. Runs over stdio or stateless streamable HTTP.
</td>
<td valign="top">
**🔐 Secure by default**<br>
Outbound host allowlist on every request and redirect, masked secrets, redacted audit log, private-IP blocking, XXE-safe XML.
</td>
</tr>
</table>
## 🚀 Quick start
**1 — Install** (needs [Rust 1.88+](https://rustup.rs) and a C compiler)
```bash
cargo install --git https://github.com/Liohtml/cve-mcp-server --locked
cve-mcp --version
```
Prefer a download? Tagged releases ship prebuilt binaries for Linux, macOS and Windows on the [Releases](https://github.com/Liohtml/cve-mcp-server/releases) page.
**2 — Connect your MCP client**
```bash
# Claude Code
claude mcp add cve-mcp -e NVD_API_KEY=your-key -- cve-mcp
```
<details>
<summary><b>Claude Desktop</b></summary>
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
```json
{
"mcpServers": {
"cve-mcp": {
"command": "/absolute/path/to/cve-mcp",
"env": {
"NVD_API_KEY": "your-key",
"GITHUB_TOKEN": "ghp_..."
}
}
}
}
```
Use the **absolute path** (e.g. `~/.cargo/bin/cve-mcp`, expanded) — Claude Desktop does not inherit your shell `PATH`. Fully quit and restart Claude Desktop afterwards.
</details>
<details>
<summary><b>Any other MCP client</b> (Cursor, VS Code, MCP Inspector, …)</summary>
Register a **stdio** server whose command is `cve-mcp` (no arguments), or point an HTTP-capable client at a running instance: `http://localhost:8000/mcp` (see [Docker / HTTP](#-docker--http)). To poke at it interactively:
```bash
npx @modelcontextprotocol/inspector cve-mcp
```
</details>
**3 — Ask**
> *“Is CVE-2021-44228 actively exploited and how urgent is it?”*<br>
> *“Scan this requirements.txt and tell me what to upgrade first.”*<br>
> *“Compare CVE-2024-3400, CVE-2024-6387 and CVE-2023-38545 — what do we patch first?”*
No API key is required to start. Adding a free [`NVD_API_KEY`](https://nvd.nist.gov/developers/request-an-api-key) makes NVD lookups 10× faster.
## 🎬 See it in action
Every tool can also be run straight from the terminal with `cve-mcp call <tool> key=value …` — handy for scripting and CI. The recordings below are real output.
**Scan dependencies** — the five most severe vulnerabilities per package, with CVE aliases and severities computed from CVSS v2/v3/v4 vectors:
<img src="assets/demo-scan.gif" alt="Terminal recording: cve-mcp call scan_dependencies lists critical and high vulnerabilities for django 3.2.0, pillow 9.0.0, requests 2.19.0 and flask 0.12" width="100%">
**Prioritise a backlog** — rank CVEs by composite risk to decide what gets patched first:
<img src="assets/demo-compare.gif" alt="Terminal recording: cve-mcp call compare_cves ranks five CVEs by risk score, KEV status and EPSS" width="100%">
## 🧰 Tools
### ⭐ Start here
| Tool | What it does |
|---|---|
| **`triage_cve`** | One-call verdict for a CVE: NVD (+ VulnCheck fallback), EPSS, CISA KEV and PoC discovery in parallel → risk score, urgency, recommendation; `depth="deep"` adds an SSVC decision. `depth`: `quick` · `standard` · `deep` |
### 🛡️ Vulnerability intelligence
| Tool | What it does | Key |
|---|---|---|
| `lookup_cve` | Full NVD record: status, dates, CVSS score & vector, CWEs, description, KEV status | NVD (optional) |
| `search_cves` | Keyword search of NVD with optional severity filter (1–50 results) | NVD (optional) |
| `get_cve_summary` | One-page summary: severity, EPSS, KEV, description, weaknesses, timeline | NVD (optional) |
| `get_epss_score` | EPSS exploitation probability & percentile for one or many CVEs | — |
| `check_kev` | CISA Known Exploited Vulnerabilities status, due date, ransomware use | — |
| `parse_cvss` | Score & explain CVSS v2, v3.0/3.1 (base, temporal, environmental) and v4.0 vectors | — |
| `get_cve_timeline` | Publication → EPSS → KEV timeline, patch lag, exploit window | — |
| `get_vendor_advisory` | Advisories from Microsoft MSRC, Red Hat and Ubuntu | — |
| `get_attack_mapping` | MITRE ATT&CK techniques referencing the CVE and the groups using them | — |
| `check_package_vulns` | Known vulnerabilities of one package/version via OSV.dev | — |
### 💥 Exploits, risk & reporting
| Tool | What it does | Key |
|---|---|---|
| `check_poc_exists` | PoC discovery across GitHub, Exploit-DB and Nuclei templates with a confidence ladder | `GITHUB_TOKEN` (optional) |
| `check_exploit_availability` | Public PoC repositories on GitHub, sorted by stars | `GITHUB_TOKEN` (optional) |
| `calculate_risk_score` | Composite 0–100 score with a per-component breakdown | NVD (optional) |
| `compare_cves` | Rank 2–10 CVEs into a patch-priority table | NVD (optional) |
| `generate_vuln_report` | Executive, technical or full report, optionally with remediation guidance | NVD (optional) |
### 🌐 Network & threat intelligence
| Tool | What it does | Key |
|---|---|---|
| `check_ip_reputation` | Abuse score, ISP, country, Tor flag + GreyNoise classification | `ABUSEIPDB_KEY`, `GREYNOISE_API_KEY` |
| `shodan_host_lookup` | Open ports, services, OS, hostnames and CVEs of a host | `SHODAN_KEY` |
| `get_domain_intel` | Certificate transparency (crt.sh), subdomains, passive DNS | CIRCL (optional) |
| `passive_dns_lookup` | Historical DNS resolutions of an IP or domain | `CIRCL_PDNS_USER` / `_PASS` |
| `lookup_file_hash` | MalwareBazaar + VirusTotal verdicts, family, tags, detection rate | `ABUSECH_AUTH_KEY`, `VIRUSTOTAL_KEY` |
| `lookup_known_file` | Known-good check via CIRCL hashlookup (NIST NSRL + distro packages) | — |
| `check_url_safety` | URLScan.io verdicts, score, categories, screenshot | `URLSCAN_KEY` (optional) |
| `lookup_malware_family` | ThreatFox malware attribution for an IP, domain, URL or hash | `ABUSECH_AUTH_KEY` |
| `check_ransomware_intel` | Ransomwhere lookup of a Bitcoin address | — |
### 🧑💻 DevSecOps
| Tool | What it does | Key |
|---|---|---|
| `scan_dependencies` | Bulk OSV scan of `requirements.txt`, `package.json`, `pom.xml` or `name:ecosystem:version` lines (≤ 1,000 packages) | — |
| `scan_container_packages` | Same for OS packages (Alpine, Debian, Ubuntu, …) | — |
| `scan_repo_secrets` | Search public GitHub code for potentially exposed secrets | `GITHUB_TOKEN` |
| `health_check` | Upstream connectivity, KEV catalog, cache, tool-manifest hash, configured keys (names only) | — |
<details>
<summary><b>MCP resources & prompts</b></summary>
| Kind | Name | Purpose |
|---|---|---|
| Resource | `kev://catalog` | Summary of the in-memory CISA KEV catalog |
| Resource template | `epss://scores/{cve_id}` | EPSS score of one CVE |
| Resource | `manifest://tool-hash` | SHA-256 over every tool's name, description and schema — pin and verify the tool surface |
| Prompt | `patch_decision` | Patch-now-or-schedule decision for one CVE via `triage_cve` (deep) |
| Prompt | `compare_and_prioritize` | Rank a list of CVEs for patching |
| Prompt | `dependency_triage` | Scan dependencies, then triage the CVEs found |
</details>
## 🧮 How the verdict is made
<img src="assets/triage-flow.svg" alt="Diagram: triage_cve fans out to NVD with VulnCheck fallback, EPSS, CISA KEV and PoC discovery, feeds the risk engine, applies the KEV hard override and optionally an SSVC decision, producing a verdict" width="100%">
<img src="assets/risk-score.svg" alt="Diagram: the 100-point score is split into CVSS 20, EPSS 35, CISA KEV 30 and PoC 15 points with boosters; labels LOW 0-25, MEDIUM 26-50, HIGH 51-75, CRITICAL 76-100; KEV-listed CVEs are always CRITICAL" width="100%">
- **Why these weights?** EPSS is the best predictor of real-world exploitation, KEV is ground truth that exploitation happens, CVSS describes *severity* rather than likelihood (Allodi & Massacci, 2014), and a public PoC lowers the attacker's bar.
- **KEV hard override:** a CISA KEV listing means confirmed in-the-wild exploitation — the label is always `CRITICAL` and the score at least 76, whatever CVSS and EPSS say.
- **PoC confidence ladder:** `WEAPONIZED` 15 · `PUBLIC_EXPLOIT_REMOTE` 12 · `PUBLIC_EXPLOIT` 10 · `PUBLIC_POC_HIGH_QUALITY` 7 · `PUBLIC_POC_LOW_QUALITY` 3.
- **SSVC (deep mode):** the CISA *Deployer* decision tree (Exploitation × Automatable × Technical Impact × Mission & Well-being) yields `Act`, `Attend`, `Track*` or `Track` — an explainable, non-numeric second opinion.
## 🏗️ Architecture
<img src="assets/architecture.svg" alt="Diagram: MCP clients connect over stdio or streamable HTTP to the cve-mcp binary (dispatcher, tools, triage orchestrator, risk engine, cache, audit log, allowlisted HTTPS client), which queries vulnerability, exploit, network and threat-intelligence sources" width="100%">
<details>
<summary><b>Source layout</b></summary>
```
src/
├── main.rs CLI: serve (stdio / HTTP), call, --list-tools, --healthcheck
├── mcp/ JSON-RPC dispatcher · stdio transport · streamable-HTTP transport
├── tools/ tool registry + handlers (core, threat, vuln, devsecops, risk, triage)
├── api/ one client per upstream (NVD, EPSS, KEV, OSV, VulnCheck, …)
├── utils/ risk scorer · SSVC · CVSS v2/v3/v4 · fan-out · retry · source trust
├── http.rs reqwest + rustls, host allowlist, redirect checks, size limits
├── cache.rs SQLite cache (WAL, per-source TTLs)
├── audit.rs rotating JSON audit log
├── config.rs environment config, Secret type
├── validators.rs CVE / IP / hash validation, SSRF checks
├── resources.rs kev://catalog · epss://scores/{cve_id} · manifest://tool-hash
└── prompts.rs patch_decision · compare_and_prioritize · dependency_triage
```
</details>
## ⚙️ Configuration
Configuration comes from environment variables; a `.env` file in the working directory is loaded automatically (see [`.env.example`](.env.example)). **Every key is optional** — tools say which key they are missing.
| Variable | Enables | Get it |
|---|---|---|
| `NVD_API_KEY` | 50 instead of 5 NVD requests / 30 s | [nvd.nist.gov](https://nvd.nist.gov/developers/request-an-api-key) |
| `GITHUB_TOKEN` | PoC, Nuclei & secret search (5,000 instead of 60 req/h) | [github.com/settings/tokens](https://github.com/settings/tokens) |
| `VULNCHECK_TOKEN` | NVD++ fallback inside `triage_cve` | [vulncheck.com](https://vulncheck.com) |
| `ABUSECH_AUTH_KEY` | MalwareBazaar + ThreatFox (required by abuse.ch since 2025) | [auth.abuse.ch](https://auth.abuse.ch/) |
| `ABUSEIPDB_KEY` | IP abuse scores | [abuseipdb.com](https://www.abuseipdb.com/account/api) |
| `GREYNOISE_API_KEY` | Internet-noise classification | [greynoise.io](https://viz.greynoise.io/signup) |
| `VIRUSTOTAL_KEY` | Multi-engine detection stats | [virustotal.com](https://www.virustotal.com/gui/join-us) |
| `SHODAN_KEY` | Host exposure lookups | [shodan.io](https://account.shodan.io/register) |
| `URLSCAN_KEY` | Higher URLScan.io limits | [urlscan.io](https://urlscan.io/user/signup) |
| `CIRCL_PDNS_USER` / `CIRCL_PDNS_PASS` | Passive DNS | [circl.lu](https://www.circl.lu/services/passive-dns/) |
<details>
<summary><b>Runtime settings</b></summary>
| Variable | Default | Meaning |
|---|---|---|
| `MCP_TRANSPORT` | `stdio` | `http` serves stateless streamable HTTP |
| `HOST` / `PORT` | `0.0.0.0` / `8000` | HTTP bind address (endpoint `/mcp`) |
| `MCP_ALLOWED_ORIGINS` | — | Extra browser origins allowed to call the HTTP endpoint (`*` disables the check) |
| `CACHE_DB_PATH` | `~/.cve-mcp/cache.db` | SQLite response cache |
| `AUDIT_LOG_PATH` | `~/.cve-mcp/audit.log` | JSON-lines audit log (50 MB × 5 rotation) |
| `REQUEST_TIMEOUT` | `30` | Upstream HTTP timeout in seconds |
| `MAX_RETRIES` | `3` | Attempts for retrying clients (NVD, VulnCheck, HIBP) |
| `RUST_LOG` | `info` | Log filter — logs always go to stderr |
</details>
### 🐳 Docker / HTTP
```bash
docker build -t cve-mcp-server .
docker run --rm -p 8000:8000 --env-file .env cve-mcp-server
claude mcp add --transport http cve-mcp http://localhost:8000/mcp
```
The HTTP transport is stateless (one JSON-RPC message per `POST /mcp`, JSON response) and accepts browser requests only from local origins unless `MCP_ALLOWED_ORIGINS` says otherwise. Put it behind your own TLS / auth proxy before exposing it beyond a trusted network.
### ⌨️ Command line
```bash
cve-mcp # serve MCP (stdio, or HTTP with MCP_TRANSPORT=http)
cve-mcp call triage_cve cve_id=CVE-2024-3400 depth=deep
cve-mcp call scan_dependencies dependency_list=@requirements.txt # @file / @- for stdin
cve-mcp call compare_cves '{"cve_ids": ["CVE-2024-3400", "CVE-2024-6387"]}'
cve-mcp --list-tools | --healthcheck | --version | --help
```
## 🔐 Security & privacy
- **Outbound only, allowlisted:** every request *and every redirect hop* must target a known API host; no inbound ports with stdio.
- **No secrets in logs:** keys live in a `Secret` type with masked output, error messages strip credentials from URLs, and the audit log redacts any parameter named `*key*` / `*token*`.
- **Input hardening:** CVE / IP / hash validation, private & reserved IP ranges blocked (including IPv4-mapped IPv6), size-limited response bodies, `pom.xml` parsed with DTDs disabled.
- **No sampling:** the server never sends `sampling/createMessage` back to the client — all model interaction is client-driven.
- **Local data:** responses are cached in a local SQLite file; nothing is sent anywhere except the lookups themselves.
Found a vulnerability? Please follow [SECURITY.md](SECURITY.md).
<details>
<summary><b>🗄️ Data sources (23)</b></summary>
| Source | Used for | Auth |
|---|---|---|
| NIST NVD 2.0 | CVE records, CVSS, CWE, search | optional API key |
| VulnCheck NVD++ | NVD fallback in `triage_cve` | token |
| FIRST EPSS | Exploitation probability | — |
| CISA KEV (+ GitHub mirror) | Known exploited vulnerabilities | — |
| OSV.dev | Package vulnerabilities, bulk scans, record details | — |
| MITRE ATT&CK (STIX) | Technique & group mapping | — |
| Microsoft MSRC · Red Hat · Ubuntu | Vendor advisories | — |
| GitHub search | PoC repositories, secret exposure | optional token |
| Exploit-DB | Public exploits | — |
| Nuclei templates | Detection templates | optional token |
| AbuseIPDB · GreyNoise | IP reputation | keys |
| Shodan | Host exposure | key |
| crt.sh · CIRCL Passive DNS | Certificates, subdomains, DNS history | CIRCL credentials |
| VirusTotal · MalwareBazaar · ThreatFox | Malware verdicts & attribution | keys |
| URLScan.io | URL verdicts | optional key |
| Ransomwhere | Ransomware wallets | — |
| CIRCL hashlookup | Known-good files (NSRL) | — |
</details>
## 🧪 Development
```bash
cargo build # debug build
cargo test # unit + integration tests (offline, no API keys)
cargo fmt --all --check && cargo clippy --all-targets -- -D warnings
```
CI runs the tests on Linux, macOS and Windows, checks the MSRV (1.88), audits dependencies and builds the Docker image. See [CONTRIBUTING.md](CONTRIBUTING.md) for how to add a tool.
<details>
<summary><b>Regenerating the README visuals</b></summary>
The banner and diagrams in [`assets/`](assets) are hand-written SVG. The terminal GIFs are rendered from real recordings in [`scripts/readme-assets/recordings`](scripts/readme-assets/recordings) with headless Chromium (Playwright) and Pillow:
```bash
cve-mcp call triage_cve cve_id=CVE-2021-44228 depth=deep > scripts/readme-assets/recordings/triage.txt
node scripts/readme-assets/render-terminal.cjs triage # also: scan, compare
```
</details>
<details>
<summary><b>🛠️ Troubleshooting</b></summary>
- **Client shows no tools** — check the config JSON for trailing commas, use the absolute binary path, fully restart the client. `RUST_LOG=debug cve-mcp` prints diagnostics to stderr.
- **“NVD rate limit exceeded”** — set `NVD_API_KEY`; `triage_cve` also falls back to VulnCheck when `VULNCHECK_TOKEN` is set.
- **MalwareBazaar / ThreatFox return nothing** — abuse.ch requires `ABUSECH_AUTH_KEY` since 2025.
- **GitHub-based searches are empty** — unauthenticated GitHub search allows only a handful of requests per minute; set `GITHUB_TOKEN`. Rate-limited results are never cached, so a retry works once the limit resets.
- **Corporate proxy / custom CA** — `HTTPS_PROXY` / `NO_PROXY` are honoured and TLS uses the OS certificate store (plus `SSL_CERT_FILE` on Linux).
</details>
## 🙏 Credits & license
Originally created in Python by [Mahipal Jangra (mukul975)](https://github.com/mukul975/cve-mcp-server); this fork is a full Rust rewrite with the same tool surface. Licensed under the [Apache License 2.0](LICENSE).
<div align="center"><sub>Built for defenders · intelligence lookups only — no scanning, no exploitation.</sub></div>
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues