Skip to main content
Glama

CVEstate

Your CVE intel remembers your estate. CVEstate watches what changed against it.

License Python Tests Tools Release

Vulnerability intel tools answer lookups. Ask about a CVE and you get its CVSS, its EPSS, its KEV status, its timeline, a risk score. Ask again tomorrow and you get the same work done twice, because the tool kept nothing. Your estate sat unchanged in the middle: the same packages, the same versions, quietly waiting for the day one of their CVEs turns into a headline.

CVEstate keeps the estate. You hand it your SBOMs and lockfiles once, and from then on every scan snapshots your exposure and diffs it against the last one. The output leads with the only movement that matters: a CVE on your stack entering CISA KEV, an EPSS score spiking overnight, a fresh disclosure against a version you actually run. Underneath the watch sits a full MCP security-intelligence server: 28 lookup and triage tools across NVD, EPSS, CISA KEV, OSV, Shodan, VirusTotal, GreyNoise, and 20+ more sources, plus the 4 Watchtower tools that give it a memory.

Contents: See the watch fire · Before and after · Watchtower · The server · Install · Quickstart · Repository layout · Tests · Contributing · License · Credits


See the watch fire

The launch demo runs fully offline against a small estate assembled from four real files: a CycloneDX SBOM carrying log4j-core 2.14.1, a requirements.txt, an npm lockfile, a go.mod. Eight packages in total. Day 1, CVE-2021-44228 sits in the exposure list with an EPSS of 31% and no KEV listing. Day 2, CISA adds it to KEV with a remediation due date, and its EPSS jumps to 97%. Here is the brief the second scan produces:

Watchtower delta brief: CVE-2021-44228 newly KEV-listed against the demo estate, EPSS 97%, due 2026-11-07

That block is the whole product in one screen. The CVE was already in the estate on day 1, and a lookup tool would have shown the same record both days. The watch caught the change, ranked it above everything else that moved, and attached the date CISA expects a fix by. The demo ships in examples/watchtower/ and reproduces with six commands, no API keys, no network.


Related MCP server: CVE Search MCP Server

Before and after: one day in the demo estate

Snapshot #1 (baseline)

Snapshot #2 (day 2)

Packages scanned

8

8

Findings

4

5

In CISA KEV

0

1

CVE-2021-44228 EPSS

31.0%

97.0%

New exposure

CVE-2020-8203 (lodash 4.17.20)

Snapshot comparison: findings, KEV count, and the CVE-2021-44228 EPSS movement between the two demo scans

Every number in that table comes from the demo run itself, captured in the build evidence. The diff engine separates the movement into classes (NEW_KEV, EPSS_SPIKE, NEW_EXPOSURE, RESOLVED, package changes) so a brief reads as a work list, ranked by urgency.


Watchtower — the estate watch

Watchtower is the system CVEstate 1.0.0 adds. It lives in src/cve_mcp/watchtower/ and plugs into the base server as four MCP tools plus a CLI.

Estate store

A persistent SQLite inventory at ~/.cve-mcp/estate.db (override with WATCHTOWER_DB_PATH), opened with the same WAL pragmas as the base cache. Ingestion accepts the formats your pipeline already produces:

Format

Source

CycloneDX JSON

SBOM generators (Syft, cdxgen, build plugins)

SPDX JSON

SBOM generators

requirements.txt

Python

package-lock.json (v1–v3)

npm

go.mod

Go

Package URLs are mapped to OSV ecosystems automatically, and re-adding a package preserves its first_seen date. The estate remembers when every package entered your stack.

Snapshot engine

Each scan walks the estate and records exposure using the base project's own intel clients: OSV for package vulnerabilities, the pre-warmed CISA KEV catalog for known-exploited status and due dates, one batched EPSS call for exploitation probability. A snapshot is a dated, labelled, queryable record of your exposure at that moment.

Diff engine

Consecutive snapshots are compared by a pure function that classifies every change:

Event

Fires when

NEW_KEV

A CVE in your estate is KEV-listed in the new snapshot and was unlisted in the old one

EPSS_SPIKE

A finding's EPSS delta meets the threshold (default 10 points, configurable)

NEW_EXPOSURE

A finding appears that the old snapshot lacked

RESOLVED

A finding disappears (patched, upgraded, or removed)

PACKAGE_ADDED / PACKAGE_REMOVED

Estate membership changed between snapshots

The surface

MCP tools (registered alongside the base 28):

Tool

What it does

estate_add

Parse an SBOM or lockfile pasted as text and ingest it into the estate

estate_scan

Snapshot the estate now and summarize exposure

watch_diff

Diff the two latest snapshots

watch_brief

Emit the prioritized patch brief: KEV first, EPSS spikes second, new exposure third

CLI (same operations, scriptable, cron-friendly):

cve-watch add estate.cdx.json
cve-watch scan --label monday
cve-watch diff
cve-watch brief

The CLI takes --db for the estate path and --fixture for offline runs against a fixture intel file, which is how the demo and the test suite drive it with zero network.


The server — 28 intelligence tools

The foundation layer is a production MCP server with a one-call triage orchestrator and 27 lookup tools across 24 data sources.

Start with triage_cve. One call fans out to NVD, EPSS, CISA KEV, and public PoC discovery in parallel, then scores the result with a composite model (CVSS × 0.20 + EPSS × 0.35 + KEV × 0.30 + PoC × 0.15). A KEV listing hard-overrides the label to CRITICAL: confirmed in-the-wild exploitation outranks arithmetic. At depth=deep it adds an SSVC v2 decision, and when NVD throttles, it falls back to the VulnCheck NVD++ index transparently.

Category

Tools

Core vulnerability intel (8)

lookup_cve · search_cves · get_epss_score · check_kev · parse_cvss · get_cve_summary · check_package_vulns · health_check

Exploit and ATT&CK (4)

check_exploit_availability · get_vendor_advisory · get_attack_mapping · get_cve_timeline

Risk and reporting (4)

check_poc_exists · calculate_risk_score · generate_vuln_report · compare_cves

Network intel (4)

check_ip_reputation · get_domain_intel · passive_dns_lookup · shodan_host_lookup

Threat intel (4)

lookup_file_hash · check_url_safety · lookup_malware_family · check_ransomware_intel

DevSecOps (3)

scan_dependencies · scan_container_packages · scan_repo_secrets

Orchestrator (1)

triage_cve

The server also exposes MCP resources (kev://catalog, epss://scores/{cve_id}, a SHA-256 tool-manifest hash) and prompts (patch_decision, compare_and_prioritize, dependency_triage). It serves over stdio by default, or streamable HTTP with MCP_TRANSPORT=http. Operational hardening carries over from the base project: structured audit logging on every tool call, SSRF and private-IP guards on outbound requests, secrets held as SecretStr, and a standing rule that the server never issues sampling requests.

A note on fresh installs

CVEstate 1.0.0 pins the MCP dependency to the 1.x line (mcp[cli]>=1.7.0,<2.0.0). The base project's unbounded requirement resolved to mcp 2.x, which removed the FastMCP module the server imports, and a fresh install crashed before it could answer a single question. The pin is the same fix the upstream project's dependabot branch proposed. Fresh installs start, and the project's CI runs green against it.


Install

Requires Python 3.10 or newer.

git clone https://github.com/enabled404/cvestate.git
cd cvestate
python -m venv .venv && source .venv/bin/activate
pip install -e .

Copy .env.example to .env and add the API keys you hold (NVD, Shodan, VirusTotal, and friends). Several sources need no key at all, and the entire Watchtower demo runs offline with none.

Point any MCP client at the server:

{
  "mcpServers": {
    "cvestate": {
      "command": "cve-mcp",
      "args": []
    }
  }
}

A ready-made .mcp.json ships at the repository root. Docker works too: docker build -t cvestate . then run with --env-file .env.

Quickstart

Watch your estate (offline demo, two minutes):

export WATCHTOWER_DB_PATH=/tmp/demo-estate.db
cve-watch add examples/watchtower/estate.cdx.json
cve-watch add examples/watchtower/requirements.txt
cve-watch add examples/watchtower/package-lock.json
cve-watch add examples/watchtower/go.mod
cve-watch scan --label baseline --fixture examples/watchtower/intel_v1.json
cve-watch scan --label day2     --fixture examples/watchtower/intel_v2.json
cve-watch brief

The brief leads with CVE-2021-44228 entering KEV against your log4j-core 2.14.1, EPSS 31% → 97%, due 2026-11-07.

Run the watch live: drop --fixture and the scan uses the real OSV, KEV, and EPSS clients. Add your real SBOMs, scan on a schedule, and read the brief each morning.

Triage a single CVE: connect the MCP server to your client and call triage_cve with any CVE ID, or ask for estate_add, estate_scan, and watch_brief in conversation.


Repository layout

src/cve_mcp/
  server.py          the FastMCP server: 28 intel tools, resources, prompts
  api/               22 client and intel modules (NVD, EPSS, KEV, OSV, Shodan, VT, ...)
  cache/             aiosqlite response cache (WAL)
  utils/             risk scorer, SSVC, fan-out, validators, source trust
  watchtower/        the estate watch added in CVEstate 1.0.0
    parsers.py       CycloneDX / SPDX / requirements / package-lock / go.mod
    store.py         persistent estate + snapshot store (SQLite)
    engine.py        snapshot builder + pure diff engine
    brief.py         delta brief and snapshot summaries
    tools.py         estate_add, estate_scan, watch_diff, watch_brief
    cli.py           the cve-watch CLI
    fixtures.py      offline fixture intel provider (demo + tests)
tests/               78 tests, all offline
examples/watchtower/ the offline demo: estate files + day-1/day-2 intel

Runtime identity, for anyone scripting against the base project: the Python package stays cve_mcp, the MCP server name stays cve-mcp, and the console scripts stay cve-mcp and cve-watch. CVEstate is the release name over the top of them.

Tests

pip install -e ".[test]"
python -m pytest -q

78 passing: the 30 base tests intact, plus 48 Watchtower tests covering parsers (14), the estate store (8), the snapshot and diff engines (15), the brief (5), and MCP tool registration and flow (6). Every test runs offline; intel is injected as fixtures.

Contributing

See CONTRIBUTING.md. Issues and pull requests are welcome at the CVEstate repository.

License

Apache License, Version 2.0. See LICENSE and NOTICE.

Credits

CVEstate builds on the CVE MCP Server by Mahipal Jangra (Apache-2.0) and extends it with the Watchtower estate-watch system. The 28 intelligence tools, the triage orchestrator, and the source clients are the foundation layer's work; Watchtower, the estate store, the snapshot and diff engines, the delta brief, the cve-watch CLI, and the fresh-install repair are the new work in this release. Full attribution lives in NOTICE.

Available Tools

32 tools
calculate_risk_scoreB

Calculate a composite 0-100 risk score using CVSS, EPSS, KEV, and PoC data.

Inputs: cve_id (required). Returns: result

check_exploit_availabilityA

Search GitHub for public proof-of-concept (PoC) exploit repositories for a CVE. Results are sorted by star count to surface the most credible exploits first.

Args: cve_id: CVE identifier (e.g. CVE-2021-44228)

Inputs: cve_id (required). Returns: result

check_ip_reputationA

Check an IP address reputation via AbuseIPDB and GreyNoise Community. Returns abuse confidence score, country, ISP, Tor status, and noise classification.

Args: ip: IPv4 or IPv6 address to check (e.g. 1.2.3.4 or 2001:db8::1)

Inputs: ip (required). Returns: result

check_kevB

Check if a CVE is in the CISA Known Exploited Vulnerabilities (KEV) catalog.

Args: cve_id: CVE identifier (e.g. CVE-2021-44228)

Inputs: cve_id (required). Returns: result

check_package_vulnsB

Check a package for known vulnerabilities via OSV.dev.

Args: package: Package name (e.g. log4j-core, requests, lodash) ecosystem: Package ecosystem — PyPI, npm, Go, Maven, NuGet, crates.io, Packagist, Hex, RubyGems, Android, CocoaPods, GitHub Actions version: Optional specific version to check (e.g. 2.14.1)

Inputs: package (required), ecosystem (required), version. Returns: result

check_poc_existsB

Search for proof-of-concept exploits across GitHub, Exploit-DB, and Nuclei templates.

Inputs: cve_id (required), include_github, include_exploitdb. Returns: result

check_ransomware_intelA

Check if a Bitcoin address is associated with a known ransomware family using the Ransomwhere database.

Args: bitcoin_address: Bitcoin wallet address to look up (P2PKH, P2SH, or bech32)

Inputs: bitcoin_address (required). Returns: result

check_url_safetyA

Check a URL or domain for malicious activity via URLScan.io. Returns scan verdicts, malicious flag, score, and categories.

Args: url_or_domain: URL (https://example.com/path) or bare domain (example.com)

Inputs: url_or_domain (required). Returns: result

compare_cvesB

Compare multiple CVEs by risk score and generate a patch priority ranking.

Inputs: cve_ids (required). Returns: result

estate_addA

Add packages to your Watchtower estate from an SBOM or lockfile.

Paste the file contents: CycloneDX JSON, SPDX JSON, requirements.txt, package-lock.json, or go.mod. Packages are remembered persistently (ecosystem, name, version, first-seen date) so later scans can diff your exposure over time.

Args: source_text: The SBOM/lockfile file contents. source_name: Optional original filename (helps format detection).

Inputs: source_text (required), ctx, source_name. Returns: result

estate_scanA

Snapshot your estate's current vulnerability exposure.

Queries OSV for every installed package, joins the CISA KEV catalog and current EPSS scores, and persists the result as a snapshot that watch_diff / watch_brief can compare against earlier snapshots.

Args: label: Optional label for this snapshot (e.g. "monday", "pre-release").

Inputs: ctx, label. Returns: result

generate_vuln_reportC

Generate a vulnerability report in executive, technical, or full format.

Inputs: cve_id (required), format, include_remediation. Returns: result

get_attack_mappingA

Map a CVE to MITRE ATT&CK techniques and associated threat groups. Downloads the ATT&CK dataset lazily (cached 24h). Searches technique descriptions and references for the CVE ID.

Args: cve_id: CVE identifier (e.g. CVE-2021-44228)

Inputs: cve_id (required). Returns: result

get_cve_summaryA

Get a comprehensive one-page summary of a CVE: severity, EPSS, KEV status, description, weaknesses, and timeline. Fetches NVD + EPSS concurrently.

Args: cve_id: CVE identifier (e.g. CVE-2021-44228)

Inputs: cve_id (required). Returns: result

get_cve_timelineA

Build a complete CVE lifecycle timeline: NVD publication date, EPSS score history, CISA KEV addition date, patch lag, and exploit window estimation.

Args: cve_id: CVE identifier (e.g. CVE-2021-44228)

Inputs: cve_id (required). Returns: result

get_domain_intelC

Get domain intelligence: certificate transparency logs (crt.sh) and passive DNS records (CIRCL PDNS). Returns subdomains and certificate history.

Args: domain: Domain name to investigate (e.g. example.com)

Inputs: domain (required). Returns: result

get_epss_scoreB

Get EPSS (Exploit Prediction Scoring System) scores for one or more CVEs.

Args: cve_ids: Comma-separated CVE IDs (e.g. CVE-2021-44228,CVE-2022-22965)

Inputs: cve_ids (required). Returns: result

get_vendor_advisoryA

Fetch vendor security advisories for a CVE from Microsoft (MSRC), Red Hat, and Ubuntu. Shows patch availability and affected products.

Args: cve_id: CVE identifier (e.g. CVE-2021-44228)

Inputs: cve_id (required). Returns: result

health_checkA

Check the health of the CVE MCP server: NVD connectivity, KEV catalog status, cache, risk scoring version, tool manifest hash, and which optional API keys are configured (names only — never the key values).

Note: This tool pings NVD without rate limiting — do not call it in a loop.

Inputs: no named parameters. Returns: result

lookup_cveA

Look up a CVE by ID from NVD. Returns full details including CVSS scores, description, weaknesses, and CISA KEV status.

Args: cve_id: CVE identifier (e.g. CVE-2021-44228)

Inputs: cve_id (required). Returns: result

lookup_file_hashB

Look up a file hash (MD5/SHA1/SHA256) against MalwareBazaar and VirusTotal. Returns malware family, detection stats, and file metadata.

Args: hash_str: MD5 (32 hex), SHA1 (40 hex), or SHA256 (64 hex) hash

Inputs: hash_str (required). Returns: result

lookup_malware_familyA

Look up an IOC (IP, domain, URL, or hash) against ThreatFox for malware family attribution. Returns confidence level, IOC type, and threat classification.

Args: ioc: Indicator of Compromise — IP address, domain, URL, MD5/SHA256 hash

Inputs: ioc (required). Returns: result

parse_cvssA

Parse and explain a CVSS vector string (v2, v3.x, or v4.0).

Args: vector: CVSS vector string (e.g. CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Inputs: vector (required). Returns: result

passive_dns_lookupA

Query CIRCL Passive DNS for historical DNS resolutions of an IP or domain. Shows what hostnames have resolved to this IP, or what IPs a domain has pointed to.

Args: ip_or_domain: IPv4/IPv6 address or domain name to query

Inputs: ip_or_domain (required). Returns: result

scan_container_packagesA

Scan container image packages (Alpine, Debian, Ubuntu OS packages) for known CVEs via OSV.dev. Input should be 'name:ecosystem:version' lines, one package per line, e.g. from 'apk info -v' or 'dpkg -l' output.

Supported ecosystems: Alpine, Debian, Ubuntu, and any OSV-supported ecosystem.

Args: packages: Newline-separated 'name:ecosystem:version' entries. Example: openssl:Alpine:3.0.7-r0 musl:Alpine:1.2.4_r2 libssl3:Debian:3.0.7-1

Inputs: packages (required). Returns: result

scan_dependenciesA

Bulk scan application dependencies for known CVEs via OSV.dev. Supports requirements.txt (PyPI), package.json (npm), pom.xml (Maven), or generic 'name:ecosystem:version' lines. Returns only vulnerable packages.

Args: dependency_list: Raw contents of requirements.txt, package.json, pom.xml, or newline-separated 'name:ecosystem:version' entries. Max 1000 packages per call.

Inputs: dependency_list (required). Returns: result

scan_repo_secretsA

Search GitHub public repositories for potential exposed credentials or secrets related to a search term (CVE ID, tool name, or keyword). Requires GITHUB_TOKEN for best results. For security research only.

Args: search_term: CVE ID, tool name, or keyword to search for in public repo files (e.g. CVE-2024-1234, AWS_SECRET_ACCESS_KEY, log4j)

Inputs: search_term (required). Returns: result

search_cvesA

Search NVD for CVEs by keyword and optional severity filter.

Args: query: Keyword to search (letters, numbers, spaces, hyphens, dots — max 200 chars) severity: Optional CVSS v3 severity filter: NONE, LOW, MEDIUM, HIGH, CRITICAL limit: Max results (1–50, default 10)

Inputs: query (required), limit, severity. Returns: result

shodan_host_lookupA

Look up a host on Shodan: open ports, running services, OS, CVEs on the host. Requires SHODAN_KEY environment variable.

Args: ip: IPv4 address to look up (e.g. 8.8.8.8)

Inputs: ip (required). Returns: result

triage_cveA

One-call CVE triage orchestrator (v0.2.0).

Fans out the relevant upstream sources concurrently — NVD (with a transparent VulnCheck NVD++ fallback when NVD fails), EPSS, the CISA KEV catalog, and (for depth != "quick") public PoC/exploit discovery — then computes the composite risk score (KEV hard-override applies: a KEV-listed CVE is always CRITICAL with score >= 76). For depth == "deep" it additionally emits an SSVC v2 gated decision (Act / Attend / Track* / Track).

Args: cve_id: CVE identifier (e.g. CVE-2021-44228). depth: "quick" — NVD + EPSS + KEV (no PoC search; fastest), "standard" — adds PoC/exploit discovery (default), "deep" — adds an SSVC v2 qualitative decision.

Inputs: cve_id (required), depth. Returns: result

watch_briefA

Prioritized patch brief for your estate, from the latest snapshots.

KEV-listed changes first, EPSS spikes second, new exposure third — the patch order a SOC/GRC review actually works in. With only one snapshot on record, returns a current-state brief instead.

Args: epss_threshold: Minimum EPSS rise (0.0-1.0) counted as a spike. Default 0.10 (10 points).

Inputs: ctx, epss_threshold. Returns: result

watch_diffB

Show what changed between the two most recent estate snapshots.

Reports new CISA KEV additions hitting your installed packages, EPSS spikes past the threshold, newly disclosed CVEs against installed versions, resolved findings, and estate membership changes.

Args: epss_threshold: Minimum EPSS rise (0.0-1.0) counted as a spike. Default 0.10 (10 points).

Inputs: ctx, epss_threshold. Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.2/5.0

    Scored across 32 tools

    Disambiguation3/5

    Several tools overlap substantially: get_cve_summary, lookup_cve, triage_cve, calculate_risk_score, and check_exploit_availability/check_poc_exists all retrieve or derive overlapping CVE intelligence. Descriptions help distinguish depth and orchestration level, but an agent still has multiple plausible choices for common tasks like 'tell me about this CVE' or 'find exploits'. Other areas, such as threat-intel lookups, are more clearly separated.

    Naming Consistency3/5

    Most names use snake_case, but the verb conventions are mixed: check_*, get_*, lookup_*, scan_*, search_*, calculate_*, generate_*, compare_*, triage_*, estate_*, watch_*, and health_check. This is still readable, but the set does not follow a single predictable verb_noun pattern. The estate_* and watch_* prefixes are internally consistent but differ from the rest.

    Tool Count2/5

    At 32 tools, the surface is heavy for a single MCP server and includes many overlapping CVE-enrichment and scanning entry points. While the security domain is broad, several tools could reasonably be consolidated or exposed as options on fewer tools. The count exceeds the typical well-scoped range and increases selection burden.

    Completeness4/5

    The server covers a strong CVE lifecycle: lookup, search, summary, triage, timeline, risk scoring, comparison, exploit discovery, advisories, EPSS, KEV, ATT&CK mapping, and estate scanning. Minor gaps exist around estate management, such as listing or removing watched packages and more explicit snapshot management, but core workflows are covered.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      A
      quality
      D
      maintenance
      Enables searching and analyzing CVEs and vulnerabilities from multiple sources, optimized for PR review scenarios to help developers identify the latest security issues.
      8
      2
      -
    • A
      license
      A
      quality
      B
      maintenance
      Enables prioritizing vulnerabilities by real-world exploitation evidence, combining CISA KEV, EPSS, CVSS, and SSVC to rank CVEs and package versions, and providing fix recommendations.
      4
      2
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      Enables Claude to look up known vulnerabilities in a project's dependencies across OSV.dev, CVE.org, NVD, CISA KEV and EPSS, then rank the findings by real-world exploitation so the most urgent fixes surface first. It supports single-package checks, keyword CVE searches, and whole-lockfile scans, with upgrade-path reasoning.
      4
      MIT