README
¶
NVD CVE MCP Server
MCP (Model Context Protocol) server in Go that wraps the NIST NVD CVE API v2.0 and exposes it to MCP-compatible hosts (OpenCode, Cursor, Claude Desktop, Claude Code, Gemini CLI, Cline, Continue, Zed, etc.).
Built with the official MCP Go SDK (github.com/modelcontextprotocol/go-sdk, v1.6.1+).
Features
- Six strongly-typed CVE-focused MCP tools:
search_cvesget_cve_by_idget_cves_by_cpesearch_cves_by_keywordget_cves_by_date_rangeget_cves_by_severity
- Strict input validation:
- required-field, paired-date, and mutually-exclusive-group checks
- CVE ID format validation (
CVE-YYYY-NNNN+) cve_idslist capped at 100- pagination bounds (
results_per_page1..2000,start_index >= 0)
- Permissive ISO 8601 date parsing, including:
2026-01-01T00:00:00Z2026-01-01T00:00:00+07:002026-01-01T00:00:00.000Z2026-01-01T00:00:00.000+07:002026-01-01T00:00:00.0002026-01-01T00:00:00
- NVD API key support via
NVD_API_KEY, sent in theapiKeyrequest header (so it never appears in URLs, proxy logs, or access logs). - Rate limiting that matches NVD's published policy (token bucket via
golang.org/x/time/rate):5 requests / 30 swindow without API key50 requests / 30 swindow with API key
- HTTP client timeout (
30s), explicitUser-Agent: NVD-MCP-Server/1.0, and a 50 MiB response-body cap. - Timezone
+in ISO 8601 dates is URL-encoded as%2B. - MCP tool annotations: every tool advertises
readOnlyHint=trueandopenWorldHint=trueso hosts can gate tool execution accordingly. - Graceful shutdown on
SIGINT/SIGTERM.
Project Structure
.
├── main.go # entrypoint, stdio MCP transport, signal handling
├── nvd/
│ ├── client.go # NVD CVE API v2.0 HTTP client
│ └── client_test.go # unit tests for the client
├── tools/
│ ├── tools.go # tool definitions, validation, helpers
│ └── integration_test.go # end-to-end MCP tool tests via in-memory transport
├── Makefile # common dev/CI commands
├── go.mod / go.sum
└── README.md
Requirements
- Go toolchain
1.26.x(tested on1.26.3).
Installation
From source
git clone https://github.com/ayitas/mcp-nvd-go
cd mcp-nvd-go
go build -o nvd-mcp-server .
Via go install
go install github.com/ayitas/mcp-nvd-go@latest
The binary will be installed to $(go env GOPATH)/bin/mcp-nvd-go. Add that
directory to your PATH (or reference the binary by absolute path in your
MCP client config).
Run
go mod tidy
go run .
The server speaks MCP over stdio.
Environment
NVD_API_KEY(optional): If set, sent as theapiKeyrequest header (NVD's recommended channel) and unlocks the higher50 req / 30 srate-limit class. Request a free key from NIST.
Use with OpenCode
This server speaks MCP over stdio, so it works unchanged with any MCP host (Cursor, Claude Desktop, Claude Code, Gemini CLI, Cline, Continue, Zed, etc.). The instructions below target OpenCode because that is the host this project is tested against most often. Pick one of the three flows below.
[!IMPORTANT] OpenCode's local MCP config has no
cwdfield. Thecommandyou register must therefore use an absolute path (a binary on disk) or an absolute module path (go run github.com/...@version). Relative paths andgo run .will fail when OpenCode spawns the server from a directory that is not this repository.
Flow A — opencode mcp add (recommended)
The least error-prone path. OpenCode walks you through scope, name, and command, then writes the correct JSON to the right config file:
go install github.com/ayitas/mcp-nvd-go@latest
opencode mcp add
Answer the prompts:
| Prompt | Answer |
|---|---|
| Location | Current project for a per-project server, or Global for everywhere |
| MCP server name | nvd-vulnerabilities |
| Type | Local (stdio) |
| Command | the absolute path printed by go env GOPATH, with /bin/mcp-nvd-go appended |
Then export your key in the shell OpenCode is launched from (so the spawned server inherits it):
export NVD_API_KEY=...
Flow B — go install + hand-edited config
Use this when you want the JSON in version control alongside the rest of your OpenCode setup.
go install github.com/ayitas/mcp-nvd-go@latest
echo "$(go env GOPATH)/bin/mcp-nvd-go" # copy this absolute path
OpenCode loads config from these locations (highest precedence last):
~/.config/opencode/opencode.json— globalopencode.json— project root.opencode/opencode.json— project root (preferred for per-project)
Add the server under the top-level mcp block, pasting the path you copied
above:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"nvd-vulnerabilities": {
"type": "local",
"command": ["/Users/you/go/bin/mcp-nvd-go"],
"enabled": true,
"environment": {
"NVD_API_KEY": "your-nvd-api-key-here"
}
}
}
}
| Field | Required | Notes |
|---|---|---|
type |
yes | Must be "local" for stdio MCP servers. |
command |
yes | Array. First element is the absolute executable path; remaining elements are CLI args (none needed here). |
enabled |
no | Defaults to true. Set to false to keep the entry but disable the server. |
environment |
no | Env vars passed to the spawned process. Omit if you have no API key; the server falls back to the public 5 req / 30 s rate-limit class. |
timeout |
no | Connection/request timeout in ms. The default is fine. |
Flow C — clone + build (contributors)
Use this when you want to hack on the server itself:
git clone https://github.com/ayitas/mcp-nvd-go
cd mcp-nvd-go
go build -o nvd-mcp-server .
realpath ./nvd-mcp-server # prints the absolute path to paste below
Then paste that path into the same JSON shape shown in Flow B.
Verify the connection
After registering by any of the flows above, restart OpenCode and confirm:
opencode mcp list
You should see nvd-vulnerabilities listed as connected.
Inside an OpenCode session, type:
/mcp
The interactive panel should show nvd-vulnerabilities connected with 6
tools:
search_cvesget_cve_by_idget_cves_by_cpesearch_cves_by_keywordget_cves_by_date_rangeget_cves_by_severity
Then drive it directly from a prompt:
Use
nvd-vulnerabilitiesto find CRITICAL CVSS v3 CVEs published between 2026-01-01 and 2026-01-31, then summarize the top five by impact.
OpenCode routes the call through this server, the server hits NVD with the
right query parameters, and the structured JSON response comes back as
structuredContent for the model to reason over.
Make Targets
make run # run MCP server over stdio
make test # run all tests with race detector
make vet # run go vet
make cover # run tests with coverage profile + summary
make verify # fmt + vet + test in one command
make ci # CI-style alias for verify
make build # compile all packages
make tidy # go mod tidy
GO is overridable, e.g. make test GO=/usr/local/go/bin/go.
Testing
This project ships both unit tests and an integration harness that drives the real MCP server through an in-memory client/server session and a mock NVD HTTP backend.
make test # go test -race ./...
go test ./... -coverprofile=coverage.out
go tool cover -func=coverage.out
Current coverage (statements):
| Package | Coverage |
|---|---|
github.com/ayitas/mcp-nvd-go/nvd |
97.4% |
github.com/ayitas/mcp-nvd-go/tools |
98.6% |
| total | 94.7% |
main.go is a thin stdio wrapper around tools.Register + server.Run,
exercised indirectly through the in-memory integration harness, so the
line-based coverage tool reports it as 0%.
The integration tests in tools/integration_test.go exercise:
- Success paths for every tool.
- Optional → query parameter mapping for
search_cves(all 25 fields). - Timezone
+→%2Bencoding. - Validation error paths (mutually exclusive severities, paired dates, pagination bounds, invalid CVE IDs, invalid severity, invalid date type, etc.).
- Backend error propagation (HTTP 500 from mock NVD) for all six tools.
Tool Reference
1) search_cves
General-purpose CVE search. At least one non-pagination filter must be provided.
Filters:
cpe_namecve_ids(max 100)keyword_search(+ optionalkeyword_exact_match)cve_tagcvss_v2_severity/cvss_v3_severity/cvss_v4_severity(mutually exclusive)cwe_idvuln_statuseshas_kev,has_cert_alerts,has_cert_notes,has_ovalis_vulnerable(requirescpe_name)source_identifierno_rejected
Date ranges (both endpoints required if either is provided):
pub_start_date+pub_end_datelast_mod_start_date+last_mod_end_datekev_start_date+kev_end_date
Pagination:
results_per_page(default2000, max2000)start_index(default0)
2) get_cve_by_id
- Required:
cve_id(must matchCVE-YYYY-NNNN+)
3) get_cves_by_cpe
- Required:
cpe_name - Optional:
is_vulnerable,results_per_page,start_index
4) search_cves_by_keyword
- Required:
keyword - Optional:
exact_match,results_per_page,start_index
5) get_cves_by_date_range
- Required:
date_type=publishedorlastModifiedstart_date(ISO 8601)end_date(ISO 8601,>= start_date)
- Optional: pagination
6) get_cves_by_severity
- Required:
cvss_version=v2|v3|v4severity(LOW,MEDIUM,HIGHfor v2; plusCRITICALfor v3/v4)
- Optional: pagination
Response Shape
List-style tools return:
{
"totalResults": 0,
"resultsPerPage": 0,
"startIndex": 0,
"timestamp": "2026-05-28T21:00:00.000Z",
"vulnerabilities": [ { "cve": { /* NVD CVE object */ } } ]
}
get_cve_by_id returns:
{
"totalResults": 1,
"timestamp": "2026-05-28T21:00:00.000Z",
"cve": { "cve": { /* NVD CVE object */ } }
}
Validation/backend errors are returned as MCP tool errors with IsError=true
and a text Content describing the failure (matches the MCP spec recommendation
so the LLM can self-correct).
Known limitations
- No retry/backoff on transient
5xx/429responses from NVD. The bundled rate limiter keeps the client within NVD's published window, but shared egress IPs can still trip the firewall; callers should treat tool errors as retryable. - Malformed
vulnerabilities[]entries returned by NVD are silently dropped from the tool output. The remaining valid entries are still surfaced. get_cves_by_date_rangedoes not split queries: NVD caps a single request at a 120-day window, so callers must chunk longer ranges.
Contributing
Pull requests are welcome. Please read CONTRIBUTING.md
for the workflow, scope rules, and make verify gate.
Security
If you believe you have found a security issue, do not open a public
issue. Follow the disclosure process in SECURITY.md.
Acknowledgements
- NIST NVD for publishing the CVE 2.0 API that this server wraps.
- Model Context Protocol for the open protocol that makes this server reusable across every MCP-capable host.
github.com/modelcontextprotocol/go-sdkauthors for the official Go SDK.golang.org/x/time/ratefor the rate limiter.
License
MIT — see LICENSE.
Documentation
¶
There is no documentation for this package.