cd /news/ai-tools/prescryb-an-mcp-server-for-cve-and-c… · home topics ai-tools article
[ARTICLE · art-56629] src=github.com ↗ pub= topic=ai-tools verified=true sentiment=· neutral

Prescryb – an MCP server for CVE and config remediation

Prescryb, a new open-source MCP server, enables natural-language-driven vulnerability and compliance remediation by connecting AI assistants to SSH-based host inventory, CVE matching, advisory lookups, and Ansible playbook generation. The tool remains read-only against target hosts, letting the connected model reason about findings and generate suggested fixes without applying changes automatically.

read7 min views1 publishedJul 12, 2026
Prescryb – an MCP server for CVE and config remediation
Image: source

See OVERVIEW.md for a high-level description of the repository's purpose, components, and scope before making behavioral changes.

A remediation orchestrator, exposed as an MCP server. Connect an MCP client (Claude Desktop, Claude Code, or similar) and submit a natural-language request, for example:

Log into host a.b.c, check installed packages, find CVEs, and suggest a fix - Ansible if possible, and tell me what compliance controls it maps to.

prescryb

supplies the primitives (SSH inventory, CVE matching, live advisory lookups, compliance-topic mapping, Ansible playbook rendering). The connected model does the reasoning: which findings matter, which CVEs to dig into, which playbook to generate. prescryb

never applies anything to the target host - every tool is read-only against it, or pure text/data generation.

Tool What it does
inventory_host(host, user="", port=22, hostname="", identity_file="", trust_unknown_host=False)
SSH in, detect the distro, list installed packages with versions.
check_cves(system, packages)
Batch-match package versions against

fetch_advisory(cve_id)

fetch_epss(cve_ids)

EPSSexploitation-probability scores for CVE IDs not already covered bycheck_cves

(e.g. from fetch_advisory

or a web search).map_compliance(area)

"ssh"

, "sudo"

, "kernel modules"

, ...) to CIS/DISA STIG topic areas and, if present in the konstruktoid.hardening

GitHub repo, the matching role - plus the MITRE ATT&CK techniques and mitigations that area addresses.lookup_cce(target, keyword, cve_id)

NIST CCE(Common Configuration Enumeration) entries for a platform (e.g."rhel8"

), sourced from the community JSON conversion at .konstruktoid/cce-web

list_cce_targets()

lookup_cce

can query.generate_playbook(system, cve_matches, compliance_areas, hosts_alias)

suggest-only Ansible playbook: CVE fixes become package-upgrade tasks, compliance areas becomeroles:

references.Typical flow: inventory_host

, then check_cves

on the returned packages, then optionally fetch_advisory

on interesting CVEs, then map_compliance

(and lookup_cce

) for any insecure-config areas noticed, then generate_playbook

to produce something to review.

uv sync

Claude Code:

claude mcp add prescryb -- uv --directory /path/to/prescryb run prescryb

Claude Desktop (claude_desktop_config.json

):

{
  "mcpServers": {
    "prescryb": {
      "command": "uv",
      "args": ["--directory", "/path/to/prescryb", "run", "prescryb"]
    }
  }
}

inventory_host

never accepts a password argument. MCP tool-call arguments can be logged by clients and are visible to the connected model, so credentials must never flow through them. Auth works exactly like running ssh host

yourself:

  • Host/user/port/identity files are resolved from ~/.ssh/config

. - Keys come from an SSH agent or the default identity files. inventory_host

'shostname

/identity_file

arguments override the resolved address/key path directly, for hosts you do not want to add to~/.ssh/config

(only a path is passed, never key contents).- Unknown host keys are rejected unless you passtrust_unknown_host=True

  • prefer running ssh host

manually once to pin the key instead.

  • prefer running
claude 'run vagrant up, connect to the created VM, check any vulnerabilities
and suggest a fix, include compliance mapping if possible,
write the playbook suggestion to /tmp/ and print the file location'

For a host already reachable via ssh

(resolved through ~/.ssh/config

, an agent key, or the default identity file), specify the hostname directly; no port or identity-file configuration is required:

Inventory prod-web-01, check installed packages, find CVEs, and suggest a fix - Ansible if possible, and tell me what compliance controls it maps to.

If the host is not in ~/.ssh/config

yet, either add a Host

block or pass user

/port

/hostname

/identity_file

straight to inventory_host

for a one-off connection - same as the molecule example below.

molecule converge -s default

Find the ssh_port

/ssh_user

from that scenario's molecule.yml

platform entry (e.g. ssh_port: 22201

, ssh_user: almalinux

for the almalinux10

platform) and the private key molecule login

uses to connect - either add a Host

block to ~/.ssh/config

, or skip the file entirely and pass them straight to inventory_host

for a one-off, ephemeral connection:

Inventory 127.0.0.1, port 22201, user almalinux, identity_file /path/to/molecule's/generated/key, check installed packages, find CVEs, and suggest a fix - Ansible if possible, and tell me what compliance controls it maps to.

Run molecule destroy -s default

when finished; prescryb

will not do it for you, and will not touch the instance beyond reading it.

map_compliance

names topic areas (e.g. "SSH Server Configuration") and, if found in the konstruktoid/ansible-collection-hardening GitHub repository, a link to the Ansible role that implements it.

By default it queries the GitHub API against konstruktoid/ansible-collection-hardening. Override with:

export HARDENING_COLLECTION_REPO=owner/repo

Set GITHUB_TOKEN

to raise the (otherwise low) unauthenticated GitHub API rate limit. If a role is not found in the repo, map_compliance

still returns the topic/framework/role name so you know what to install (ansible-galaxy collection install konstruktoid.hardening

).

lookup_cce

looks up NIST Common Configuration Enumeration entries - unique identifiers for individual configuration checks, distinct from the topic-area CIS/DISA STIG mapping above. NIST only publishes CCE as spreadsheets, so this reads the pre-converted JSON exports hosted by the community project konstruktoid/cce-web instead of parsing Excel.

Coverage is per-platform, not per-topic, and thin for this project's target distros: only RHEL-family (rhel6

/rhel7

/rhel8

  • AlmaLinux/Rocky use the matching upstream RHEL number) and SUSE (SLES12-DISA-STIG

/SLES15-DISA-STIG

/SLES15-PCI-DSS

) have usable data. Debian, Ubuntu, Alpine, and Arch have no CCE data upstream at all. A few older cce-web

exports (e.g. rhel4

, rhel5

, apache-httpd2.2

) lost their column headers in the upstream Excel-to-JSON conversion; those are reported as unsupported rather than returning garbled fields. Call list_cce_targets

to see every published platform, including non-Linux ones (firefox

, win2k8r2

, ...).

Override the source repo with:

export CCE_REPO=owner/repo

Alongside CIS/DISA STIG, map_compliance

and generate_playbook

also cite the MITRE ATT&CK technique(s) mitigated by a topic area's hardening (e.g. "ssh" maps to T1110

Brute Force and T1021.004

Remote Services: SSH) and, where ATT&CK defines one, the corresponding mitigation (e.g. M1032

Multi-factor Authentication) with a link to attack.mitre.org. Unlike CIS/DISA STIG rule numbers, ATT&CK technique and mitigation IDs are MITRE's own public catalog, so they are cited directly rather than needing a licensed benchmark lookup. This mapping is static (built into attack.py

), not fetched live.

OSV.dev is the sole CVE-matching source. It resolves{name, ecosystem, version}

server-side against the ecosystem's actual version ordering, so a match reflects the exact installed version rather than "any CVE that mentions this package name." Coverage is mature forDebian, Ubuntu, Alpine; thinner for RHEL-family (AlmaLinux, Rocky) and SUSE.check_cves

returns awarning

field flagging thinner-coverage ecosystems, and returns nothing (rather than a guess) for distros with no ecosystem mapping at all - an empty result there means "not checked," not "clean."NVD(fetch_advisory

) is used only to enrich a CVE you already have the ID for. SetNVD_API_KEY

to raise the (otherwise low) unauthenticated rate limit.EPSS(epss_score

/epss_percentile

on everycheck_cves

match, orfetch_epss

for CVE IDs from elsewhere) estimates the probability of exploitation in the next 30 days - independent of, and a useful complement to, CVSS/severity: a LOW-severity CVE can carry a high EPSS score, and vice versa. This lets findings be sorted/filtered bycve_id

,severity

, orepss_score

. No API key needed. CVEs with no EPSS record (very new, reserved, or rejected IDs) simply haveepss_score

unset - not an error. A FIRST.org outage surfaces as awarning

oncheck_cves

rather than failing the CVE match itself.- Severity: OSV gives a raw CVSS vector string ( cvss_vector

), not a precomputed label, for most OS-package entries.severity

is only populated when the source explicitly labels it; otherwise it is"UNKNOWN"

and the vector is left for you (or the model) to interpret, rather than guessing.

Output is always a full playbook as text, prefixed with a comment header citing every CVE/compliance source used. It is never executed by prescryb

. Review it - ansible-playbook --syntax-check

, then --check --diff

  • before running it anywhere.

Package-upgrade tasks use the module for the target's package manager (ansible.builtin.apt

/dnf

/zypper

/community.general.apk

). Version pins are only applied where the module supports them; Arch/pacman targets get state: latest

since pacman does not support the same pinning syntax.

Variable Default Purpose
HARDENING_COLLECTION_REPO
konstruktoid/ansible-collection-hardening
GitHub owner/repo queried for compliance-mapped Ansible roles.
CCE_REPO
konstruktoid/cce-web
GitHub owner/repo queried for CCE JSON exports by lookup_cce /list_cce_targets .
GITHUB_TOKEN
unset Raises GitHub API rate limits for map_compliance , lookup_cce , and list_cce_targets .
NVD_API_KEY
unset Raises NVD API rate limits for fetch_advisory .
  • Does not apply playbooks or otherwise mutate the target host.
  • Does not accept passwords as tool arguments.
  • Does not fabricate CIS/DISA STIG rule numbers.
  • Does not guess CVEs for ecosystems OSV does not cover; it reports that instead.
── more in #ai-tools 4 stories · sorted by recency
── more on @prescryb 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/prescryb-an-mcp-serv…] indexed:0 read:7min 2026-07-12 ·