Skip to main content
Glama
konstruktoid

prescryb

by konstruktoid

prescryb - A remediation orchestrator

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.

How it works

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 OSV.dev using its ecosystem-aware version comparison (not name-only matching). Each match is enriched with an EPSS exploitation-probability score.

fetch_advisory(cve_id)

Fetch the current NVD record for one CVE - description, CVSS, CWE, references - live, not from training data.

fetch_epss(cve_ids)

Batch-fetch EPSS exploitation-probability scores for CVE IDs not already covered by check_cves (e.g. from fetch_advisory or a web search).

map_compliance(area)

Map a free-text topic ("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, cce_id)

Look up 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()

List every platform lookup_cce can query.

list_local_docs()

List documents (Markdown/text/PDF) found under the local, gitignored LOCAL_DOCS_DIR.

search_local_docs(query, top_k=5)

RAG-style semantic search over those local documents, embedded entirely on-machine.

generate_playbook(system, cve_matches, compliance_areas, hosts_alias)

Render a suggest-only Ansible playbook: CVE fixes become package-upgrade tasks, compliance areas become roles: 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 optionally search_local_docs for relevant internal runbook/policy context, then generate_playbook to produce something to review.

flowchart TD
    U["Operator: natural-language request"] --> M["Connected model\n(Claude Desktop / Claude Code)"]
    M --> A["inventory_host\nSSH in, list packages"]
    A --> B["check_cves\nOSV.dev match + EPSS score"]
    B --> C{"Interesting\nCVE?"}
    C -->|yes| D["fetch_advisory\nNVD detail for one CVE"]
    C -->|no| E
    D --> E["map_compliance / lookup_cce\nCIS/DISA STIG + ATT&CK mapping"]
    E --> F["search_local_docs\ninternal runbooks (optional)"]
    F --> G["generate_playbook\nsuggest-only Ansible playbook"]
    G --> H["Model reasons over the results\nand presents findings + playbook"]
    H --> R["Operator reviews\n(ansible-playbook --check --diff)\nand applies manually"]

    style R fill:#f9f,stroke:#333,stroke-width:1px

Only the tool boxes are prescryb: inventory_host, check_cves, fetch_advisory, map_compliance/lookup_cce, search_local_docs, and generate_playbook - each a read-only lookup or pure text/data generation call. The operator and connected model are not part of prescryb, and nothing in this chain touches the target host beyond inventory_host's read-only SSH session - applying the generated playbook is a deliberate, separate step the operator takes outside prescryb.

Related MCP server: Red Hat Lightspeed MCP

Install

uv sync

Register with an MCP client

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"]
    }
  }
}

SSH auth model

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's hostname/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 pass trust_unknown_host=True

    • prefer running ssh host manually once to pin the key instead.

Example: running claude against the repository Vagrant VM

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'

Example: checking a regular host

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.

Example: inspecting a molecule test instance

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.

Compliance mapping

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).

CCE lookup

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

MITRE ATT&CK mapping

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.

CVE data sources and their limits

  • 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 for Debian, Ubuntu, Alpine; thinner for RHEL-family (AlmaLinux, Rocky) and SUSE. check_cves returns a warning 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. Set NVD_API_KEY to raise the (otherwise low) unauthenticated rate limit.

  • EPSS (epss_score/epss_percentile on every check_cves match, or fetch_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 by cve_id, severity, or epss_score. No API key needed. CVEs with no EPSS record (very new, reserved, or rejected IDs) simply have epss_score unset - not an error. A FIRST.org outage surfaces as a warning on check_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.

Local documents

search_local_docs and list_local_docs are a RAG-style complement to the network-backed tools above: semantic search over documents an operator places in a local directory (default local_docs/, override with LOCAL_DOCS_DIR) - internal runbooks, hardening policy, past remediation notes - that no public source knows about. Supported formats: Markdown (.md/.markdown), plain text (.txt), and PDF (.pdf); dotfiles, dotdirs (e.g. .git/), and symlinks are skipped.

mkdir local_docs
cp ~/ssh-hardening-runbook.pdf local_docs/

local_docs/ is already listed in .gitignore - these are operator-supplied and machine-local, never committed to this repository.

Documents are split into paragraph-preferring chunks and embedded locally with sentence-transformers (default model sentence-transformers/all-MiniLM-L6-v2, override with LOCAL_DOCS_MODEL). Embedding and ranking happen entirely on this machine - the only network access this feature needs is downloading the model weights from Hugging Face the first time it runs; after that first run indexing and search are fully offline (set HF_HUB_OFFLINE=1 to force it, once the model is cached). That is a claim about prescryb, not about the client: search_local_docs returns the matched chunks as its tool result, so the query and those chunks reach the connected MCP client and whatever model it is driving, exactly like every other tool's output. Put nothing in LOCAL_DOCS_DIR you would not send to that client. The chunk index is rebuilt in memory whenever a file under LOCAL_DOCS_DIR is added, removed, or modified, and reused otherwise.

A file that can't be read (e.g. a corrupt PDF), yields no extractable text (e.g. a scanned, image-only PDF that would need OCR first), or exceeds the 20 MB per-file limit, is skipped rather than failing the whole index; list_local_docs's skipped field names it and why. A skipped file still appears in documents - it is listed, but none of its content is searchable. Indexing is also capped at 500 files: LOCAL_DOCS_DIR should point at a directory dedicated to this purpose, not something broad like a home directory - both caps exist to bound indexing cost and to stop an overbroad directory from pulling unrelated local files into search results. Indexing stops at 5000 chunks overall, whichever files that spans, so a few very large documents cannot exhaust memory on their own. Both tools' truncated field is true if either cap left content out of the index.

Requirements

  • uv sync installs sentence-transformers and pypdf; this pulls in torch, so the sync is noticeably larger than the rest of this project's dependencies.

  • Outbound HTTPS to huggingface.co the first time search_local_docs (or list_local_docs, which also builds the index) runs, to download the embedding model - about 90 MB for the default model, cached under ~/.cache/huggingface (override with HF_HOME) so later runs, including after a restart, need no network. No connectivity is required beyond that: prescryb itself sends document content and queries nowhere, though search results do go back to the connected MCP client as tool output.

  • At least one file under LOCAL_DOCS_DIR - the tools return an empty result, not an error, if the directory is missing or empty.

Example: grounding a fix in an internal runbook

Check host db-01 for CVEs, and see if our internal runbooks say anything relevant before you suggest a fix.

This drives inventory_host and check_cves as usual, then search_local_docs against whatever's in local_docs/ (e.g. an internal Postgres patching runbook) so the connected model can fold that guidance into its suggestion alongside the CVE data. The runbook is never uploaded anywhere for indexing; the matched excerpts do reach that model, since answering with them is the point.

Playbook generation

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.

Environment variables

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.

LOCAL_DOCS_DIR

local_docs

Directory search_local_docs/list_local_docs read from.

LOCAL_DOCS_MODEL

sentence-transformers/all-MiniLM-L6-v2

Hugging Face model ID used to embed local documents.

Development

Install with the test extras (pytest, ruff, ty, numpy for the local-docs tests):

uv sync --extra test

Before considering any change to src/ or tests/ done, run all of:

uv run ruff check .
uv run ruff format --check .
uv run ty check
uv run pytest

ruff/ty apply to tests/ as well as src/; tests/per-file-ignores in pyproject.toml only relaxes docstring (D103), assert (S101), and private-member-access (SLF001) rules there, since those are normal in test code.

Test layout: one tests/test_<module>.py per src/prescryb/<module>.py that has coverage, following pytest's plain function style already used throughout - no test classes, no third-party mocking/fixture library beyond pytest's own monkeypatch and tmp_path. Network-facing code (paramiko, httpx calls to OSV/NVD/EPSS/GitHub/cce-web) is exercised through small hand-written fakes substituted via monkeypatch, not real sockets. Pure data-transform helpers (parsers, extractors, dataclass<->dict round-trips, playbook rendering) are called directly.

Coverage is currently uneven: parsing/extraction/rendering logic across ssh.py, cve.py, playbook.py, cce.py, server.py, and all of docs.py has tests; the async network-calling functions in epss.py, advisories.py, compliance.py, and most of cce.py (fetch_target, list_targets, resolve_target) do not yet - adding those would need an httpx.MockTransport fixture rather than monkeypatch alone.

.github/workflows/lint.yml runs ruff check, ruff format --check, ty check, pytest, and pip-audit on every push/PR.

What this deliberately does not do

  • 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.

Available Tools

7 tools
check_cvesA

Match installed package versions against known CVEs via OSV.dev.

`system` and `packages` are the objects returned by inventory_host (or a
filtered subset of `packages` if you only want to check specific ones).
Uses OSV's server-side ecosystem-aware version comparison rather than
name-only matching, so results reflect the exact installed version.
ParametersJSON Schema
NameRequiredDescriptionDefault
systemYes
packagesYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full burden. It discloses that matching is ecosystem-aware and version-specific, adding significant behavioral context. It does not detail potential side effects or auth requirements, but given the read-only nature, it is adequate.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences with no wasted words. Each sentence adds distinct value: action, input source, and matching methodology. Well-structured and front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers purpose and usage well, but given the complexity of nested object parameters and 0% schema coverage, it lacks completeness in parameter documentation. Output schema exists to cover return values, but input semantics are insufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% with no parameter descriptions. The description only states that system and packages are objects from inventory_host, but does not explain their internal structure or required fields. More detail is needed to compensate for the lack of schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's action ('Match installed package versions against known CVEs') and specifies the external service (OSV.dev). It distinguishes from sibling tools like inventory_host and fetch_advisory by indicating the input source and the nature of matching.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains that inputs come from inventory_host and allows filtering by subset of packages, providing clear context for when to use this tool. However, it does not explicitly state when not to use it or mention alternatives among siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fetch_advisoryA

Fetch the current, authoritative NVD record for a specific CVE ID.

Use this to get an up-to-date description, CVSS score/severity, CWE
weakness classification, and reference links for a CVE surfaced by
check_cves (or one you already know about), rather than relying on
potentially stale training data.
ParametersJSON Schema
NameRequiredDescriptionDefault
cve_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.8/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description must bear the burden. It mentions fetching a record but does not disclose side effects (likely read-only), rate limits, or authentication needs. Lacks behavioral context beyond purpose.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences: first states purpose, second gives usage guidance and return info. No wasted words, front-loaded with key information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Simple tool with one parameter and output schema. Description covers what it does, what it returns, and when to use. Slightly lacking in behavioral details, but sufficient given output schema existence.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Only one parameter, cve_id, with 0% schema description coverage. The description implies it's a CVE ID but adds no format or constraints. Minimal added value over the parameter name.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Fetch the current, authoritative NVD record for a specific CVE ID', specifying verb, resource, and scope. It distinguishes from siblings like check_cves by emphasizing authority and specificity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says to use after check_cves or for known CVEs, and why (to avoid stale data). Lacks explicit when-not, but context is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

generate_playbookA

Generate a suggest-only Ansible playbook from findings. Does NOT run it.

`system`: object from inventory_host. `cve_matches`: entries from
check_cves you want remediated (each becomes a package-upgrade task
citing the CVE). `compliance_areas`: topic hints (e.g. ["ssh", "sudo"])
- each resolved via map_compliance and, where a matching
konstruktoid.hardening role exists, referenced in the playbook's
`roles:` list instead of reimplemented. The header also cites the MITRE
ATT&CK techniques/mitigations each area addresses. Always review the
output with `ansible-playbook --check --diff` before applying.
ParametersJSON Schema
NameRequiredDescriptionDefault
systemYes
cve_matchesNo
hosts_aliasNo
compliance_areasNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description fully discloses key behavioral traits: the playbook is 'suggest-only' and not executed, output should be reviewed with 'ansible-playbook --check --diff', compliance areas are resolved via map_compliance and may reference a hardening role, and MITRE ATT&CK techniques are cited. This goes beyond what any annotations would provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is relatively concise given the complexity, front-loaded with the main purpose, and uses clear structure with code formatting. A few redundant phrases could be trimmed, but overall it earns its keep.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 4 parameters, nested objects, and existing output schema, the description is thorough. It explains the input sources, parameter behavior, and post-generation step, though it doesn't detail the return format beyond suggesting it's a YAML playbook.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Despite 0% schema coverage, the description adds significant meaning: 'system' is an object from inventory_host, 'cve_matches' become package-upgrade tasks, and 'compliance_areas' are topic hints resolved via map_compliance. However, 'hosts_alias' is not explained, leaving a gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Generate a suggest-only Ansible playbook from findings. Does NOT run it.' This specific verb-resource pair and the explicit exclusion of execution make the purpose unmistakable and distinct from siblings that perform checks or lookups.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context on when to use the tool: after inventory_host, check_cves, and map_compliance. It explains the role of each parameter, but does not explicitly state when not to use it or compare directly to alternative tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

inventory_hostA

SSH into host and inventory installed packages.

Auth uses ~/.ssh/config, an SSH agent, and default identity files - the
same as running `ssh host` yourself. No password argument is accepted:
credentials must never flow through MCP tool-call arguments. Unknown
host keys are rejected unless trust_unknown_host=True; prefer connecting
with `ssh host` once yourself to pin the key instead.

`hostname`/`identity_file` override the resolved address and key path
without editing ~/.ssh/config - handy for e.g. a local molecule/vagrant
instance. Only a path is passed, never key contents.
ParametersJSON Schema
NameRequiredDescriptionDefault
hostYes
portNo
userNo
hostnameNo
identity_fileNo
trust_unknown_hostNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description fully discloses key behaviors: authentication method, no password argument for security, unknown host key rejection with trust_unknown_host option, and that only file paths (not contents) are passed. This gives the agent a complete safety and operation picture.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is structured well with a clear first sentence stating the main action, followed by details on auth, security, and overrides. It is concise enough but could be trimmed slightly without losing clarity. Every sentence adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (6 parameters, no annotations, output schema exists), the description covers authentication, security constraints, and parameter use for overrides. Missing details on port and user are a minor gap, but overall it provides sufficient context for correct tool use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must compensate. It explains host, hostname, identity_file, and trust_unknown_host, but does not mention the port or user parameters. These are important for SSH connections and their defaults (22 and empty string) are noted in the schema, but the description misses the chance to clarify their role.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it SSHs into a host and inventories installed packages, which is a specific verb+resource. It distinguishes from sibling tools (check_cves, fetch_advisory, etc.) that focus on security/compliance, not package inventory.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains authentication via ~/.ssh/config, SSH agent, and default identity files, likening it to running `ssh host` yourself. It clarifies when to use overrides (e.g., local molecule/vagrant instances) and how to handle unknown host keys. It lacks explicit 'when not to use' or comparisons to siblings, but context is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_cce_targetsA

List platform names lookup_cce can query (e.g. 'rhel8', 'firefox').

Sourced live from https://github.com/konstruktoid/cce-web, the community
JSON conversion of NIST's CCE spreadsheets. Coverage of this project's
target distros is thin: only RHEL-family ('rhel6'/'rhel7'/'rhel8') and
SUSE ('SLES12-DISA-STIG'/'SLES15-DISA-STIG'/'SLES15-PCI-DSS') are
usable - Debian, Ubuntu, Alpine, and Arch have no CCE data upstream.
ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full burden. It discloses that the tool lists platform names (read-only), the data source (a GitHub repo), and important limitations on usable targets. This provides adequate behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise with two sentences in the first paragraph for the main purpose, followed by a necessary second paragraph for additional context. Every sentence adds value, and it is front-loaded with the primary action.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has no parameters and an output schema (implied), the description provides all necessary context: what it does, its data source, and critical usage caveats about available targets. It is complete for a simple list tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has zero parameters, so the description is not required to add parameter meaning. The baseline for 0 parameters is 4, and the description neither adds nor detracts.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb (list) and the resource (platform names that lookup_cce can query), with concrete examples like 'rhel8' and 'firefox'. It also implicitly distinguishes from sibling tools by indicating that this tool provides targets for lookup_cce.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides implicit guidance by stating that coverage is thin and listing only usable targets (RHEL-family and SUSE). However, it does not explicitly say when to use this tool versus alternatives, though the context of sibling tools suggests its role as a prerequisite for lookup_cce.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

lookup_cceA

Look up NIST Common Configuration Enumeration entries for a platform.

`target`: free text naming a platform, resolved against the export
names published by https://github.com/konstruktoid/cce-web (NIST itself
only publishes CCE as spreadsheets). For a host from inventory_host, use
'rhel<major version>' for any RHEL-family distro_id (rhel, almalinux,
rocky - CCE only tracks the upstream RHEL number) or
'SLES<major version>-DISA-STIG' for suse; Debian, Ubuntu, Alpine, and
Arch have no CCE coverage upstream at all. Call list_cce_targets to see
every published platform (also covers e.g. 'firefox', 'apache-httpd2.2',
'win2k8r2').

Pass `keyword` (matched against title/description/rationale/config
group) or `cce_id` (exact ID, e.g. 'CCE-80876-6') to filter results.
Without either, only the platform's config-group categories and entry
count are returned - dumping an entire platform (hundreds of entries)
isn't useful; narrow with a keyword first.
ParametersJSON Schema
NameRequiredDescriptionDefault
cce_idNo
targetYes
keywordNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so description covers the behavior: returns filtered entries or categories/count, explains target resolution, and warns against dumping entire platform. Safe read operation implied.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Multiple paragraphs but each sentence adds value. Structured with summary line, target details, and filtering options. Could be slightly more concise but not wasteful.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 3 params, no annotations, but output schema exists. Explains behavior with and without filters, covers main use cases. Lacks mention of error handling or missing targets, but overall adequate.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has 0% description coverage; description compensates fully by detailing target formation (examples, resolution), keyword matching fields, and cce_id with example. All three parameters are well explained.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states it looks up NIST CCE entries for a platform, using specific verbs and resource. It distinguishes from siblings like list_cce_targets and check_cves.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides clear guidance on when to use target, keyword, cce_id, and directs to list_cce_targets for platform discovery. Implicitly covers when not to use by explaining behavior without filters.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

map_complianceA

Map a free-text topic to CIS Benchmark / DISA STIG topic areas and ATT&CK.

`area` examples: 'ssh', 'sudo', 'kernel modules', 'password policy'. If a
matching role is found in the konstruktoid.hardening GitHub repo, it is
returned too. Also returns the MITRE ATT&CK techniques (and, where
defined, the ATT&CK mitigation) that hardening this area addresses.

Only topic-area mapping is returned for CIS/DISA STIG, never fabricated
specific rule IDs (e.g. "CIS 5.2.1") - those require the licensed
benchmark text. Consult the referenced role's own docs/tags for exactly
what it covers. ATT&CK technique/mitigation IDs, by contrast, are
MITRE's own public catalog (attack.mitre.org), so they're cited directly.
ParametersJSON Schema
NameRequiredDescriptionDefault
areaYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Since no annotations are provided, the description fully bears the burden. It discloses that only topic-area mapping is returned, not rule IDs, and explains why ATT&CK IDs are cited directly.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with a clear opening sentence and explanatory notes, though it is slightly verbose with disclaimers. It earns its place but could be tightened.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the existence of an output schema, the description adequately explains the return values (role, ATT&CK techniques, mitigations) without over-specifying format.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description compensates by providing concrete examples for the 'area' parameter (e.g., 'ssh', 'sudo') and explaining its purpose.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool maps a free-text topic to CIS Benchmark/DISA STIG topic areas, ATT&CK techniques, and optionally a GitHub role. It uses specific verbs and distinguishes from sibling tools like check_cves and generate_playbook.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use the tool (mapping topics) and what not to expect (no specific rule IDs). It also provides alternative contexts by referencing licensed vs. public catalogs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 7 tool updatesv0.1.0
    • First observedcheck_cves
    • First observedfetch_advisory
    • First observedgenerate_playbook
    • First observedinventory_host
    • First observedlist_cce_targets
    • First observedlookup_cce
    • First observedmap_compliance

TDQS

A4.4/5.0

Scored across 7 tools

Disambiguation5/5

Each tool has a unique, well-defined purpose: checking CVEs, fetching advisories, generating playbooks, inventorying hosts, listing CCE targets, looking up CCE entries, and mapping compliance topics. No functional overlap exists.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern in snake_case (e.g., check_cves, fetch_advisory, generate_playbook). The naming style is uniform and predictable.

Tool Count5/5

With 7 tools, the set is well-scoped for its security compliance domain. Each tool covers a necessary step in the workflow (inventory, CVE scanning, advisory lookup, compliance mapping, playbook generation) without extraneous additions.

Completeness5/5

The tool surface covers the full lifecycle: inventory → CVE matching → advisory retrieval → compliance mapping (CCE and CIS/DISA) → playbook generation. No critical gaps are apparent for the stated purpose of automated compliance remediation.

Maintenance

ActivityActive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    An MCP server that exposes SOC2 and HIPAA compliance remediation logic as structured tools for AI agents to call, enabling an LLM-driven workflow to discover, assess, remediate, and report on compliance controls.
    MIT
  • A
    license
    B
    quality
    A
    maintenance
    A lightweight, self-hosted MCP server that connects LLM-based agents to Red Hat Lightspeed services, enabling natural language querying of read-only (and optionally write) operations across Advisor, Image Builder, Inventory, Planning, Remediations, and Vulnerability services.
    42
    24
    Apache 2.0
  • A
    license
    A
    quality
    A
    maintenance
    MCP server that provides deterministic remediation plans from vulnerability scan data (Trivy, Grype, Sysdig) via tools to generate plans from scan content or fetch live from Sysdig API, enabling AI agents to produce concrete, distro-aware fix commands.
    2
    2
    Apache 2.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    MCP server that provides SSH tools (read-only probes and arbitrary exec) to a fleet of hosts outside Kubernetes, with an inventory-based allowlist and key-based authentication.
    MIT