CISA KEV with BOD 26-04 deadlines, SSVC prioritization, and the ICS advisory corpus (CSAF). Keyless.
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 outputs, made queryable: the Known Exploited Vulnerabilities catalog and the federal remediation deadlines it carries, the SSVC decision points CISA publishes per CVE in Vulnrichment, the full CSAF corpus of industrial control system advisories back to 2010, and CISA's current publication feeds. Check a scan's worth of CVE IDs against KEV in one call, find what is overdue for a vendor, work out what BOD 26-04 implies for an asset you own, and search or read ICS advisories by vendor, product, CVE, CWE, CVSS, sector, or whether they cover an exploited vulnerability. Every source is keyless and read-only. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
| Tool | Description |
|---|---|
cisa_list_reference | Decode the vocabulary the other tools take as input — BOD 26-04 timelines, KEV fields, SSVC values, sector names, ID formats, severity bands, and what data this server currently holds |
cisa_check_cve_status | Check up to 200 CVE IDs against the KEV catalog in one call — remediation deadlines, overdue status, ransomware and forensic-triage flags, and the directive each entry cites |
cisa_search_kev | Search the KEV catalog by vendor, product, CWE, date added, due date, overdue status, ransomware linkage, forensic-triage tier, or directive |
cisa_get_ssvc | Fetch the SSVC decision points CISA publishes per CVE and compute the BOD 26-04 remediation timeline they imply for a stated asset exposure |
cisa_search_ics_advisories | Search the ICS advisory corpus by vendor, product, CVE, CWE, KEV membership, CVSS range, severity, sector, series, or free text over titles and product names |
cisa_get_advisory | Read one ICS advisory in full — affected products with version ranges, per-CVE CVSS and CWE, remediations, sectors, and revision history — or just the vulnerability entries for the CVEs you name |
cisa_get_alerts | List what CISA has published recently 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 sourcesdirectives returns all sixteen rows of BOD 26-04 Appendix A, Table 1 as data — row number, the four decision points, the timeline label in the directive's own wording, remediationTimelineDays, and forensicTriageRequired — plus the directive's supporting definitions and what it supersedessources reports what this server currently holds: the KEV snapshot's catalogVersion and last check, the advisory index's readiness, document count and sync status, the SSVC cache TTL, and the cached feed windowscisa_check_cve_status toolinKev, and when present dateAdded, dueDate, daysUntilDue, overdue, requiredAction, knownRansomwareCampaignUse, forensicTriage, CISA's vendorProject / product labels, cwes[], and references[] typed by kind (nvd, cisa, bod_guidance, forensic_triage, vendor, other)directive is three-state — BOD 26-04, BOD 22-01, or null for the entries citing neither; it is never inferred from an entry's ageasOf date overdue and daysUntilDue were computed againstcisa_search_ics_advisories with cve, or inKev for every advisory covering a KEV CVEcisa_search_kev toolvendorProject, product, nameContains, cwe, cveIdPrefix, dateAddedFrom / dateAddedTo, dueBefore / dueAfter, overdue, ransomware, forensicTriage, and directive (BOD 26-04 / BOD 22-01 / none)dueDate or dateAdded; up to 100 per page (default 25) with an opaque cursor, and totalCount reports matches before pagingvendorProject and product are CISA's own free-text labels, not CPE names — cisa_list_reference with topic kev_fields carries the value domaindateAddedFrom adds a caveat: the feed carries no per-record modified timestamp, so the result covers additions in the window, not revisions to existing entriescatalog_unavailable (retryable), invalid_date_rangecisa_get_ssvc toolexploitation, automatable, and technicalImpact, plus the CVSS score and CWEs CISA contributes where presentassetExposure (publicly_exposed / not_publicly_exposed / unknown) is the one BOD 26-04 decision point CISA cannot publish; unknown returns both arms rather than a guessbod2604.timelines[] carries the Table 1 row, the label, remediationTimelineDays (null for the "Fix on system upgrade" rows), and forensicTriageRequired, under a fixed caveat that this is CISA's decision table applied to CISA's decision points and your stated exposure — not a compliance determinationkevAssigned reports CISA's own due date side by side, and assignmentAgrees surfaces a disagreement as a fact; the two are never reconciledfound: false with guidance naming the outcome, not an errorcisa_search_ics_advisories toolq over advisory titles, vendor names, and product names — tokens are AND-combined, FTS5 operators in the input are neutralized rather than honored, and a q with no word or number in it is an error rather than a match-everythingvendor and product (case-insensitive substrings, % and _ matched literally), cve, cwe (exact, against every vulnerability entry), inKev (true = covers at least one CVE in the KEV catalog, false = covers none), cvssMin / cvssMax, severity (NONE–CRITICAL), sector (the sixteen canonical names plus the Multiple sentinel), series (ICSA / ICSMA), publisher (coordinator = CISA-authored, other = republished vendor advisory), publishedFrom / publishedTo, revisedFrom / revisedTorevised (default), published, maxCvss, or relevance (requires q); up to 50 per page (default 20) with an opaque cursoradvisoryId for cisa_get_advisory, the first twenty CVEs, kevCves — every CVE the advisory covers that is in KEV, from its full CVE list — the cisa.gov url, the raw csafUrl, and an attribution stringinKev, a KEV catalog that has not loaded leaves kevCves out and says so rather than failing the search; with inKev, the search waits for the catalog and echoes the KEV catalogVersion it used as kevCatalogVersion in appliedFiltersmirror_not_ready (retryable), catalog_unavailable (retryable, inKev only), invalid_cvss_range, invalid_date_range, relevance_sort_without_query, empty_search_textcisa_get_advisory tooladvisoryId is case-insensitive and accepts both real suffix forms (a single letter a–f, or a numeric -N); a trailing .json is strippedadvisory, summary, products, vulnerabilities, revisionHistory, references, acknowledgmentsvulnerabilities section holds, instead of the whole record — re-call with sections to pull what you need; the re-call is statelesscves narrows the vulnerabilities section to the named entries — one CVE out of the 352 in the largest section is a few kilobytes instead of 905 KB. Alone it selects that section; with sections, the list must include vulnerabilitiesproducts arm carries every flattened version row — all 585 for the largest advisory — so sections: ["products"] always returns the whole listmirror_not_ready (retryable), unknown_section, unknown_cve, cves_need_vulnerabilities_section. An ID that is not in the index returns found: false with guidancecisa_get_alerts toolfeed selects advisories, alerts, or ics; limit tops out at 30 because that is the upstream window, not a server choicesince filters within the fetched window and cannot reach back beyond itwindow (itemCount, oldest, newest, upstreamWindowSize) and a caveat that the feed has no history, no pagination, and no server-side date filtersummary is the item description with HTML stripped and entities decoded, capped at 1,200 characters with summaryTruncated flagging the cutadvisoryId that chains straight into cisa_get_advisoryfeed_unavailable (retryable)cisa://kev/{cveId} resourceapplication/json, identical in shape to a cisa_check_cve_status resultcveId completes from the snapshot, up to 100 suggestionscisa://advisory/{advisoryId} resourceapplication/json, carrying the same 24 KB outline-on-overflow treatment cisa_get_advisory applies on a call with no sectionssections or cves parameter, so follow an outline up with cisa_get_advisory to request named sections or vulnerability entries — the outline lists the CVE IDs to choose fromadvisoryId 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:
asOf date that answered a KEV call, the index checkpoint behind a search, and a source URL plus attribution on every advisorydirective, typed references[].kind, found / inKev booleans, severityDerived on a band the upstream never published, and per-tool 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:
{
"mcpServers": {
"cisa-cybersecurity-mcp-server": {
"type": "streamable-http",
"url": "https://cisa-cybersecurity.caseyjhand.com/mcp"
}
}
}
Add the following to your MCP client configuration file. No API key is required.
{
"mcpServers": {
"cisa-cybersecurity-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/cisa-cybersecurity-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"cisa-cybersecurity-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/cisa-cybersecurity-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"cisa-cybersecurity-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"-v", "cisa-mirror:/usr/src/app/.mirror",
"ghcr.io/cyanheads/cisa-cybersecurity-mcp-server:latest"
]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
git clone https://github.com/cyanheads/cisa-cybersecurity-mcp-server.git
cd cisa-cybersecurity-mcp-server
bun install
cisa_search_ics_advisories and cisa_get_advisory read a local SQLite index of the CSAF advisory corpus. CISA offers no search endpoint for it, and the corpus exists only as thousands of individual documents, so the index is what makes the two tools possible.
It seeds itself. On first run the server fetches one repository archive in the background and builds the index in about ten seconds, leaving roughly 55 MB at CISA_CSAF_MIRROR_PATH (.mirror/csaf.sqlite3 by default). Nothing else waits on it: the KEV, SSVC, and alert tools serve from the first request, and until the index is ready the two ICS tools report that state through a retryable mirror_not_ready error rather than an empty result. cisa_list_reference with topic sources reports the progress.
An index built by an older server version rebuilds itself the same way: on the next start the server re-ingests the archive in the background, over the existing index, which keeps answering queries from its current rows until the rebuild finishes. No manual step is needed; mirror:verify reports when an index is due for one.
Under HTTP transport the index refreshes itself on a cron. Three scripts cover explicit control — a stdio deployment, a container, a CI gate:
bun run mirror:init # full build, idempotent, safe to re-run after an interrupt
bun run mirror:refresh # incremental — fetches only the documents whose revision date moved
bun run mirror:verify # readiness, sync status, checkpoint, count, content version, SQLite integrity; exits non-zero on failure
Set CISA_CSAF_MIRROR_AUTO_INIT=false where seeding runs out of band.
Docker: mount a volume over CISA_CSAF_MIRROR_PATH so a container recreation does not rebuild the index, then run the scripts with docker exec <container> bun run mirror:refresh. The image ships them for exactly that.
Claude Desktop (.mcpb): the bundle has no shell, so the automatic background seed is the whole story — install it and the ICS tools come online shortly after the first launch.
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. HTTP transport only; empty disables the in-process schedule. | */30 * * * * |
CISA_CSAF_MIRROR_PATH | Filesystem path to the ICS advisory SQLite index. | .mirror/csaf.sqlite3 |
CISA_CSAF_MIRROR_AUTO_INIT | Seed the advisory index in the background at startup when it has never synced, and re-ingest it when an older server version built it. | true |
CISA_CSAF_REFRESH_CRON | Cron for the incremental advisory refresh. HTTP transport only; empty disables it. | 17 */6 * * * |
CISA_VULNRICHMENT_CACHE_TTL_SECONDS | TTL for a cached SSVC record. Negative results use one sixth of this. | 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 milliseconds. | 30000 |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | Port for the HTTP server. | 3010 |
MCP_HTTP_ENDPOINT_PATH | HTTP endpoint path where the server is mounted. | /mcp |
MCP_SESSION_MODE | HTTP session mode. This server ships stateless — no handler collects input mid-request. | stateless |
MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (RFC 5424). | info |
LOGS_DIR | Directory for log files (Node.js only). | <project-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend. | in-memory |
OTEL_ENABLED | Enable OpenTelemetry instrumentation. | false |
See .env.example for the full list of optional overrides.
Build and run:
# One-time build
bun run rebuild
# Run the built server
bun run start:stdio
# or
bun run start:http
Run checks and tests:
bun run devcheck # Lint, format, typecheck, security
bun run test # Vitest test suite
bun run lint:mcp # Validate MCP definitions against spec
docker build -t cisa-cybersecurity-mcp-server .
docker run --rm -p 3010:3010 -v cisa-mirror:/usr/src/app/.mirror cisa-cybersecurity-mcp-server
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. Mount a volume over /usr/src/app/.mirror so the advisory index survives a container recreation.
There is no Cloudflare Workers deployment: the advisory index needs embedded SQLite and a persistent filesystem, and an isolate has neither.
| Source | What it provides | Status |
|---|---|---|
| KEV catalog | Exploited-in-the-wild CVEs with federal remediation deadlines | US Government work, public domain under 17 U.S.C. §105 |
| Vulnrichment | SSVC decision points, CVSS, and CWE CISA publishes per CVE | CC0-1.0 |
| CSAF ICS advisories | The machine-readable advisory corpus back to 2010 | No declared license — see below |
| Advisory and alert feeds | CISA's current publication windows | Relayed with a link to each item |
The CSAF repository declares no license, and a substantial share of its advisories are vendor advisories CISA republished with the vendor's own text and revision history. This server therefore makes no public-domain claim over advisory content: every advisory a tool or resource returns carries its cisa.gov url, its raw csafUrl, and an attribution string naming the publisher and, for a republication, the originating vendor. Resolve reuse rights against the source before redistributing advisory text.
This project is not affiliated with, endorsed by, or sponsored by CISA or the Department of Homeland Security. It uses no DHS seal, no CISA logo, and no agency branding.
These are properties of the upstream sources, not of this server. Each is stated in the description or output of the tool it affects.
dateAdded but no per-record modified timestamp, so a revised dueDate or requiredAction on an existing entry cannot be distinguished from an unchanged one. Additions are queryable; revisions are not.directive is null for them rather than inferred from an entry's age.Exploitation value can predate a KEV addition that contradicts it.| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — wires the four services, schedules the refresh loops, registers the surface. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). Seven tools across KEV, SSVC, ICS advisories, and the feeds. |
src/mcp-server/resources | Resource definitions (*.resource.ts). KEV entry and ICS advisory templates. |
src/mcp-server/schemas | Shared output schemas and format() renderers for KEV records and advisories. |
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 three mirror:* lifecycle commands. |
tests/ | Unit, integration, fuzz, and smoke tests mirroring src/. |
See CLAUDE.md/AGENTS.md for development guidelines and architectural rules. 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:
bun run devcheck
bun run test
Apache-2.0 — see LICENSE for details.
Source-derived launch command. Check the maintainer’s required arguments and credentials before running:
npx -y @cyanheads/cisa-cybersecurity-mcp-serverMerge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.
{
"mcpServers": {
"io-github-cyanheads-cisa-cybersecurity-mcp-server": {
"command": "npx",
"args": [
"-y",
"@cyanheads/cisa-cybersecurity-mcp-server"
]
}
}
}Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.
Claude Desktop setup referenceio.github.cyanheads/cisa-cybersecurity-mcp-server works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.
~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.~/.cursor/mcp.jsonRestart Cursor for changes to take effect..vscode/mcp.jsonReload VS Code window for changes to take effect.~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect..mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.