The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Cisa Cybersecurity MCP Server listing page.
CISA Known Exploited Vulnerabilities with BOD 26-04 deadlines, SSVC prioritization, and the full ICS advisory corpus (CSAF) via MCP. Keyless. STDIO & Streamable HTTP.
Public Hosted Server: https://cisa-cybersecurity.caseyjhand.com/mcp
CISA's open vulnerability data: the Known Exploited Vulnerabilities catalog and its federal remediation deadlines, the SSVC decision points CISA publishes per CVE in Vulnrichment, the CSAF corpus of industrial control system advisories back to 2010, and CISA's publication feeds. Check a scan's worth of CVE IDs against KEV in one call, find what is overdue for a vendor, work out the BOD 26-04 timeline for an asset you own, and search or read ICS advisories. Every source is keyless. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
| Tool | Description |
|---|---|
cisa_list_reference | Decode the input vocabulary (BOD 26-04 timelines, KEV fields, SSVC values, sectors, ID formats, severity bands) and report what data the server holds |
cisa_check_cve_status | Check up to 200 CVE IDs against KEV in one call: deadlines, overdue status, ransomware and forensic-triage flags, cited directive |
cisa_search_kev | Search KEV by vendor, product, CWE, date added, due date, overdue status, ransomware linkage, forensic-triage tier, or directive |
cisa_get_ssvc | Fetch CISA's published SSVC decision points per CVE and compute the BOD 26-04 timeline for a stated asset exposure |
cisa_search_ics_advisories | Search ICS advisories by vendor, product, CVE, CWE, KEV membership, CVSS, severity, sector, series, dates, or free text |
cisa_get_advisory | Read one ICS advisory: affected product versions, per-CVE CVSS and CWE, remediations, sectors, revision history |
cisa_get_alerts | List CISA's latest items from its advisory, alert, or ICS advisory feed |
| Resource | Description |
|---|---|
cisa://kev/{cveId} | One KEV catalog entry by CVE ID |
cisa://advisory/{advisoryId} | One ICS advisory, flattened from CSAF 2.0 |
Both resources are fully covered by the tools above, so a tool-only client loses nothing.
cisa_list_reference tooltopic: directives, kev_fields, ssvc_values, sectors, advisory_id_formats, severity_bands, or sources; reads in-process state only, so it answers while other tools are failingdirectives adds timelineTable (all 16 rows of BOD 26-04 Table 1, with remediationTimelineDays and forensicTriageRequired), definitions, and supersedes; sources reports the KEV catalogVersion, the advisory index's ready / documentCount / syncStatus, the SSVC cache TTL, and the cached feed windowscisa_check_cve_status toolcveIds per call, answered from the cached KEV snapshot with no upstream request; a CVE outside KEV comes back inKev: false, not as an errordateAdded, dueDate, daysUntilDue, overdue, requiredAction, knownRansomwareCampaignUse, forensicTriage, cwes[], every URL in the entry's notes as references[] typed by kind, and a three-state directive (BOD 26-04, BOD 22-01, or null); the response echoes the catalog snapshot and the asOf date the deadlines were computed againstdetail: "summary" trims each in-KEV record to the triage fields (dates, deadline status, directive, vendor and product labels, ransomware and forensic-triage flags), which keeps a full 200-CVE batch compact; full is the defaultcisa_search_kev toolvendorProject and product (CISA's own labels, not CPE names), nameContains, cwe, cveIdPrefix, dateAddedFrom / dateAddedTo, dueBefore / dueAfter, overdue, ransomware, forensicTriage, directive (BOD 26-04 / BOD 22-01 / none); up to 100 per page (default 25) with an opaque cursor. cwe and cveIdPrefix normalize case and surrounding whitespacedateAdded (default) or dueDate and reports totalCount and appliedFilters; a zero-hit result names the filter that matches nothing on its own and what dropping it restores, or the filters whose removal restores results and how manynameContains matches the letters a-z and the digits 0-9 after folding case and accents and spelling letters such as ß, æ, and ø as ss, ae, and o; any other letter or digit, such as a word in another script, is dropped, and the notice names it and the tokens actually searchedcatalog_unavailable (retryable), invalid_date_range, empty_search_textcisa_get_ssvc toolcveIds per call, each a live Vulnrichment lookup, plus assetExposure: publicly_exposed, not_publicly_exposed, or unknown (default, returns both arms)found, exploitation, automatable, technicalImpact, cvss and cwes where CISA published them, and bod2604.timelines[] (tableRow, remediationTimelineDays, forensicTriageRequired); a KEV entry adds kevAssigned, plus assignmentAgrees when an exposure was statedfound: false with guidance; enrichment_source_unavailable (retryable) fires only when every fetch failscisa_search_ics_advisories toolq over titles, vendor names, and product names, plus vendor, product, cve, cwe, inKev, cvssMin / cvssMax, severity, sector, series (ICSA / ICSMA), publisher (coordinator / other), publishedFrom / publishedTo, revisedFrom / revisedTo; sorts by revised (default), published, maxCvss, or relevance (needs q); up to 50 per page (default 20) with an opaque cursor. cve and cwe normalize case and surrounding whitespaceadvisoryId, up to 20 cves with cveCount, kevCves, maxCvss with severityDerived, sectors, url, csafUrl, and attribution; without inKev, an unloaded KEV snapshot leaves kevCves out and says so rather than failingtotalCount and appliedFilters; a zero-hit result names the filter that matches nothing on its own and what dropping it restores, or the filters whose removal restores results and how manyvendor and product are case-insensitive substrings matched literally, non-ASCII capitals included, so a label copied from a result matches its own advisoriesmirror_not_ready and catalog_unavailable (retryable), mirror_unavailable, invalid_cvss_range, invalid_date_range, relevance_sort_without_query, empty_search_textcisa_get_advisory tooladvisoryId (optional revision suffix; case, surrounding whitespace, and a trailing .json are normalized, and every ID comes back in its uppercase form), optional sections (advisory, summary, products, vulnerabilities, revisionHistory, references, acknowledgments), and optional cves to narrow vulnerabilities to named entrieskind: "full", or kind: "outline" when a document read without sections exceeds the 24,000-byte budget: per-section byte sizes plus the CVE IDs in vulnerabilities, for a stateless re-callfound: false with guidance, indexCheckpoint, and indexLastSyncedAt; the guidance says when the ID's own date is later than the last sync, so the advisory may be newer than the indexmirror_not_ready (retryable), mirror_unavailable, unknown_section, unknown_cve, cves_need_vulnerabilities_sectioncisa_get_alerts toolfeed: advisories (default), alerts, or ics; limit up to 30, the upstream window size; since (YYYY-MM-DD) filters within that window and cannot reach past ittitle, link, pubDate, summary (HTML stripped, capped at 1,200 characters, flagged by summaryTruncated), and on ICS items an advisoryId for cisa_get_advisory; window reports itemCount, oldest, newest, and upstreamWindowSize. Typed error: feed_unavailable (retryable)cisa://kev/{cveId} resourceapplication/json, the same shape as a cisa_check_cve_status result under the default detail: "full"; a CVE not in KEV is a not-found errorcveId completes from the snapshot, up to 100 suggestionscisa://advisory/{advisoryId} resourceapplication/json, with the same 24,000-byte outline-on-overflow as cisa_get_advisory; the template takes no sections or cves, so follow an outline up with the tooladvisoryId completes from the index, up to 100 suggestionsBuilt on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
CISA-specific:
Agent-friendly output:
catalog version and asOf date, the index checkpoint behind a search, and url, csafUrl, and attribution on every advisorydirective, typed references[].kind, found / inKev booleans, severityDerived on a band upstream never published, and typed error reasons with recovery hintsA public instance is available at https://cisa-cybersecurity.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
Add the following to your MCP client configuration file. No API key is required.
Or with npx (no Bun required):
Or with Docker:
For Streamable HTTP, set the transport and start the server:
Every variable is optional; the server runs correctly with none of them set.
| Variable | Description | Default |
|---|---|---|
CISA_KEV_REFRESH_CRON | Cron for the KEV conditional-refresh poll, on every transport. off disables it; an invalid expression fails startup. | */30 * * * * |
CISA_CSAF_MIRROR_PATH | Filesystem path to the ICS advisory SQLite index. | <user cache dir>/cisa-cybersecurity-mcp-server/csaf.sqlite3 |
CISA_CSAF_MIRROR_AUTO_INIT | Seed the index in the background when it has never synced, and re-ingest one an older version built. | true |
CISA_CSAF_REFRESH_CRON | Cron for the incremental advisory refresh, on every transport; it also runs once at startup. off disables both; an invalid expression fails startup. | 17 */6 * * * |
CISA_VULNRICHMENT_CACHE_TTL_SECONDS | TTL for a cached SSVC record; negative results use one sixth of it. | 21600 |
CISA_FEED_CACHE_TTL_SECONDS | TTL for a parsed RSS feed window. | 900 |
CISA_HTTP_TIMEOUT_MS | Per-request timeout for every upstream fetch, in ms. | 30000 |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_SESSION_MODE | HTTP session mode: stateless, stateful, or auto. | stateless |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
LOGS_DIR | Directory for log files (Node.js only). | <project-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend for the SSVC cache: in-memory, filesystem, or supabase. | in-memory |
OTEL_ENABLED | Enable OpenTelemetry. | false |
See .env.example for the full list of optional overrides.
Build and run:
Run checks and tests:
The two ICS tools and the advisory resource read a local SQLite index of the CSAF corpus at CISA_CSAF_MIRROR_PATH. Unset, it lives in your user cache directory — ~/Library/Caches on macOS, $XDG_CACHE_HOME or ~/.cache on Linux, %LOCALAPPDATA% on Windows — under cisa-cybersecurity-mcp-server/csaf.sqlite3, whatever directory the client starts the server from. On first run the server builds it in the background from one repository archive; until it is ready those surfaces return a retryable mirror_not_ready, and cisa_list_reference with topic sources shows progress. An index built by an older version re-ingests on the next start and keeps serving its current rows meanwhile. The .mcpb bundle needs nothing beyond that background seed. If the path cannot be opened (not writable, read-only, a missing directory, a file that is not a SQLite database), the ICS surfaces fail with a non-retryable mirror_unavailable, sources says why, and every other tool keeps working.
On every transport, the server refreshes the index once at startup and then on CISA_CSAF_REFRESH_CRON, and polls KEV on CISA_KEV_REFRESH_CRON. Server processes that share one index — every stdio session uses the default path — take a lease before seeding or refreshing it, so only one syncs at a time and the rest keep serving reads. For containers, CI, or seeding out of band (CISA_CSAF_MIRROR_AUTO_INIT=false, and CISA_CSAF_REFRESH_CRON=off to leave refreshes to you), run the scripts directly; they take the same lease:
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/cisa-cybersecurity-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them. The image sets CISA_CSAF_MIRROR_PATH=/usr/src/app/.mirror/csaf.sqlite3; mount a volume over /usr/src/app/.mirror so the advisory index survives a container recreation; the image ships the mirror:* scripts for docker exec <container> bun run mirror:refresh.
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point: wires the four services, schedules the refresh jobs, registers the surface. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). |
src/mcp-server/resources | Resource definitions (*.resource.ts). |
src/mcp-server/schemas | Output schemas and renderers shared by the KEV and advisory tools and resources. |
src/reference | Static reference data: BOD 26-04 Table 1, canonical sector names, CVSS bands. |
src/services/kev-catalog | KEV JSON feed: snapshot, derived indexes, conditional refresh. |
src/services/vulnrichment | Per-CVE SSVC enrichment fetch with a TTL cache. |
src/services/csaf-mirror | The ICS advisory index: schema, ingest, normalization, queries. |
src/services/cisa-feeds | The three RSS feeds, parsed and cached on a TTL. |
scripts/ | Build, checks, and the mirror:* commands. |
tests/ | Unit, integration, fuzz, and smoke tests mirroring src/. |
See CLAUDE.md/AGENTS.md for development guidelines and architectural rules, and docs/design.md for the as-built specification. The short version:
try/catch in tool logicctx.log for request-scoped logging, ctx.state for tenant-scoped storagesrc/mcp-server/*/definitions/index.tsIssues are welcome. Run checks and tests before submitting:
Apache-2.0 — see LICENSE for details.
The license covers this code, not the data it serves. KEV entries are US Government work in the public domain and Vulnrichment is CC0-1.0, but the CSAF repository declares no license and many advisories republish vendor text, so check reuse rights at each advisory's url before redistributing it. This project is not affiliated with or endorsed by CISA or the Department of Homeland Security.