Skip to main content
Glama
orcohen5

Vulnerability Registry MCP Server

by orcohen5
README.md
# Vulnerability Registry MCP Server

Author: Or Cohen

An MCP (Model Context Protocol) server that wraps a legacy vulnerability database and exposes it as tools for any MCP-compatible LLM client. Built as a smart access layer over custom pipe-delimited data files, enabling security analysts to query vulnerabilities using natural language.

## Quick Start

### Prerequisites

- Node.js 18+
- Claude Desktop (or any MCP-compatible client)

### Setup

```bash
git clone https://github.com/orcohen5/vulnerability-registry.git
cd vulnerability-registry
npm install
npm run build
```

### Connect to Claude Desktop

Add to your Claude Desktop config (`%APPDATA%\Claude\claude_desktop_config.json` on Windows, `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):

```json
{
  "mcpServers": {
    "vulnerability-registry": {
      "command": "node",
      "args": [
        "<FULL_PATH>/vulnerability-registry/dist/index.js",
        "<FULL_PATH>/vulnerability-registry/data"
      ]
    }
  }
}
```

Replace `<FULL_PATH>` with the absolute path to the cloned repository.

Restart Claude Desktop, then ask:

> "What MCP tools do you have for vulnerabilities?"

![Tool Discovery](docs/screenshots/1.png)
*Claude Desktop discovering all 6 vulnerability registry tools*

## Available Tools

| Tool | Description | Key Parameters | Example Query |
|------|-------------|---------------|---------------|
| `list_vendors` | List all registered software vendors | `category` (optional) | "Show me all open source vendors" |
| `get_vendor` | Find a vendor by ID or name | `vendor_id`, `name` | "Find the vendor ID for Linux Kernel" |
| `search_vulnerabilities` | Search with flexible filters | `severity`, `status`, `min_cvss`, `keyword`, `published_after` | "Show critical open vulnerabilities" |
| `get_vulnerability` | Get full CVE details | `cve_id` | "What is the CVSS score of Log4Shell?" |
| `get_vulnerability_stats` | Aggregate statistics | `vendor_id` (optional) | "How many vulnerabilities by severity?" |
| `get_vendor_risk_summary` | Vendor risk profile | `vendor_id` | "Show me Microsoft's risk profile" |

## Example Queries

### "How many critical vulnerabilities are still open?"

Uses `search_vulnerabilities` with `severity: "critical"` and `status: "open"`.

![Critical Open Vulnerabilities](docs/screenshots/3.png)

### "What is the CVSS score of Log4Shell?"

Uses `get_vulnerability` with `cve_id: "CVE-2021-44228"`.

![Log4Shell CVSS](docs/screenshots/5.png)

### "Show me the risk profile for Microsoft"

Uses `get_vendor_risk_summary` with `vendor_id: "V1"`.

![Microsoft Risk Profile](docs/screenshots/7.png)

### "Which vulnerabilities were found in Linux Kernel after 2022?"

This query demonstrates **multi-tool orchestration** — Claude first calls `list_vendors` to resolve "Linux Kernel" to vendor ID `V5`, then calls `search_vulnerabilities` with `vendor_id: "V5"` and `published_after: "2022-01-01"`.

![Linux Kernel Multi-Tool Query](docs/screenshots/9.png)

## Architecture

```
┌─────────────────┐     ┌──────────────┐     ┌──────────────┐
│  Claude Desktop │────▶│  MCP Server  │────▶│  Data Files  │
│  (MCP Client)   │◀────│  (stdio)     │◀────│  (.db)       │
└─────────────────┘     └──────┬───────┘     └──────────────┘
                               │
                    ┌──────────┼──────────┐
                    ▼          ▼          ▼
               tools.ts   repository.ts  parser.ts
              (MCP layer)  (query engine) (file reader)
```

The codebase follows a strict three-layer separation:

- **parser.ts** — Reads the custom pipe-delimited format dynamically. Knows nothing about MCP.
- **repository.ts** — In-memory data store with indexed Maps for O(1) lookups. Knows nothing about MCP.
- **tools.ts** — Registers MCP tools using the high-level `McpServer` API. Translates between MCP and the repository.

This means swapping the data source (files → database) requires changing only `parser.ts`, with zero changes to the MCP layer.

## Design Decisions

**Dynamic metadata parsing** — The file parser reads column names from the `# FORMAT:` header at runtime rather than hardcoding field positions. Combined with version checking (`# VERSION: 1.0`), this ensures the server can detect and warn about format changes without code modifications.

**Repository pattern with in-memory indexing** — Data is loaded once at startup and indexed into multiple Maps (`vendorById`, `vulnByCveId`, `vulnsByVendor`, `vulnsBySeverity`, `vulnsByStatus`). Primary lookups are O(1). Filtered searches start from the smallest indexed subset and intersect, making combined queries efficient even at scale.

**High-level McpServer API** — Uses `McpServer.registerTool()` with Zod schemas for type-safe input validation, rather than the low-level `Server` class with manual JSON Schema definitions and request routing.

**Flexible search with optional filters** — `search_vulnerabilities` accepts all parameters as optional, allowing any combination. One tool handles queries from "show all critical" to "find Linux CVEs from 2023 with CVSS above 8". Results are always sorted by CVSS score (highest first) so the most severe issues appear first.

**Enriched responses** — `get_vulnerability` returns the full vendor object alongside the CVE data. `get_vendor_risk_summary` includes the list of open vulnerabilities. This reduces the number of tool calls the LLM needs to answer common questions.

**Strict type safety** — `Severity` and `Status` are union types derived from `as const` arrays, with runtime type guards (`isSeverity`, `isStatus`). The same source-of-truth arrays feed both TypeScript types and Zod enum validators.

## Known Data Anomalies

While working with the source data files, I identified at least one attribution inconsistency:
`CVE-2024-21762` (Fortinet SSL VPN OOB) is mapped to vendor `V4` (Google) in `vulnerabilities.db`,
although this is a Fortinet vulnerability. The server faithfully returns the data as stored —
correcting source data is out of scope for a read-only query layer. In a production system,
I would add a data validation step at load time to flag such inconsistencies for human review,
possibly by cross-referencing the NVD API for canonical vendor attribution.

## What I'd Build With More Time

- **SQLite/PostgreSQL persistence** — Replace in-memory storage for datasets that exceed available RAM, with connection pooling for concurrent access.
- **Pagination** — Add `limit`/`offset` parameters to `search_vulnerabilities` for large result sets.
- **Fuzzy text search** — Levenshtein distance matching on vulnerability titles for typo-tolerant queries.
- **NVD API integration** — Automatic CVE data updates from NIST's National Vulnerability Database.
- **MCP Resources** — Expose raw data files as MCP Resources for direct LLM access when full-text context is needed.
- **Structured logging & observability** — JSON-formatted logs with correlation IDs for debugging tool call chains.
- **Authentication & rate limiting** — Protect the server in shared deployment scenarios.
- **CI/CD pipeline** — GitHub Actions running lint, type-check, and tests on every push.

## Tech Stack

| Component | Choice |
|-----------|--------|
| Language | TypeScript (ES2022, Node16 modules) |
| MCP SDK | `@modelcontextprotocol/sdk` — `McpServer` high-level API |
| Validation | Zod |
| Transport | stdio |
| Build | tsc |
| Tests | Vitest |

## Testing

```bash
npm test        # Run all tests (30 tests across parser + repository)
npm run build   # Compile TypeScript
npm start       # Start the MCP server (stdio mode)
```

TDQS

A4/5.0

Scored across 6 tools

Disambiguation5/5

Each tool has a clearly distinct purpose with no overlap: get_vendor retrieves vendor details, get_vendor_risk_summary provides risk profiles, get_vulnerability fetches specific vulnerability data, get_vulnerability_stats offers statistical summaries, list_vendors enumerates vendors, and search_vulnerabilities enables filtered searches. The descriptions explicitly differentiate their functions, preventing agent misselection.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern using snake_case, with verbs like 'get', 'list', and 'search' clearly indicating actions. This uniformity makes the tool set predictable and easy to navigate, enhancing agent usability without any naming deviations.

Tool Count5/5

With 6 tools, the server is well-scoped for a vulnerability registry, covering core operations such as retrieving vendors and vulnerabilities, assessing risks, and generating statistics. Each tool serves a unique and necessary function, avoiding bloat or gaps for this domain.

Completeness4/5

The tool set provides comprehensive coverage for querying and analyzing vulnerability data, including CRUD-like operations for vendors and vulnerabilities, risk assessment, and statistical insights. A minor gap exists in the lack of tools for creating, updating, or deleting entries, but this is reasonable for a read-only registry focused on data retrieval and analysis.

Maintenance

ActivityInactive
ResponsivenessNo issues