Look up FIRST EPSS scores and daily history, find CVEs by threshold, and prioritize them with CVSS.
Look up FIRST EPSS exploitation scores and daily history, find CVEs by score threshold, and prioritize CVEs with caller-supplied CVSS via MCP. STDIO or Streamable HTTP.
Daily forecasts of observed CVE exploitation within the next 30 days from FIRST EPSS, generated by Empirical Security and published by FIRST. Look up scores, inspect daily movement, discover CVEs by score or year, and combine likelihood with caller-supplied CVSS severity. The public API needs no key or registration.
| Tool | Description |
|---|---|
epss_get_scores | Fetch current or historical EPSS probability and percentile for supplied CVEs |
epss_get_timeseries | Inspect daily history, exact-date changes, peaks, gaps, and model transitions |
epss_query_threshold | Discover CVEs by inclusive score bounds, CVE year, or recent dataset addition |
epss_prioritize_cves | Assign configurable remediation tiers using EPSS and caller-supplied CVSS severity |
epss_list_reference | Explain score interpretation, models, coverage, and prioritization conventions |
epss_get_scores toolcves as an array or a whitespace/comma/semicolon-delimited string; case and Unicode dashes are normalized. Process up to 500 unique identifiers from batchOffset (default 0). Optional date (2021-04-14 through today, UTC) selects a historical snapshot; omitted or blank means latest availableepss, percentile, date, and modelVersionSource (csv_header, published_schedule, or unavailable), ordered by EPSS descendingunscoredCves separates invalid_format from not_returned; a missing score is unknown, never zerosnapshot_unavailable identifies a dated snapshot FIRST cannot supply; select another date or omit it. A CVE omitted from a published snapshot remains not_returnedepss_get_timeseries toolcves / date / batchOffset inputs; process up to 50 unique identifiers per callcurrent with up to 30 preceding daily observations in series, plus missingDates, peak, and delta7d / delta30d, which need an exact calendar baseline and otherwise appear in unavailableDeltasmodelBoundariesCrossed and each comparison's crossesModelBoundary flag published model transitions; neither explains why a score movedpreviousAnchorDate for the preceding window without overlap; the pointer does not guarantee a published snapshotepss_query_threshold toolminEpss / maxEpss / minPercentile / maxPercentile bounds, year, and addedWithinDays intersect, with optional historical date; at least one filter and ordered bounds are required, otherwise invalid_filter_combination. addedWithinDays is dataset-addition recency and ignores datelimit 1–10,000 (default 100) plus offset. With year, candidateTotal counts broad candidates and total is null (totalRelation: "unknown"), so a filtered page can be empty with hasMore: truepagination.budgetLimited is true and withheldCount counts inspected matches omitted; examinedCount still counts every inspected candidate. nextOffset resumes at the first withheld match's original candidate position, including with year filteringlimit, provenance.scoreDates[0] as date, and nextOffset as offsetepss_prioritize_cves tool{ cve, cvssBaseScore? } entries (CVSS 0–10, caller-supplied) against latest or optional dated EPSS; process up to 500 unique CVEs from batchOffset. A duplicate CVE with differing CVSS, including missing versus supplied, fails as conflicting_cvss. Unavailable dated snapshots fail as snapshot_unavailableseverityThreshold: 7 and likelihood: { metric: "epss", threshold: 0.10 }, both inclusive; metric: "percentile" is an explicit alternative, and neither is FIRST guidanceact_now (high severity, high likelihood), scheduled (high, low), monitor (low, high), and defer (low, low); missing CVSS lands in epssOnly, missing EPSS in unscoredCves with its CVSS keptepss_list_reference tooltopic: interpretation, models, coverage, prioritization, or all (default)verifiedAt date; makes no upstream requestAll four dynamic tools bound each JSON-serialized content array and structuredContent object to 65,536 UTF-8 bytes, including notices, provenance, filters and pagination. Threshold discovery uses candidate continuation as described above; supplied-CVE tools use the batch continuation below.
Scores, timeseries and prioritization retain whole score rows, history items and unscored entries. processedCount still counts the workload processed, including invalid identifiers; returnedCount counts complete entries retained and withheldCount counts processed entries omitted by the byte limit. deferredCount counts entries never queried. withheldCves and deferredCves retain their array types; their *Complete flags explicitly identify previews when the full lists cannot fit. budgetLimited discloses any response shortening.
When nextBatchOffset is present, call the same tool with the original cves, nextBatchOffset as batchOffset, and provenance.scoreDates[0] as date (retain the requested date if no score date was returned). Prioritization also keeps every original cvssBaseScore, severityThreshold and likelihood. The offset counts normalized unique identifiers in first-occurrence input order, including invalid entries; score sorting does not change it. Continue until nextBatchOffset is absent. An offset past the end returns explicit empty accounting. Lists are for inspection; continuation through the original batch preserves entries even when lists are previews.
Built 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.
FIRST EPSS-specific:
Agent-friendly output:
verified, inferred, or unavailableunscoredCves and deferredCves distinguish omissions from work not queried. Transport failures fail the callAdd the following to your MCP client configuration file.
{
"mcpServers": {
"first-epss-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/first-epss-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"first-epss-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/first-epss-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"first-epss-mcp-server": {
"type": "stdio",
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/first-epss-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/first-epss-mcp-server.git
cd first-epss-mcp-server
bun install
cp .env.example .env
# Optional: edit framework transport, auth, logging, and telemetry settings
No server-specific settings are required.
| Variable | Description | Default |
|---|---|---|
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_HOST | HTTP bind address. | 127.0.0.1 |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_HTTP_ENDPOINT_PATH | HTTP MCP endpoint path. | /mcp |
MCP_SESSION_MODE | HTTP session mode: stateless, stateful, or auto. | auto; .env.example and Docker select stateless |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | debug; client examples and Docker select info |
LOGS_DIR | Directory for log files (Node.js only). | <app-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1. | in-memory |
OTEL_ENABLED | Enable OpenTelemetry. | false |
See .env.example for every server setting and the common framework overrides.
Build and run the production version:
# One-time build
bun run rebuild
# Run the built server
bun run start:http
# or
bun run start:stdio
Run checks and tests:
bun run devcheck # Lints, formats, type-checks, and more
bun run test # Runs the test suite
| Directory | Purpose |
|---|---|
src/mcp-server/tools | Five tool definitions, shared schemas, formatting, and history policy. |
src/services/epss | FIRST API requests, normalization, pacing, retries, and model metadata. |
tests/ | Unit and integration tests mirroring the src/ structure. |
See CLAUDE.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for logging, ctx.state for storagesrc/mcp-server/tools/definitions/index.ts barrel, which src/index.ts passes to createApp()Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.
Source-derived launch command. Check the maintainer’s required arguments and credentials before running:
npx -y @cyanheads/first-epss-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-first-epss-mcp-server": {
"command": "npx",
"args": [
"-y",
"@cyanheads/first-epss-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/first-epss-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.