echelongraph-mcp 2.3.4 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +136 -0
- package/README.md +304 -46
- package/dist/http.js +240 -0
- package/dist/httpPolicy.js +244 -0
- package/dist/index.js +123 -46
- package/dist/prompts.js +137 -0
- package/dist/resources.js +153 -0
- package/dist/runtime.js +65 -0
- package/dist/tools/check_affected.js +269 -0
- package/dist/tools/check_sbom.js +329 -0
- package/dist/tools/cve_intel.js +276 -0
- package/dist/tools/epss_history.js +125 -0
- package/dist/tools/get_cwe.js +148 -0
- package/dist/tools/kev_recent.js +190 -0
- package/dist/tools/vendor_advisories.js +316 -0
- package/package.json +5 -2
- package/server.json +18 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Every version of `echelongraph-mcp` published to npm, newest first. Dates are the npm publish
|
|
4
|
+
dates (UTC). Issue numbers refer to EchelonGraph's tracker. Since 2.3.3 each version is published
|
|
5
|
+
with a provenance attestation by the release workflow of
|
|
6
|
+
[github.com/echelongraph/echelongraph-mcp](https://github.com/echelongraph/echelongraph-mcp),
|
|
7
|
+
from the commit tagged `v<version>`.
|
|
8
|
+
|
|
9
|
+
## 2.5.0 — 2026-10-04
|
|
10
|
+
|
|
11
|
+
- Four prompts (`triage_cve`, `kev_weekly_brief`, `am_i_affected`, `sbom_review`) and three
|
|
12
|
+
resources (`echelongraph://methodology`, `echelongraph://sources`, `cve://{cve_id}`), in both
|
|
13
|
+
protocol eras (#2722).
|
|
14
|
+
- README: a quick start for Claude Desktop, Claude Code, Cursor, VS Code, Windsurf and Cline,
|
|
15
|
+
and for the hosted endpoint; a title and an example question per tool; what is sent where;
|
|
16
|
+
the API's and the hosted endpoint's rate limits; the HTTP entrypoint's settings.
|
|
17
|
+
- This changelog, now in the npm package.
|
|
18
|
+
- `server.json`: a description covering the 14 tools, and the icon.
|
|
19
|
+
|
|
20
|
+
## 2.4.0 — 2026-10-04
|
|
21
|
+
|
|
22
|
+
Nine new tools, 14 in all (#2726):
|
|
23
|
+
|
|
24
|
+
- `kev_recent`: the CVEs CISA has added to its KEV catalog, newest first, with due date,
|
|
25
|
+
ransomware use, EPSS and when EchelonGraph first saw each in the catalog; filter by date
|
|
26
|
+
range, ransomware and vendor, and page with a cursor (#2717).
|
|
27
|
+
- `epss_history`: how one CVE's EPSS score has changed, as EchelonGraph recorded it, change-only
|
|
28
|
+
and never a daily series (#2718).
|
|
29
|
+
- `check_affected`: whether a product (CPE path) or a registry package at a version is affected
|
|
30
|
+
by known CVEs; `assessed` false is never "not affected" (#2716).
|
|
31
|
+
- `check_sbom`: up to 200 purls, or a CycloneDX or SPDX JSON document read on your machine,
|
|
32
|
+
checked against the advisory corpus, one verdict per component (#2721).
|
|
33
|
+
- `cve_intel`: weakness (CWE), public exploit code, affected packages and fixed versions for one
|
|
34
|
+
CVE (#2720).
|
|
35
|
+
- `get_cwe`: one CWE and the CVEs classified under it, CISA-KEV-listed first (#2720).
|
|
36
|
+
- `vendor_advisories_for_cve`, `get_vendor_advisory`, `search_vendor_advisories`: the
|
|
37
|
+
vendor-published advisories that name a CVE, one advisory in full, and search (#2719).
|
|
38
|
+
|
|
39
|
+
Also:
|
|
40
|
+
|
|
41
|
+
- An HTTP entrypoint, `dist/http.js` (`npm run start:http`): the same tools over stateless
|
|
42
|
+
Streamable HTTP on `POST /mcp`, in both protocol eras, with an Origin policy, a per-client limit
|
|
43
|
+
and a body cap. It is what EchelonGraph's hosted endpoint, `https://mcp.echelongraph.io/mcp`,
|
|
44
|
+
runs (#2316).
|
|
45
|
+
- `ECHELONGRAPH_MCP_UA`: one product token put ahead of the package's User-Agent, so an
|
|
46
|
+
automated caller such as a monitor is told apart from people using the server (#2724).
|
|
47
|
+
- Inputs a user types for `check_affected`, `kev_recent` and `search_vendor_advisories` travel in
|
|
48
|
+
`X-EG-*` request headers, never in the URL (#1983).
|
|
49
|
+
- `get_cve` relays NVD's configuration tree (`cpe_configurations`) when the API serves it, and
|
|
50
|
+
says when it does not; a missing tree does not mean no product is affected (#2720).
|
|
51
|
+
- A failure message names the HTTP method of a request that is not a GET (`check_sbom`'s POST).
|
|
52
|
+
- Listing files for directories (`glama.json`, a `Dockerfile`, an MCPB manifest); none of them is
|
|
53
|
+
in the npm package (#2723).
|
|
54
|
+
|
|
55
|
+
## 2.3.4 — 2026-09-30
|
|
56
|
+
|
|
57
|
+
- `cve_summary` says what `summary.nvd_critical` … `summary.nvd_none` count (the same active
|
|
58
|
+
CVEs by NVD's severity label, as provenance, never EchelonGraph's severity) and what
|
|
59
|
+
`summary.rejected` counts (withdrawn records, outside the total) (#2641).
|
|
60
|
+
|
|
61
|
+
## 2.3.3 — 2026-09-30
|
|
62
|
+
|
|
63
|
+
- The first release published by the public repo's release workflow, with npm provenance. No
|
|
64
|
+
change to the server beyond the version (#2317).
|
|
65
|
+
|
|
66
|
+
## 2.3.2 — 2026-09-30
|
|
67
|
+
|
|
68
|
+
- `cve_summary`: `summary.none` is labelled as CVEs not yet scored, never as a severity rating of
|
|
69
|
+
None (#2610).
|
|
70
|
+
|
|
71
|
+
## 2.3.1 — 2026-09-30
|
|
72
|
+
|
|
73
|
+
- `get_cve` and `search_cves` label a CVE EchelonGraph has not scored NOT YET SCORED (a rejected
|
|
74
|
+
record NOT SCORED), never as an EchelonGraph score of 0 (#2535).
|
|
75
|
+
|
|
76
|
+
## 2.3.0 — 2026-09-30
|
|
77
|
+
|
|
78
|
+
- `exposure_radar` reads a fifth radar, `mcp_servers`: hostnames named like an MCP server in
|
|
79
|
+
EchelonGraph's own Certificate Transparency feed, by their latest verdict on record (RFC 9728
|
|
80
|
+
protected, pending re-adjudication, or not assessed by reason), with the protocol era and
|
|
81
|
+
transport splits. An answer that does not say `service` `mcp` is refused, never relayed (#2315).
|
|
82
|
+
|
|
83
|
+
## 2.2.0 — 2026-09-28
|
|
84
|
+
|
|
85
|
+
- `not_assessed` no longer says its numbers are not findings: a count it relays is what the
|
|
86
|
+
source holds on record, undated (#2465).
|
|
87
|
+
- The last text block stops repeating what an earlier block already says (#2467).
|
|
88
|
+
|
|
89
|
+
## 2.1.0 — 2026-09-27
|
|
90
|
+
|
|
91
|
+
- `cve_exposure` answers `not_assessed`, with `measured_at` null: its `last_seen` is when
|
|
92
|
+
EchelonGraph last wrote a row, not when a service was observed (#2439).
|
|
93
|
+
- Every result's last text block carries the structured result, for clients that pass only
|
|
94
|
+
`content` to the model (#2440).
|
|
95
|
+
|
|
96
|
+
## 2.0.0 — 2026-09-27
|
|
97
|
+
|
|
98
|
+
- Both MCP protocol eras on stdio: `server/discover` (2026-07-28) and `initialize` (2025-11-25
|
|
99
|
+
and earlier) (#2311).
|
|
100
|
+
- Every tool has a title, annotations (`readOnlyHint` and the rest) and an `outputSchema`; every
|
|
101
|
+
result carries `structuredContent` with `state`, `measured_at`, `method`, `coverage`,
|
|
102
|
+
`freshness` and `notes` (#2313).
|
|
103
|
+
- `@modelcontextprotocol/server` 2.1.0 replaces `@modelcontextprotocol/sdk` 1.x; Node.js 20 or
|
|
104
|
+
later (Node.js 18 dropped).
|
|
105
|
+
|
|
106
|
+
## 1.0.4 — 2026-09-27
|
|
107
|
+
|
|
108
|
+
- `exposure_radar` labels every number of every radar by what it counts, leaves out and names a
|
|
109
|
+
field it cannot label, and never relays a CVE the radar does not track as "0 exposed" (#2313).
|
|
110
|
+
- `exposure_radar` relays each radar's `last_run_at`, its last completed check (#2335).
|
|
111
|
+
- The MIT `LICENSE` file ships in the package.
|
|
112
|
+
|
|
113
|
+
## 1.0.3 — 2026-09-27
|
|
114
|
+
|
|
115
|
+
- `exposure_radar` regroups the shadow-AI radar's numbers by what each counts
|
|
116
|
+
(`confirmed_exposed`, `observed`, `authentication`) and relays no number unlabelled (#2307).
|
|
117
|
+
|
|
118
|
+
## 1.0.2 — 2026-09-27
|
|
119
|
+
|
|
120
|
+
- Freshness is the radar's last completed cycle, not a follower instance's zero time (#2307).
|
|
121
|
+
- Every Shodan-derived field states Shodan's ownership and copyright (#2306).
|
|
122
|
+
|
|
123
|
+
## 1.0.1 — 2026-09-27
|
|
124
|
+
|
|
125
|
+
- An API failure is an error result, never a success with empty data (#1874).
|
|
126
|
+
- Wording: no claim the code does not support; exposure counts are internet-facing services
|
|
127
|
+
(distinct ip:port), attributed as derived from Shodan data (#2306).
|
|
128
|
+
- `cve_exposure` refuses an id that is not a CVE ID, and reports a CVE outside the radar's
|
|
129
|
+
tracked set as not assessed, not 0 (#2313).
|
|
130
|
+
- `mcpName` and `server.json` for the official MCP Registry (#1879); the handshake and the
|
|
131
|
+
User-Agent carry the package version.
|
|
132
|
+
|
|
133
|
+
## 1.0.0 — 2026-06-25
|
|
134
|
+
|
|
135
|
+
- First release: `cve_summary`, `search_cves`, `get_cve`, `cve_exposure` and `exposure_radar`
|
|
136
|
+
over stdio.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# EchelonGraph MCP server
|
|
2
2
|
|
|
3
|
-
**CVE and internet-exposure data** for Claude, Cursor, Cline, and any
|
|
3
|
+
**CVE and internet-exposure data** for Claude, Cursor, VS Code, Cline, and any
|
|
4
4
|
[MCP](https://modelcontextprotocol.io) client, straight from
|
|
5
5
|
[EchelonGraph](https://echelongraph.io)'s free public feed.
|
|
6
6
|
|
|
@@ -10,18 +10,168 @@ internet-facing services (distinct ip:port) EchelonGraph's KEV-exposure radar ha
|
|
|
10
10
|
running a version that maps to the CVE. Exposure counts are derived from Shodan data.
|
|
11
11
|
Shodan data is owned by Shodan, which holds its copyright (© Shodan).
|
|
12
12
|
|
|
13
|
+
Its 14 tools look up one CVE (record, exploit code and fixed versions, EPSS history, vendor
|
|
14
|
+
advisories, exposure footprint), list CISA's newest KEV additions, check whether a product or
|
|
15
|
+
package version is affected, check an SBOM, and read a CWE and its CVEs. Every result says how
|
|
16
|
+
it was measured (`state`, `measured_at`, `method`, `coverage`, `freshness`, `notes`), so a model
|
|
17
|
+
cannot mistake an outage or an unassessed lookup for an all-clear.
|
|
18
|
+
|
|
19
|
+
It also serves four prompts, ready-made workflows a client can show as slash commands
|
|
20
|
+
(`triage_cve`, `kev_weekly_brief`, `am_i_affected`, `sbom_review`; see "Prompts"), and three
|
|
21
|
+
resources (`echelongraph://methodology`, `echelongraph://sources` and `cve://{cve_id}`; see
|
|
22
|
+
"Resources").
|
|
23
|
+
|
|
13
24
|
Free and keyless: no API key, no auth, read-only. The server makes no request other than the
|
|
14
25
|
API call a tool needs to answer.
|
|
15
26
|
|
|
27
|
+
- npm: [`echelongraph-mcp`](https://www.npmjs.com/package/echelongraph-mcp) · Official MCP
|
|
28
|
+
Registry: `io.echelongraph/echelongraph-mcp` · Docs: <https://echelongraph.io/pulse/mcp>
|
|
29
|
+
- Source: <https://github.com/echelongraph/echelongraph-mcp> · Changes:
|
|
30
|
+
[CHANGELOG.md](CHANGELOG.md) · Security: [SECURITY.md](SECURITY.md)
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
The local server runs through `npx` and needs Node.js 20 or later; nothing is installed
|
|
35
|
+
globally. Add one of the blocks below to your client, restart it, then ask: *"Is
|
|
36
|
+
CVE-2023-44487 actively exploited, and how many exposed services does EchelonGraph's radar
|
|
37
|
+
have on record for it?"*
|
|
38
|
+
|
|
39
|
+
### Claude Desktop
|
|
40
|
+
|
|
41
|
+
Settings → Developer → Edit Config opens `claude_desktop_config.json`
|
|
42
|
+
(macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`; Windows:
|
|
43
|
+
`%APPDATA%\Claude\claude_desktop_config.json`). Add:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"mcpServers": {
|
|
48
|
+
"echelongraph": {
|
|
49
|
+
"command": "npx",
|
|
50
|
+
"args": ["-y", "echelongraph-mcp"]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Claude Code
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
claude mcp add --transport stdio echelongraph -- npx -y echelongraph-mcp
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Add `--scope user` before the `--` to make it available in every project, or `--scope project`
|
|
63
|
+
to share it through the project's `.mcp.json`.
|
|
64
|
+
|
|
65
|
+
### Cursor
|
|
66
|
+
|
|
67
|
+
`~/.cursor/mcp.json` (every project) or `.cursor/mcp.json` (one project):
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"echelongraph": {
|
|
73
|
+
"command": "npx",
|
|
74
|
+
"args": ["-y", "echelongraph-mcp"]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### VS Code
|
|
81
|
+
|
|
82
|
+
`.vscode/mcp.json` in the workspace (VS Code's key is `servers`, not `mcpServers`):
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"servers": {
|
|
87
|
+
"echelongraph": {
|
|
88
|
+
"command": "npx",
|
|
89
|
+
"args": ["-y", "echelongraph-mcp"]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
or from a terminal:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
code --add-mcp '{"name":"echelongraph","command":"npx","args":["-y","echelongraph-mcp"]}'
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Windsurf, Cline and other clients
|
|
102
|
+
|
|
103
|
+
Clients that read an `mcpServers` block (Windsurf's `mcp_config.json`, Cline's MCP settings,
|
|
104
|
+
and most others) take the same block as Cursor above.
|
|
105
|
+
|
|
106
|
+
### Remote (no install)
|
|
107
|
+
|
|
108
|
+
The same 14 tools, four prompts and three resources are served over Streamable HTTP at:
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
https://mcp.echelongraph.io/mcp
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Keyless, no sign-in, stateless; both protocol eras. The hosted endpoint is being rolled out:
|
|
115
|
+
until `https://mcp.echelongraph.io/health` answers `{"status":"ok",…}`, use the `npx` setup
|
|
116
|
+
above.
|
|
117
|
+
|
|
118
|
+
- **claude.ai** (Free, Pro, Max, Team and Enterprise plans):
|
|
119
|
+
Customize → Connectors → **+ Add** → **Add custom connector**. Name it `EchelonGraph`, paste
|
|
120
|
+
the URL, choose **No sign in**, and add it. On Team and Enterprise an Owner adds it for the
|
|
121
|
+
organization first, and each member then connects it. Free plans allow one custom connector.
|
|
122
|
+
- **Claude Code**:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
claude mcp add --transport http echelongraph https://mcp.echelongraph.io/mcp
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- **Cursor** (`mcp.json`):
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{ "mcpServers": { "echelongraph": { "url": "https://mcp.echelongraph.io/mcp" } } }
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
- **VS Code** (`.vscode/mcp.json`):
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{ "servers": { "echelongraph": { "type": "http", "url": "https://mcp.echelongraph.io/mcp" } } }
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
- **Windsurf** (`mcp_config.json`):
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{ "mcpServers": { "echelongraph": { "serverUrl": "https://mcp.echelongraph.io/mcp" } } }
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The hosted endpoint runs this package's code (`dist/http.js`, below) against the same public
|
|
147
|
+
API, so its answers are the npm package's answers. What it sees and logs is under "Privacy",
|
|
148
|
+
and its per-client limit under "Rate limits".
|
|
149
|
+
|
|
16
150
|
## Tools
|
|
17
151
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
|
22
|
-
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
152
|
+
Every tool is read-only (`readOnlyHint: true`) and declares a title and an `outputSchema`.
|
|
153
|
+
The example questions are ones a client can answer with that tool alone.
|
|
154
|
+
|
|
155
|
+
| Tool | Title | What it answers | Example question |
|
|
156
|
+
|---|---|---|---|
|
|
157
|
+
| `cve_summary` | CVE feed summary | Counts of active CVEs by severity band, the count with no severity band from any source (`summary.none`, sent again as `summary.unscored`: CVEs not yet scored, not a rating of None), the same CVEs counted by NVD's severity label, as provenance (`summary.nvd_critical` to `summary.nvd_none`), the rejected (withdrawn) records outside the total (`summary.rejected`), and when the feed was last updated. | *How many critical CVEs does the feed hold, and when was it last updated?* |
|
|
158
|
+
| `search_cves` | Search CVEs | Search/filter CVEs (severity, min CVSS, text, sort) with EchelonGraph scores and `score_assessed`; the note names each row not yet scored. | *Find critical CVEs that mention Tomcat with a CVSS of 9 or more.* |
|
|
159
|
+
| `get_cve` | CVE detail | Full record for one CVE: CVSS v3 and (when scored) v4, the EchelonGraph score and its confidence, whether EchelonGraph has scored it (`score_assessed`), EPSS, CISA-KEV status and known ransomware use, the GitHub GHSA id, references, and its published, modified and `updated_at` times. | *What are CVE-2024-3094's CVSS, EPSS and CISA-KEV status?* |
|
|
160
|
+
| `cve_exposure` | Internet exposure for one CVE | Internet-exposure footprint for a CVE: exposed service count (distinct ip:port, the `exposed_hosts` field) + country/product breakdown, from the KEV-exposure radar. | *How many exposed services does EchelonGraph's radar have on record for CVE-2023-44487?* |
|
|
161
|
+
| `exposure_radar` | Exposure radar totals | Aggregate totals across the exposure radars: services running CISA-KEV CVEs; unauthenticated data stores and observability UIs, found through Shodan (LeakIX when Shodan query credits run low) and then confirmed by EchelonGraph's own identified check, which is not a pure read (on Redis it names its client; on ClickHouse its query lands in the server's query log); leaked credentials; shadow AI; and MCP servers found in EchelonGraph's own Certificate Transparency feed, by RFC 9728 verdict, protocol era and transport. Every number is labelled by what it counts, and a field the tool cannot label is left out and named. | *How many unauthenticated data stores has EchelonGraph's radar confirmed?* |
|
|
162
|
+
| `kev_recent` | Recent CISA KEV additions | The CVEs CISA has added to its Known Exploited Vulnerabilities catalog, newest first (`kev_added_date`), from EchelonGraph's copy of the catalog, polled from CISA every 5 minutes: due date, vendor, product, known ransomware use, EchelonGraph's severity, CVSS, EPSS and `eg_kev_tier`, and `our_first_seen_kev`. Filter by date range, ransomware and vendor; page with `limit` and `next_cursor`. Dated by `last_successful_fetch_at`, our last successful fetch of CISA's feed; the filters travel as request headers, never in the URL. | *Which CVEs has CISA added to KEV since 1 September, and which have known ransomware use?* |
|
|
163
|
+
| `epss_history` | EPSS change history for one CVE | How one CVE's EPSS score has changed, as EchelonGraph recorded it: one point per recorded change (`series_kind` `change_only`), never a daily series, with the value now and `series_starts_at`, when recording began; before it a missing point means not recorded, not unchanged. | *How has CVE-2023-44487's EPSS score changed, as EchelonGraph recorded it?* |
|
|
164
|
+
| `check_affected` | Am I affected? (product or package at a version) | Whether a product (its NVD CPE product token) or a registry package (`ecosystem` and `package`) at a given version is affected by known CVEs, from the matcher behind echelongraph.io/am-i-affected: `assessed` first (false: not evaluated, with `not_assessed_reason`, and a count of 0 then is not "not affected"), the matching CVEs with `kev_listed`, `ransomware`, `epss_score`, `effective_score` and `score_assessed`, and advisories it cannot decide counted as `undetermined_count`, never as safe. What you look up travels in request headers, never in the URL. | *Is openssl 3.0.0 affected by known CVEs? Is lodash 4.17.15 on npm?* |
|
|
165
|
+
| `check_sbom` | Check an SBOM against the advisory corpus | A dependency list checked against EchelonGraph's advisory corpus (OSV.dev records), one verdict per component: `affected`, `not_affected`, `undetermined` or `not_assessed`, each with its `not_assessed_reason`. Pass up to 200 purls, or a CycloneDX JSON or SPDX JSON document: the purls are read from it on your machine and only they are sent, in a POST body; the document is not. A deb, apk or rpm purl without a `distro` qualifier naming its release is not assessed (`distro_release_unknown`): EchelonGraph does not guess a release. Only `not_affected` is clean. No ranking, no score. | *Check this CycloneDX SBOM against the advisory corpus.* |
|
|
166
|
+
| `cve_intel` | CVE weakness, exploits and packages | Weakness, public exploit code, affected packages and fixed versions for one CVE, from EchelonGraph's per-CVE enrichment: `cwes`, `exploits` (at most 10, verified first) with `exploits_total`, `exploits_capped`, `exploits_by_kind` and `exploits_by_status`, `affected_packages`, `fixed_versions` and `timeline`. `verified_status` is the label stored with each reference, not a guarantee that the exploit works. An empty `exploits` list is not evidence that no public exploit exists; a section the API could not read is named in `coverage.sections_failed`, never relayed as an empty list. | *Is there public exploit code for CVE-2021-44228, and which versions fix it?* |
|
|
167
|
+
| `get_cwe` | CWE and its CVEs | One CWE (weakness class) and the CVEs classified under it: `name` and `description` from the MITRE CWE catalog EchelonGraph embeds, `total`, and one page of 50 `cves`, ordered as `order` states (CISA-KEV-listed first, then EchelonGraph score). A `total` of 0 says that no CVE in EchelonGraph's feed is classified under that CWE, not that none exists. | *Which CVEs are classified under CWE-79, CISA-KEV-listed first?* |
|
|
168
|
+
| `vendor_advisories_for_cve` | Vendor advisories for one CVE | The vendor-published advisories (Microsoft MSRC, Red Hat, Cisco, Palo Alto Networks, GitHub GHSA and the other feeds EchelonGraph polls) that name one CVE, newest first, at most 20. | *Which vendor advisories name CVE-2024-3400?* |
|
|
169
|
+
| `get_vendor_advisory` | Vendor advisory detail | One vendor advisory in full: description, severity, `cve_ids` and the subset with a CVE record here (`known_cve_ids`), `affected_products`, `remediation` and `references`. | *Show one of those advisories in full: affected products, remediation and references.* |
|
|
170
|
+
| `search_vendor_advisories` | Search vendor advisories | Search vendor advisories by text (title, description, vendor, advisory ID, products, CVE IDs), vendor, severity and whether they name a CVE. The search text is sent in a request header, never in the URL. | *Search vendor advisories for FortiOS, critical severity only.* |
|
|
171
|
+
|
|
172
|
+
The three vendor-advisory tools relay `vendor_published_at` (the vendor's date), `our_first_seen_at`
|
|
173
|
+
(when EchelonGraph first recorded the advisory) and `withdrawn` (the vendor rescinded it; the note
|
|
174
|
+
names each one, and the search leaves them out).
|
|
25
175
|
|
|
26
176
|
Every figure is as fresh as the schedule that refreshes it. The CVE feed is polled from its
|
|
27
177
|
sources on a schedule; each radar refreshes on its own.
|
|
@@ -276,6 +426,60 @@ above, each a whole number adding up to the count it divides, is left out whole
|
|
|
276
426
|
field this version does not know is left out, and named only when its name is shaped like a
|
|
277
427
|
field name.
|
|
278
428
|
|
|
429
|
+
### How `check_affected` decides
|
|
430
|
+
|
|
431
|
+
`check_affected` asks the matcher behind
|
|
432
|
+
[echelongraph.io/am-i-affected](https://echelongraph.io/am-i-affected) whether a version is
|
|
433
|
+
affected, by one of two lookup paths:
|
|
434
|
+
|
|
435
|
+
- **CPE path** — `product` and `version`. `product` is the NVD CPE product token (`openssl`,
|
|
436
|
+
`nginx`), which can differ from a package name. It returns the CVEs whose NVD CPE match
|
|
437
|
+
criteria name that product with a version range that includes the version. It matches the
|
|
438
|
+
token across vendors, so each match carries `cpe_vendor` and `vendor_unknown`: a match with
|
|
439
|
+
`vendor_unknown` true is real, but its vendor is not verified to be yours.
|
|
440
|
+
- **Registry path** — `ecosystem` (`npm`, `PyPI`, `Maven`, …), `package` and `version`. Each OSV
|
|
441
|
+
advisory record EchelonGraph holds for the package is decided against the version as
|
|
442
|
+
affected (a match), not affected (`not_affected_count`) or undetermined.
|
|
443
|
+
|
|
444
|
+
Read `assessed` before `count`. `assessed` false means the lookup did not evaluate the
|
|
445
|
+
component, and `not_assessed_reason` says why: `product_not_in_cpe_corpus`,
|
|
446
|
+
`package_not_cpe_nameable`, `candidate_load_pending` (with `degraded` true),
|
|
447
|
+
`candidate_window_truncated`, `package_not_in_advisory_corpus` or `no_decidable_advisory`. The
|
|
448
|
+
result's `state` is then `not_assessed`, and its `count` of 0 never means "not affected". An
|
|
449
|
+
advisory whose version range cannot be decided at this version is counted in
|
|
450
|
+
`undetermined_count` (up to 50 listed in `undetermined`) and is never reported as safe. `capped`
|
|
451
|
+
says the match list stopped at its cap, and `candidates_capped` that not every candidate CVE was
|
|
452
|
+
loaded. A failed advisory lookup (`advisory_lookup_failed`) comes back as a failure. Each match
|
|
453
|
+
keeps `kev_listed`, `ransomware`, `epss_score`, `effective_score`, `effective_severity` and
|
|
454
|
+
`score_assessed` as the API sent them.
|
|
455
|
+
|
|
456
|
+
`product`, `version`, `ecosystem` and `package` travel in the `X-EG-Product`, `X-EG-Version`,
|
|
457
|
+
`X-EG-Ecosystem` and `X-EG-Package` request headers, never in the URL, so request logs and
|
|
458
|
+
trace spans that record URLs do not hold what was looked up. The endpoint reads a CPE vendor
|
|
459
|
+
from the URL only, so this tool takes none.
|
|
460
|
+
|
|
461
|
+
## Prompts
|
|
462
|
+
|
|
463
|
+
Ready-made workflows a client can show as slash commands. Each prompt names the tools to call, in
|
|
464
|
+
order, and tells the model to read every result's `state`, `measured_at`, `method`, `coverage`
|
|
465
|
+
and `notes`: `not_assessed` is never reported as clean, a count of 0 under `assessed: false` is
|
|
466
|
+
never "not affected", and each figure cites its `measured_at`. A prompt makes no API request.
|
|
467
|
+
|
|
468
|
+
| Prompt | Arguments | What it asks the model to do |
|
|
469
|
+
|---|---|---|
|
|
470
|
+
| `triage_cve` | `cve_id` | `get_cve`, `cve_intel`, `vendor_advisories_for_cve`, `epss_history` and `cve_exposure` (and `kev_recent` for the due date when the CVE is KEV-listed), then a decision template: exploited, likelihood, reachable, patch, deadline, decision. |
|
|
471
|
+
| `kev_weekly_brief` | `days` (1 to 365, default 7) | Work out the start date from `days`, page through `kev_recent` from it, and write a brief grouped by vendor with ransomware flags and due dates. |
|
|
472
|
+
| `am_i_affected` | `product`, or `ecosystem` and `package`; `version` | `check_affected`, read `assessed` before `count`, with the not-assessed wording. |
|
|
473
|
+
| `sbom_review` | `sbom` (CycloneDX or SPDX JSON text, up to 5,000,000 characters) | Pass the document to `check_sbom`, look up each affected CVE with `get_cve`, and write a fix list ordered by CISA-KEV listing, then EPSS, then score, each key stated. |
|
|
474
|
+
|
|
475
|
+
## Resources
|
|
476
|
+
|
|
477
|
+
| Resource | What it holds |
|
|
478
|
+
|---|---|
|
|
479
|
+
| `echelongraph://methodology` | What each envelope field and `state` value means, and how each tool measures (Markdown). |
|
|
480
|
+
| `echelongraph://sources` | What the API reports about its feeds when the resource is read: the NVD poller's interval and last poll (of the instance that answered, from `/api/v1/public/cves/summary`) and the CISA KEV catalog's last successful fetch and method (from `/api/v1/public/kev/recent`). It names the feeds no endpoint reports a schedule for, and states none for them (JSON). |
|
|
481
|
+
| `cve://{cve_id}` | One CVE as `get_cve` returns it: the structured result, envelope and record, as JSON. An ID that is not a CVE ID is refused before any request; a CVE the API has no record of is not found. |
|
|
482
|
+
|
|
279
483
|
## What a result means
|
|
280
484
|
|
|
281
485
|
Every tool answers in one of two shapes, so a model reading the result cannot mistake an
|
|
@@ -323,7 +527,7 @@ declares its shape as an `outputSchema` in `tools/list`, with a title and the an
|
|
|
323
527
|
| `state` | `measured`, `not_assessed`, `failed` or `invalid_input` (below). |
|
|
324
528
|
| `measured_at` | When the underlying observation was made, as the API states it. `null` when the answer does not say or holds no observation; never the Go zero time. |
|
|
325
529
|
| `method` | How the numbers were produced. `null` on a failure. |
|
|
326
|
-
| `coverage` | What the answer covers, where the tool can say: `in_scope` for `cve_exposure`, the list's own account of its count for `search_cves`,
|
|
530
|
+
| `coverage` | What the answer covers, where the tool can say: `in_scope` for `cve_exposure`, the list's own account of its count for `search_cves`, the radars that answered for `exposure_radar`, and `assessed`, `not_assessed_reason`, the lookup path and the match counts for `check_affected`. |
|
|
327
531
|
| `freshness` | The producing radar's last completed check (`last_run_at`), where the API serves one; `null` where it serves none. |
|
|
328
532
|
| `notes` | The caveats, one sentence each: what the envelope itself needs saying, then the note from the text block. |
|
|
329
533
|
| `data` | On a success only: the same JSON as the first text block. |
|
|
@@ -372,21 +576,26 @@ a note says so. Per tool:
|
|
|
372
576
|
to `mcp_servers.window.to`, over checks made at different times, which is not one observation
|
|
373
577
|
time. Its numbers keep the labels above. `freshness` holds each radar's `last_run_at` where the
|
|
374
578
|
API serves one, for `shadow_ai` also `running`, and for `mcp_servers` also `enabled`.
|
|
579
|
+
- `check_affected` is `measured` only when the answer says `assessed` true, and `not_assessed`
|
|
580
|
+
when it says false or does not say. Its `measured_at` and `freshness` are `null`: a match answer
|
|
581
|
+
carries no observation time. Its `coverage` gives `assessed` and `not_assessed_reason` first,
|
|
582
|
+
then `lookup` (`cpe` or `registry`), `match_layer`, `count`, `capped`, `candidates_capped`,
|
|
583
|
+
`excluded_count`, `undetermined_count`, `not_affected_count` and `degraded`.
|
|
375
584
|
|
|
376
585
|
The CVE feed tools' `freshness` is `null`: the feed's answers carry no time at which its
|
|
377
586
|
pollers last completed a poll for the feed as a whole.
|
|
378
587
|
|
|
379
588
|
A success whose fields do not fit the tool's `outputSchema` (a field of a type the schema does
|
|
380
589
|
not allow) is returned as a failure with `error.kind` `unexpected_shape`, never relayed. A
|
|
381
|
-
field the API adds later is still relayed by
|
|
382
|
-
`exposure_radar`
|
|
590
|
+
field the API adds later is still relayed by every tool that relays the API's JSON as data, and
|
|
591
|
+
`exposure_radar` and `cve_intel`, which relay a selection, leave it out and name what they leave out.
|
|
383
592
|
|
|
384
593
|
## Protocol versions
|
|
385
594
|
|
|
386
595
|
The server answers both eras of the Model Context Protocol on stdio:
|
|
387
596
|
|
|
388
597
|
- **2026-07-28**: a client that opens with `server/discover` receives a DiscoverResult listing
|
|
389
|
-
`2026-07-28`, the tools
|
|
598
|
+
`2026-07-28`, the tools, prompts and resources capabilities and the server instructions, and then sends each request
|
|
390
599
|
with the per-request `_meta` envelope.
|
|
391
600
|
- **2025 and earlier**: a client that opens with `initialize`, as every 1.x SDK client does,
|
|
392
601
|
negotiates `2025-11-25`, `2025-06-18`, `2025-03-26` or `2024-11-05`; a version the server does
|
|
@@ -402,50 +611,86 @@ Both handshakes also carry the server instructions: the data is public; what `st
|
|
|
402
611
|
numbers are aggregate counts of ip:port services, not an internet-wide census; and that Shodan
|
|
403
612
|
data is Shodan's.
|
|
404
613
|
|
|
405
|
-
##
|
|
406
|
-
|
|
407
|
-
Requires Node.js 20 or later. Add it to your MCP client's config. It runs via `npx` — no global
|
|
408
|
-
install needed.
|
|
614
|
+
## Privacy: what is sent where
|
|
409
615
|
|
|
410
|
-
|
|
616
|
+
**The npm package (stdio).** The server runs on your machine and sends HTTPS requests to one
|
|
617
|
+
host, `ECHELONGRAPH_API_BASE` (default `https://app.echelongraph.io`), one per API call a tool
|
|
618
|
+
makes (`exposure_radar` makes five; every other tool makes one). No telemetry: it sends nothing
|
|
619
|
+
else, to EchelonGraph or anyone. Every request carries the User-Agent
|
|
620
|
+
`echelongraph-mcp/<version> (+https://echelongraph.io/pulse/mcp)`, with the
|
|
621
|
+
`ECHELONGRAPH_MCP_UA` token ahead of it when you set one, so the API's access log can count use
|
|
622
|
+
per version. Like any HTTPS request, it also reaches the API from your network address.
|
|
411
623
|
|
|
412
|
-
|
|
624
|
+
Where each tool's input travels:
|
|
413
625
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
626
|
+
| Input | Where it is sent |
|
|
627
|
+
|---|---|
|
|
628
|
+
| A CVE ID (`get_cve`, `cve_exposure`, `epss_history`, `cve_intel`, `vendor_advisories_for_cve`), a CWE ID and page (`get_cwe`), a vendor and advisory ID (`get_vendor_advisory`) | The URL path. |
|
|
629
|
+
| `search_cves`: the search text, severity, minimum CVSS, sort and limit | The URL query string. |
|
|
630
|
+
| `search_vendor_advisories`: `query` | The `X-EG-Advisory-Search` request header, never the URL. Its vendor, severity, CVE filter, limit and offset go in the query string. |
|
|
631
|
+
| `check_affected`: `product`, `version`, `ecosystem`, `package` | The `X-EG-Product`, `X-EG-Version`, `X-EG-Ecosystem` and `X-EG-Package` request headers, never the URL. |
|
|
632
|
+
| `kev_recent`: `since`, `until`, `ransomware`, `vendor`, `limit`, `cursor` | The `X-EG-Since`, `X-EG-Until`, `X-EG-Ransomware`, `X-EG-Vendor`, `X-EG-Limit` and `X-EG-Cursor` request headers, never the URL. |
|
|
633
|
+
| `check_sbom`: `purls`, or an `sbom` document | The purls, in a POST body. A CycloneDX or SPDX document is read on your machine and only the purls in it are sent; the document is not. |
|
|
634
|
+
| `cve_summary`, `exposure_radar` | No input. |
|
|
635
|
+
|
|
636
|
+
The API marks its answers to header-carried lookups `no-store`, so no shared cache keeps them,
|
|
637
|
+
and request logs and trace spans that record URLs do not hold what was looked up there.
|
|
638
|
+
|
|
639
|
+
**The hosted endpoint.** A request to `https://mcp.echelongraph.io/mcp` reaches EchelonGraph's
|
|
640
|
+
server first, which then calls the same API as above. Per request it logs the HTTP method and
|
|
641
|
+
path, the status, the duration, the body size, the JSON-RPC method and (for `tools/call`) the
|
|
642
|
+
tool name, the protocol version, whether an `Origin` header was sent, and the family of your
|
|
643
|
+
client's User-Agent. It never logs tool arguments or a query string. When a client passes the
|
|
644
|
+
per-client limit it logs that client's address key. It passes your client's public address to
|
|
645
|
+
the API, in a header the API trusts only with a token it checks, so the API's rate limit counts
|
|
646
|
+
you and not the hosted service.
|
|
647
|
+
|
|
648
|
+
EchelonGraph's privacy policy: <https://echelongraph.io/privacy>.
|
|
649
|
+
|
|
650
|
+
## Rate limits
|
|
651
|
+
|
|
652
|
+
No API key, so the API limits each caller, one IPv4 address or one IPv6 /64 network as its
|
|
653
|
+
platform records it. Its WAF counts all your requests together in a 60-second window and
|
|
654
|
+
refuses one once the count passes the ceiling for its path:
|
|
655
|
+
|
|
656
|
+
| Path | Ceiling per minute | Tools |
|
|
657
|
+
|---|---|---|
|
|
658
|
+
| `/api/v1/public/cves` and every path under it | 12,000 | `cve_summary`, `search_cves`, `get_cve`, `epss_history`, `cve_intel`, `check_affected`, `check_sbom` |
|
|
659
|
+
| `/api/v1/public/cwes`, `/api/v1/public/vendor-advisories`, `/api/v1/public/kev/recent`, `/api/v1/public/shadow-ai-radar` | 6,000 | `get_cwe`, the three vendor-advisory tools, `kev_recent`, and one of `exposure_radar`'s five calls |
|
|
660
|
+
| any other `/api/v1/public` path | 600 | `cve_exposure`, and the rest of `exposure_radar`'s calls |
|
|
661
|
+
|
|
662
|
+
Because the count is shared, heavy use of one path also uses up the lower ceilings. On top of
|
|
663
|
+
that, 500 requests per second per caller is a burst cap on each serving instance, and
|
|
664
|
+
`check_sbom`'s batch route is charged per component: 1,200 components a minute per caller.
|
|
665
|
+
Over a limit the API answers 429 with `Retry-After`, and the tool returns a failure
|
|
666
|
+
(`state` `failed`, `error.status` 429), never empty data. The API's own documentation of these
|
|
667
|
+
limits: <https://echelongraph.io/pulse/api>.
|
|
668
|
+
|
|
669
|
+
The hosted endpoint adds its own limit before the API's: 120 MCP requests a minute per client
|
|
670
|
+
address, counted by each server instance, answered over it with HTTP 429, `Retry-After` and a
|
|
671
|
+
JSON-RPC error (code `-32029`) that names the limit and the seconds to wait. A request body over
|
|
672
|
+
64 KiB is refused.
|
|
424
673
|
|
|
425
|
-
|
|
674
|
+
## Configuration
|
|
426
675
|
|
|
427
|
-
|
|
676
|
+
The npm package (stdio):
|
|
428
677
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
"args": ["-y", "echelongraph-mcp"]
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
```
|
|
678
|
+
| Env var | Default | Purpose |
|
|
679
|
+
|---|---|---|
|
|
680
|
+
| `ECHELONGRAPH_API_BASE` | `https://app.echelongraph.io` | Override the API base (self-host / proxy). A credential in the URL is masked wherever a result quotes the base. |
|
|
681
|
+
| `ECHELONGRAPH_API_TIMEOUT_MS` | `15000` | Per-request timeout. A slower answer is reported as a failed lookup, not as empty data. |
|
|
682
|
+
| `ECHELONGRAPH_MCP_UA` | unset | One product token (for example `my-monitor/1.0`) put ahead of this package's own User-Agent, so automated callers such as monitors are told apart from people using the server. A value that is not a single token is ignored. |
|
|
439
683
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
## Configuration
|
|
684
|
+
The HTTP entrypoint, `dist/http.js` (`npm run start:http`), serves the same tools over
|
|
685
|
+
Streamable HTTP on `POST /mcp`, with a liveness check on `GET /health`. It reads the three
|
|
686
|
+
variables above and:
|
|
444
687
|
|
|
445
688
|
| Env var | Default | Purpose |
|
|
446
689
|
|---|---|---|
|
|
447
|
-
| `
|
|
448
|
-
| `
|
|
690
|
+
| `PORT` | `8080` | The port it listens on. |
|
|
691
|
+
| `MCP_RATE_LIMIT_PER_MIN` | `120` | MCP requests a minute per client address, per instance. |
|
|
692
|
+
| `MCP_MAX_BODY_BYTES` | `65536` | The largest request body it reads. |
|
|
693
|
+
| `ECHELONGRAPH_FORWARD_TOKEN`, `ECHELONGRAPH_FORWARD_HOST`, `MCP_REQUIRE_FORWARD_TOKEN` | unset | EchelonGraph's hosted deployment only: the token with which it names each client to the API. Leave them unset when you run it yourself; the API then counts every request as coming from your server. |
|
|
449
694
|
|
|
450
695
|
## Develop
|
|
451
696
|
|
|
@@ -464,6 +709,19 @@ counted as external MCP adoption. `npm test` never leaves the machine.
|
|
|
464
709
|
set `ECHELONGRAPH_MCP_BIN` to that package's `echelongraph-mcp` bin: the tests then run the bin
|
|
465
710
|
directly, as `npx` does, and read that package's own files.
|
|
466
711
|
|
|
712
|
+
## Versioning
|
|
713
|
+
|
|
714
|
+
Semantic versioning: a major version changes protocol behaviour or drops a Node.js version, a
|
|
715
|
+
minor version adds tools or fields, a patch fixes wording or behaviour without changing a tool's
|
|
716
|
+
shape. Every release is published to npm with a provenance attestation by the public repo's
|
|
717
|
+
release workflow, from the commit tagged `v<version>`. Changes per version:
|
|
718
|
+
[CHANGELOG.md](CHANGELOG.md).
|
|
719
|
+
|
|
720
|
+
## Security
|
|
721
|
+
|
|
722
|
+
Report a vulnerability privately to **support@echelongraph.io**, not in a public issue. The
|
|
723
|
+
policy, the supported versions and the scope are in [SECURITY.md](SECURITY.md).
|
|
724
|
+
|
|
467
725
|
## License
|
|
468
726
|
|
|
469
727
|
The code is MIT-licensed.
|