{"slug": "prescryb-an-mcp-server-for-cve-and-config-remediation", "title": "Prescryb – an MCP server for CVE and config remediation", "summary": "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.", "body_md": "See [ OVERVIEW.md](/konstruktoid/prescryb/blob/main/OVERVIEW.md) for a high-level description of the\nrepository's purpose, components, and scope before making behavioral\nchanges.\n\nA 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:\n\nLog 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.\n\n`prescryb`\n\nsupplies the primitives (SSH inventory, CVE matching, live advisory\nlookups, compliance-topic mapping, Ansible playbook rendering). The\nconnected model does the reasoning: which findings matter, which CVEs to\ndig into, which playbook to generate. `prescryb`\n\nnever applies anything to the\ntarget host - every tool is read-only against it, or pure text/data\ngeneration.\n\n| Tool | What it does |\n|---|---|\n`inventory_host(host, user=\"\", port=22, hostname=\"\", identity_file=\"\", trust_unknown_host=False)` |\nSSH in, detect the distro, list installed packages with versions. |\n`check_cves(system, packages)` |\nBatch-match package versions against\n|\n\n`fetch_advisory(cve_id)`\n\n`fetch_epss(cve_ids)`\n\n[EPSS](https://www.first.org/epss/api)exploitation-probability scores for CVE IDs not already covered by`check_cves`\n\n(e.g. from `fetch_advisory`\n\nor a web search).`map_compliance(area)`\n\n`\"ssh\"`\n\n, `\"sudo\"`\n\n, `\"kernel modules\"`\n\n, ...) to CIS/DISA STIG topic areas and, if present in the `konstruktoid.hardening`\n\nGitHub repo, the matching role - plus the MITRE ATT&CK techniques and mitigations that area addresses.`lookup_cce(target, keyword, cve_id)`\n\n[NIST CCE](https://ncp.nist.gov/cce)(Common Configuration Enumeration) entries for a platform (e.g.`\"rhel8\"`\n\n), sourced from the community JSON conversion at [.](https://github.com/konstruktoid/cce-web)`konstruktoid/cce-web`\n\n`list_cce_targets()`\n\n`lookup_cce`\n\ncan query.`generate_playbook(system, cve_matches, compliance_areas, hosts_alias)`\n\n**suggest-only** Ansible playbook: CVE fixes become package-upgrade tasks, compliance areas become`roles:`\n\nreferences.Typical flow: `inventory_host`\n\n, then `check_cves`\n\non the returned packages,\nthen optionally `fetch_advisory`\n\non interesting CVEs, then `map_compliance`\n\n(and `lookup_cce`\n\n) for any insecure-config areas noticed, then\n`generate_playbook`\n\nto produce something to review.\n\n```\nuv sync\n```\n\nClaude Code:\n\n```\nclaude mcp add prescryb -- uv --directory /path/to/prescryb run prescryb\n```\n\nClaude Desktop (`claude_desktop_config.json`\n\n):\n\n```\n{\n  \"mcpServers\": {\n    \"prescryb\": {\n      \"command\": \"uv\",\n      \"args\": [\"--directory\", \"/path/to/prescryb\", \"run\", \"prescryb\"]\n    }\n  }\n}\n```\n\n`inventory_host`\n\nnever accepts a password argument. MCP tool-call arguments\ncan be logged by clients and are visible to the connected model, so\ncredentials must never flow through them. Auth works exactly like running\n`ssh host`\n\nyourself:\n\n- Host/user/port/identity files are resolved from\n`~/.ssh/config`\n\n. - Keys come from an SSH agent or the default identity files.\n`inventory_host`\n\n's`hostname`\n\n/`identity_file`\n\narguments override the resolved address/key path directly, for hosts you do not want to add to`~/.ssh/config`\n\n(only a path is passed, never key contents).- Unknown host keys are\n**rejected** unless you pass`trust_unknown_host=True`\n\n- prefer running\n`ssh host`\n\nmanually once to pin the key instead.\n\n- prefer running\n\n```\nclaude 'run vagrant up, connect to the created VM, check any vulnerabilities\nand suggest a fix, include compliance mapping if possible,\nwrite the playbook suggestion to /tmp/ and print the file location'\n```\n\nFor a host already reachable via `ssh`\n\n(resolved through `~/.ssh/config`\n\n,\nan agent key, or the default identity file), specify the hostname\ndirectly; no port or identity-file configuration is required:\n\nInventory prod-web-01, check installed packages, find CVEs, and suggest a fix - Ansible if possible, and tell me what compliance controls it maps to.\n\nIf the host is not in `~/.ssh/config`\n\nyet, either add a `Host`\n\nblock or pass\n`user`\n\n/`port`\n\n/`hostname`\n\n/`identity_file`\n\nstraight to `inventory_host`\n\nfor a\none-off connection - same as the molecule example below.\n\n```\nmolecule converge -s default\n```\n\nFind the `ssh_port`\n\n/`ssh_user`\n\nfrom that scenario's `molecule.yml`\n\nplatform\nentry (e.g. `ssh_port: 22201`\n\n, `ssh_user: almalinux`\n\nfor the `almalinux10`\n\nplatform) and the private key `molecule login`\n\nuses to connect - either add\na `Host`\n\nblock to `~/.ssh/config`\n\n, or skip the file entirely and pass them\nstraight to `inventory_host`\n\nfor a one-off, ephemeral connection:\n\nInventory 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.\n\nRun `molecule destroy -s default`\n\nwhen finished; `prescryb`\n\nwill not do it\nfor you, and will not touch the instance beyond reading it.\n\n`map_compliance`\n\nnames topic areas (e.g. \"SSH Server Configuration\") and, if\nfound in the [ konstruktoid/ansible-collection-hardening](https://github.com/konstruktoid/ansible-collection-hardening) GitHub repository, a link to the\nAnsible role that implements it.\n\nBy default it queries the GitHub API against\n[ konstruktoid/ansible-collection-hardening](https://github.com/konstruktoid/ansible-collection-hardening).\nOverride with:\n\n```\nexport HARDENING_COLLECTION_REPO=owner/repo\n```\n\nSet `GITHUB_TOKEN`\n\nto raise the (otherwise low) unauthenticated GitHub API\nrate limit. If a role is not found in the repo, `map_compliance`\n\nstill\nreturns the topic/framework/role name so you know what to install\n(`ansible-galaxy collection install konstruktoid.hardening`\n\n).\n\n`lookup_cce`\n\nlooks up [NIST Common Configuration Enumeration](https://ncp.nist.gov/cce)\nentries - unique identifiers for individual configuration checks, distinct\nfrom the topic-area CIS/DISA STIG mapping above. NIST only publishes CCE as\nspreadsheets, so this reads the pre-converted JSON exports hosted by the\ncommunity project [ konstruktoid/cce-web](https://github.com/konstruktoid/cce-web)\ninstead of parsing Excel.\n\nCoverage is per-platform, not per-topic, and thin for this project's target\ndistros: only RHEL-family (`rhel6`\n\n/`rhel7`\n\n/`rhel8`\n\n- AlmaLinux/Rocky use the\nmatching upstream RHEL number) and SUSE\n(`SLES12-DISA-STIG`\n\n/`SLES15-DISA-STIG`\n\n/`SLES15-PCI-DSS`\n\n) have usable data.\nDebian, Ubuntu, Alpine, and Arch have no CCE data upstream at all. A few\nolder `cce-web`\n\nexports (e.g. `rhel4`\n\n, `rhel5`\n\n, `apache-httpd2.2`\n\n) lost\ntheir column headers in the upstream Excel-to-JSON conversion; those are\nreported as unsupported rather than returning garbled fields. Call\n`list_cce_targets`\n\nto see every published platform, including non-Linux\nones (`firefox`\n\n, `win2k8r2`\n\n, ...).\n\nOverride the source repo with:\n\n```\nexport CCE_REPO=owner/repo\n```\n\nAlongside CIS/DISA STIG, `map_compliance`\n\nand `generate_playbook`\n\nalso cite\nthe [MITRE ATT&CK](https://attack.mitre.org) technique(s) mitigated by a\ntopic area's hardening (e.g. \"ssh\" maps to `T1110`\n\nBrute Force and\n`T1021.004`\n\nRemote Services: SSH) and, where ATT&CK defines one, the\ncorresponding mitigation (e.g. `M1032`\n\nMulti-factor Authentication) with a\nlink to attack.mitre.org. Unlike CIS/DISA STIG rule numbers, ATT&CK\ntechnique and mitigation IDs are MITRE's own public catalog, so they are\ncited directly rather than needing a licensed benchmark lookup. This mapping\nis static (built into `attack.py`\n\n), not fetched live.\n\n**OSV.dev** is the sole CVE-matching source. It resolves`{name, ecosystem, version}`\n\nserver-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 for**Debian, Ubuntu, Alpine**; thinner for RHEL-family (AlmaLinux, Rocky) and SUSE.`check_cves`\n\nreturns a`warning`\n\nfield 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`\n\n) is used only to enrich a CVE you already have the ID for. Set`NVD_API_KEY`\n\nto raise the (otherwise low) unauthenticated rate limit.**EPSS**(`epss_score`\n\n/`epss_percentile`\n\non every`check_cves`\n\nmatch, or`fetch_epss`\n\nfor 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 by`cve_id`\n\n,`severity`\n\n, or`epss_score`\n\n. No API key needed. CVEs with no EPSS record (very new, reserved, or rejected IDs) simply have`epss_score`\n\nunset - not an error. A FIRST.org outage surfaces as a`warning`\n\non`check_cves`\n\nrather than failing the CVE match itself.- Severity: OSV gives a raw CVSS vector string (\n`cvss_vector`\n\n), not a precomputed label, for most OS-package entries.`severity`\n\nis only populated when the source explicitly labels it; otherwise it is`\"UNKNOWN\"`\n\nand the vector is left for you (or the model) to interpret, rather than guessing.\n\nOutput is always a full playbook as text, prefixed with a comment header\nciting every CVE/compliance source used. It is never executed by `prescryb`\n\n.\nReview it - `ansible-playbook --syntax-check`\n\n, then `--check --diff`\n\n- before\nrunning it anywhere.\n\nPackage-upgrade tasks use the module for the target's package manager\n(`ansible.builtin.apt`\n\n/`dnf`\n\n/`zypper`\n\n/`community.general.apk`\n\n). Version pins\nare only applied where the module supports them; Arch/pacman targets get\n`state: latest`\n\nsince pacman does not support the same pinning syntax.\n\n| Variable | Default | Purpose |\n|---|---|---|\n`HARDENING_COLLECTION_REPO` |\n`konstruktoid/ansible-collection-hardening` |\nGitHub `owner/repo` queried for compliance-mapped Ansible roles. |\n`CCE_REPO` |\n`konstruktoid/cce-web` |\nGitHub `owner/repo` queried for CCE JSON exports by `lookup_cce` /`list_cce_targets` . |\n`GITHUB_TOKEN` |\nunset | Raises GitHub API rate limits for `map_compliance` , `lookup_cce` , and `list_cce_targets` . |\n`NVD_API_KEY` |\nunset | Raises NVD API rate limits for `fetch_advisory` . |\n\n- Does not apply playbooks or otherwise mutate the target host.\n- Does not accept passwords as tool arguments.\n- Does not fabricate CIS/DISA STIG rule numbers.\n- Does not guess CVEs for ecosystems OSV does not cover; it reports that instead.", "url": "https://wpnews.pro/news/prescryb-an-mcp-server-for-cve-and-config-remediation", "canonical_source": "https://github.com/konstruktoid/prescryb", "published_at": "2026-07-12 22:56:17+00:00", "updated_at": "2026-07-12 23:05:18.331934+00:00", "lang": "en", "topics": ["ai-tools", "ai-agents", "developer-tools"], "entities": ["Prescryb", "Claude Desktop", "Claude Code", "Ansible", "CVE", "NIST", "MITRE ATT&CK", "CIS"], "alternates": {"html": "https://wpnews.pro/news/prescryb-an-mcp-server-for-cve-and-config-remediation", "markdown": "https://wpnews.pro/news/prescryb-an-mcp-server-for-cve-and-config-remediation.md", "text": "https://wpnews.pro/news/prescryb-an-mcp-server-for-cve-and-config-remediation.txt", "jsonld": "https://wpnews.pro/news/prescryb-an-mcp-server-for-cve-and-config-remediation.jsonld"}}