A Model Context Protocol server that exposes a live CVEasy AI install — scans, findings, CVEs, assets, threat intel, metrics — to MCP clients like Claude Desktop and Claude Code.
Its purpose is to refine report-making. Today CVEasy fills report narrative sections with the bundled local model using only aggregate numbers, which keeps prose thin and occasionally wrong. This server flips that: it lets a far more capable model (Claude) pull the real, structured data behind any report and draft or critique the prose grounded in actual figures — then render the final HTML/PDF through the same backend.
┌────────────────────┐ stdio (JSON-RPC) ┌──────────────────┐ HTTP :3001 ┌─────────────────────┐
│ Claude Desktop / │ ───────────────────▶ │ cveasy-mcp │ ─────────────▶ │ CVEasy AI backend │
│ Claude Code (LLM) │ ◀─────────────────── │ (this package) │ ◀───────────── │ (Hono, SQLite, AI) │
└────────────────────┘ tools/prompts/res └──────────────────┘ /api/* └─────────────────────┘
The server is a thin client over the CVEasy REST API — it never touches the database directly. That avoids lock contention with the running desktop app, reuses the backend's scoring/dedup logic, and survives schema changes.
git clone https://github.com/CVEasy/cveasy-mcp && cd cveasy-mcp
bun install
bun run smoke # spins up the server and exercises it against your live backendbun run smoke should print SMOKE TEST PASSED if the CVEasy AI app is running (backend on :3001).
Then wire it into a client (below) and ask: "Use CVEasy to draft an executive risk summary for Acme Corp."
Requires the CVEasy AI desktop app (or
bun run backend/src/index.ts) running so the backend answers on:3001. The server starts fine without it and every tool simply returns a clear "backend not reachable" error until it's up.
All configuration is via environment variables; defaults match a stock local install.
| Variable | Default | Purpose |
|---|---|---|
CVEASY_BASE_URL |
http://127.0.0.1:3001 |
Backend base URL. |
CVEASY_TIMEOUT_MS |
120000 |
Per-request timeout (report/AI calls can be slow). |
CVEASY_REPORT_DIR |
$HOME/Downloads |
Where report_generate saves files by default. |
CVEASY_ALLOW_WRITES |
1 |
When 0, all mutating tools (triage, risk, enrich, scans) are hidden. |
CVEASY_ALLOW_SCANS |
1 |
When 0, only the BAS scan-launching tools are hidden. |
CVEASY_SESSION_TOKEN |
— | Operator session token, only needed if RBAC is enabled on the backend (sent as x-session-token). |
CVEASY_ACCESS_TOKEN |
— | Access token, only needed if the backend runs in production/deployment mode (sent as x-access-token). |
On a normal local CVEasy install RBAC is off (/api/auth/status → authEnabled:false), so no tokens are required even for writes.
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS):
{
"mcpServers": {
"cveasy": {
"command": "/Users/<you>/.bun/bin/bun",
"args": ["run", "/absolute/path/to/cveasy-mcp/src/index.ts"],
"env": { "CVEASY_BASE_URL": "http://127.0.0.1:3001" }
}
}
}Use the absolute path to bun (which bun) — Claude Desktop doesn't inherit your shell PATH. Restart Claude Desktop; "cveasy" appears in the tools menu.
# from anywhere
claude mcp add cveasy -- /Users/<you>/.bun/bin/bun run /absolute/path/to/cveasy-mcp/src/index.tsOr commit a project-scoped .mcp.json so the team shares it:
{
"mcpServers": {
"cveasy": {
"command": "bun",
"args": ["run", "cveasy-mcp/src/index.ts"],
"env": { "CVEASY_BASE_URL": "http://127.0.0.1:3001" }
}
}
}bun run compile # → dist/cveasy-mcp (self-contained)
# then point the client's "command" at the absolute path of dist/cveasy-mcp with no argsThis is the core use case. The pattern is always pull data → write/refine → (optionally) render.
- Pull the ground truth.
report_context { type: "executive" }returns one JSON bundle with metrics, top risks, priority bands, patch compliance, threat feed, and the org profile. Every sub-source is fetched independently; any that fail land inwarningsinstead of failing the whole call. - Draft or refine. Use a prompt — e.g.
refine_executive_summary— which instructs the model to ground every number in that context and follow house style. Or just ask in natural language. (report_template_guidereturns the recommended section skeleton per type.) - Render the deliverable.
report_render { type, kpis, sections }lays Claude's authored prose + tables into a polished, branded, print-ready HTML document using the built-in design system, and saves it. The user opens it and chooses Print → Save as PDF. This closes the loop entirely in the MCP layer — Claude controls the prose AND the layout, no backend involvement. - Critique loop.
critique_report { report_type: "executive", report_text: "…" }reviews an existing report against live data and lists factual errors, gaps, and rewrites.
report_render (MCP-side) |
report_generate (backend) |
|
|---|---|---|
| Prose | Claude's authored sections | The local model's narrative |
| Templates | Built into this server (src/template/) — branded, print-tuned |
The app's own report templates |
| Output | Print-ready HTML (→ Save as PDF) | HTML, plus PDF for executive/findings/roadmap |
| Backend changes | None | None today; a narrative-injection endpoint would let report_generate use Claude's prose too (see Roadmap) |
Use report_render for the refined deliverable; use report_generate when you want the exact in-app report.
src/template/ is a small, dependency-free, offline-safe design system:
design.ts— one shared stylesheet (system fonts, KPI cards, severity badges, callouts,@page/page-break print rules) + per-type accent presets.markdown.ts— injection-safe Markdown→HTML (escapes first;_is left literal sofinding_fp_hash/T1110_001survive).render.ts—renderReport(model)assembles the full HTML from aReportModel(header+logo, KPI band, sections with prose/bullets/tables/callouts, footer).
Improving a template here improves every rendered report. To port the same look into the in-app reports later, lift these into backend/src/services/report-builder.ts.
| Tool | Purpose |
|---|---|
posture_health |
Backend reachability + AI/Ollama/BASzy status. Call first if things fail. |
posture_stats |
Global counts (CVEs, assets, scans). |
posture_metrics_summary |
Headline risk metrics + band distribution — best source of grounded numbers. |
posture_metrics_dashboard |
Full Command Center metric bundle. |
posture_burndown |
Remediation burndown / velocity over time. |
posture_compliance |
Control coverage for a named framework (pci-dss, hipaa, …). |
| Tool | Purpose |
|---|---|
cve_search |
Search/browse + filter by severity, sort by TRIS/EPSS/CVSS/date. |
cve_get |
Full enriched detail for one CVE. |
cve_top_risk |
Highest-risk CVEs by TRIS. |
cve_top_exploitable |
Most likely to be exploited (EPSS + KEV/PoC/ransomware). |
cve_priority_board |
CVEs grouped into P0–P3 bands with SLAs. |
cve_recent |
Most recently published/ingested CVEs. |
cve_attack_chain |
Kill-chain steps + narrative for a CVE. |
cve_remediation |
Generate/fetch remediation guidance (write — caches result). |
| Tool | Purpose |
|---|---|
inventory_list |
Canonical assets with criticality/OS/risk. |
inventory_stats |
Asset counts by criticality / scan coverage. |
inventory_get |
One asset's detail + its CVEs. |
inventory_affected_by_cve |
Assets affected by a given CVE (blast radius). |
inventory_asset_stats |
Scanner-side asset statistics. |
| Tool | Purpose |
|---|---|
triage_list |
The triage work queue (filter by status/CVE). |
triage_stats |
Aggregate triage counts. |
bas_findings_search |
Search BAS findings (severity/module/MITRE/CVE/scan; carries validation verdicts). |
cve_validation_status |
Whether a CVE is BAS-confirmed exploitable in this environment. |
| Tool | Purpose |
|---|---|
bas_scans_list |
List attack-simulation scans. |
bas_scan_get |
One scan's status/progress (poll after starting). |
bas_scan_findings |
Findings for a scan. |
bas_stats |
Aggregate BAS stats. |
bas_mitre_coverage |
MITRE ATT&CK coverage matrix. |
| Tool | Purpose |
|---|---|
threat_feed |
Board feed: top-exploitable, recent KEV, briefings, IoC stats, headlines. |
threat_briefings |
Curated briefings (filter by category/severity). |
threat_actors |
Known actors, sectors, CVEs, TTPs. |
threat_headlines |
Live security-news headlines (RSS). |
| Tool | Purpose |
|---|---|
report_types |
List the report types CVEasy can produce. |
org_profile |
Org name/industry + settings (compliance frameworks, AI provider). |
report_context |
Assemble the structured data feeding a report type — call before writing prose. |
report_render |
Lay author-supplied prose + tables into polished, print-ready HTML using the built-in templates. The refinement deliverable. |
report_template_guide |
The recommended section skeleton per report type (for report_render). |
report_generate |
Render the backend's own HTML/PDF report (local-model narrative) and save it. |
system_open_file |
Reveal a saved file in Finder. |
| Tool | Purpose |
|---|---|
triage_create / triage_batch_add |
Add CVE(s) to the triage queue. |
triage_update / triage_delete |
Update state/owner/notes, or remove. |
risk_accept / risk_approve |
File / approve a risk acceptance. |
business_context_set |
Attach asset-criticality / data-classification / impact to a CVE. |
enrich_cve / enrich_batch |
Refresh EPSS/KEV/TRIS scoring. |
bas_scan_start |
Launch a BAS scan (target must be within an active authorization scope). |
bas_validate_cve |
Targeted scan to confirm a CVE's exploitability. |
Reusable, data-grounded report-writing workflows. Improving the prose rules here improves every report.
| Prompt | Args | What it does |
|---|---|---|
refine_executive_summary |
company_name?, focus? |
Tight 3-paragraph exec summary + recommended actions. |
draft_board_narrative |
tone? (board/security/engineering), company_name? |
Board-level Situation→Complication→Resolution narrative. |
write_remediation_roadmap |
horizon? |
Phased Now/Next/Later plan with owners, SLAs, expected risk reduction. |
attack_sim_writeup |
scan_id? |
Narrates BAS results + MITRE coverage, CONFIRMED_EXPLOITABLE first. |
patch_compliance_brief |
— | Compliance rate, worst SLA violations, top hosts, 3 actions. |
critique_report |
report_type, report_text? |
Adversarially reviews a report against live data. |
Each prompt tells the model to call the relevant data tool first and forbids invented figures.
Read-only snapshots for clients with a resource/"attach context" UI: cveasy://reports/types, cveasy://metrics/summary, cveasy://posture/status, cveasy://org/profile, cveasy://threat/feed.
- Writes are on by default but can be disabled wholesale (
CVEASY_ALLOW_WRITES=0) for a read-only deployment. Mutating tools are annotateddestructiveHintwhere they change/delete state, so clients can prompt before running them. - Scans (
bas_scan_start,bas_validate_cve) actively probe systems and require the target to be inside an active BAS authorization scope on the backend (else403). They can be disabled separately (CVEASY_ALLOW_SCANS=0). - RBAC: when access control is enabled on the backend, mutating actions need an operator session token (
CVEASY_SESSION_TOKEN). - All logs go to stderr — stdout is reserved for the JSON-RPC stream.
bun run typecheck # tsc --noEmit (strict, no unused)
bun run smoke # end-to-end stdio test against the live backend
bun run dev # hot-reload the server
bun run build # → dist/index.js
bun run compile # → dist/cveasy-mcp (standalone binary)Layout: src/config.ts (env), src/client.ts (HTTP), src/util.ts (result helpers + tool registration), src/tools/* (domain tools — incl. render.ts), src/template/* (the design system + Markdown + renderer), src/prompts.ts, src/resources.ts, src/index.ts (wiring). Adding a tool = one registerTool(...) call in the relevant module.
- Port the
src/template/design system into the backend (report-builder.ts) so the in-app reports match the rendered ones, and optionally add a narrative-injection endpoint (POST /api/reports/generate { narrative }) soreport_generatecan use Claude's prose too. (The MCP-side loop is already closed byreport_render.) - Native one-click PDF in
report_render(currently print-to-PDF). Would need a PDF lib or headless renderer; print-ready HTML is intentionally dependency-free for now. report_render_from_context— a convenience that pullsreport_contextand pre-fills the KPI band/tables, leaving Claude to write only the prose.- More section primitives (charts via inline SVG, two-column layouts, appendix tables).