MCP server
VulnSea speaks the Model Context Protocol so an agent can triage vulnerabilities without a custom integration. One remote URL, 11 tools, no install: https://beta.vulnsea.com/api/mcp. Stateless Streamable HTTP (one JSON-RPC POST, one JSON reply), protocol 2025-06-18, server version 1.0.0.
The whole corpus is open. Anonymous callers get 60 requests a minute per IP; an API key sent as Authorization: Bearer vsk_… raises that to 600.
Connect a client
Pick your runtime. Every snippet is the same URL; add the key header if you have one.
Claude Code
claude mcp add --transport http vulnsea https://beta.vulnsea.com/api/mcp
# with an API key:
claude mcp add --transport http vulnsea https://beta.vulnsea.com/api/mcp --header "Authorization: Bearer vsk_…"Claude Desktop / claude.ai
Settings → Connectors → Add custom connector. Name VulnSea, URL https://beta.vulnsea.com/api/mcp. No OAuth: leave the client id blank.
Cursor
// .cursor/mcp.json
{
"mcpServers": {
"vulnsea": { "url": "https://beta.vulnsea.com/api/mcp" }
}
}VS Code (Copilot agent mode)
// .vscode/mcp.json
{
"servers": {
"vulnsea": { "type": "http", "url": "https://beta.vulnsea.com/api/mcp" }
}
}Anything else (raw JSON-RPC)
curl -X POST https://beta.vulnsea.com/api/mcp -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
curl -X POST https://beta.vulnsea.com/api/mcp -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"list_cves","arguments":{"depth":"hadal","limit":5}}}'Tool results carry both a text block and structuredContent with the same JSON. Argument errors come back in-band with isError: true; only malformed JSON-RPC gets a protocol error.
Tools
Scan with list_cves or search_cve, deep-fetch with get_cve, then poll changes_since for re-triage triggers. Rows are ranked by depth score; the depth filter narrows to a zone (hadal = critical and actively exploited).
Any real CVE id works with get_cve, resolve_id and batch_get_cve: a record that is not in the corpus yet is fetched live from CVE.org and NVD on first request (a few seconds), written, and is an ordinary hit afterwards. A GHSA id is mapped to its CVE through the GitHub advisory API first.
| Tool | Arguments | What it does |
|---|---|---|
| list_cves | q?, severity?, vendor?, exploited?, depth?, since?, order?, limit?, cursor? | List or incrementally poll CVEs, newest first. Filter by severity, vendor, exploited, or depth zone (sunlit|twilight|midnight|abyssal|hadal; deeper = graver + more proven, hadal = critical & actively exploited). Pass `since` (ISO or epoch ms) and follow `next` to poll for new additions. Returns compact rows; deep-fetch one with get_cve. |
| search_cve | q, severity?, exploited?, kev?, depth?, limit? | Typo-tolerant full-text search across the corpus (FTS5, bm25-ranked). Optional severity / exploited / kev / depth filters. Returns compact rows. |
| get_cve | id | Fetch one CVE in full: structured metadata plus the complete markdown writeup (body). Use after list_cves / search_cve narrows to a record of interest. Any real CVE id works: a record not yet in the corpus is fetched live from CVE.org / NVD on first request (a few seconds) and is an ordinary hit afterwards. |
| batch_get_cve | ids | Fetch a known set of CVEs in one call (e.g. an SBOM / dependency match). Up to 200 IDs; returns compact rows in request order. Up to 5 ids unknown to the corpus are fetched live first; the rest are listed in `missing`. |
| triage_sbom | components | Match a dependency list against the corpus. Pass `components` as purls (pkg:npm/[email protected]) or {ecosystem,name,version} objects. Returns, per component, affected CVEs ranked by depth with a versionMatch (in-range | unknown). Package data is GHSA-sourced. Up to 200 components. |
| related_cve | id, limit? | CVEs related to a given id, connected by shared product / vendor / CWE / references / tags, with the reasons and a relation score (highest first). Walk an attack chain or find sibling flaws in the same component. |
| changes_since | after?, since?, field?, limit? | Mutation feed: per-CVE field changes (kev added, severity bumped, exploited flipped, material epss move) as an append-only event stream with an exact numeric cursor. list_cves answers what's NEW; this answers what CHANGED about records already triaged — the re-triage trigger. Store the returned nextSeq and pass it as `after` next call. |
| resolve_id | id | Resolve any advisory identifier (CVE-…, GHSA-…, or another alias) to its canonical VulnSea record — id, aliases, url, and compact metadata. Use when an upstream tool hands you a non-CVE id. Unknown ids are fetched live (a GHSA id via the GitHub advisory API, then CVE.org / NVD). |
| get_epss_movers | days?, limit?, minDelta? | CVEs whose EPSS (exploit-prediction probability) climbed the most over a recent window — rising exploitation risk before it lands in KEV. Needs ≥2 snapshot days of history inside the window. |
| get_stats | — | Corpus summary: total CVEs, critical / exploited / KEV counts, last-7-day count, and the newest ingest timestamp. Cheap freshness check before polling. |
| get_digest | kind?, key? | One digest period in compact rows: what was published, KEV additions, EPSS risers, most-changed records and top vendors. `kind` week (default) or day; `key` (2026-W36 / 2026-09-08) picks a past period, omit for the current one. The same content as the dated pages under /digest. |
Required arguments in white; ? marks optional ones. The full JSON schema for each tool comes back from tools/list. Every tool is annotated readOnlyHint and idempotentHint; the three id lookups above carry openWorldHint because they may reach upstream.
Resources & prompts
Clients that speak MCP resources can attach a record or the current digest as context without a tool call (resources/read). Prompts are ready-made workflows the model fills in (prompts/get); each one names the tools to call and the shape of the answer.
| Resource | Type | Content |
|---|---|---|
| vulnsea://stats | application/json | Totals, critical / exploited / KEV counts, newest ingest time. |
| vulnsea://digest/week | application/json | Published, KEV additions, EPSS risers, most-changed — current ISO week. |
| vulnsea://digest/day | application/json | The same digest for the current UTC day. |
| vulnsea://cve/{id} | text/markdown | Canonical frontmatter + writeup for one CVE. Any real id; fetched live if unknown. |
| vulnsea://cve/{id}/json | application/json | The get_cve payload (record + depth score parts) for one CVE. |
| Prompt | Arguments | What it does |
|---|---|---|
| triage_sbom | components | Match a dependency list against the corpus and produce a ranked patch list. |
| explain_cve | id | Plain-language brief for one CVE: what, who is affected, exploitation status, fix, urgency. |
| weekly_patch_plan | focus? | Turn this week's digest and the EPSS movers into one ordered patch plan. |
A triage loop
get_statsfor the corpus size and newest ingest time. Nothing changed since last run, stop.list_cveswithsinceset to your last high-water mark, followingnext. New records.changes_sincewith the storedaftercursor. Records you already triaged that gained a KEV entry, a severity bump, or a material EPSS move.triage_sbomwith your dependency purls when a lockfile changes.related_cveto walk sibling flaws.get_cveonly for the handful worth reading in full. Records may carry owner-curatednotesabove the generated writeup.
Registry
VulnSea is listed in the MCP Registry as io.github.vulnsea/vulnsea. The manifest at /api/mcp/server.json is what registries and client directories read: name, description, the remote URL and the optional auth header.