psirt-exposure
Integrates with Cisco PSIRT security advisories and Cisco IOS XE device data to determine whether each device is actually affected, checking software versions and feature-enabling configuration lines and returning verdicts with supporting evidence.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@psirt-exposurecheck my devices against Cisco PSIRT advisories and show the evidence"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Status: pre-1.0 (alpha) · Cisco IOS XE · 10 reviewed rules · all sample data in this repository is fake. Stack: Python 3.11+ standalone CLI and MCP server, uv, ciscoconfparse2, PSIRT openVuln API, CISA KEV; optional NetBox and Ollama (Foundation-Sec-8B).
Use Case
The Cisco PSIRT openVuln API tells you which software versions an advisory affects and which are
fixed. It does not tell you whether the vulnerable feature is enabled on your device. Many
advisories only apply when a specific feature is configured (for example ip http server), and
Cisco no longer publishes OVAL definitions, so engineers still read every advisory and check every
device by hand with show commands.
psirt-exposure automates that check:
Collect: pull advisories from the PSIRT openVuln API and devices from config files or NetBox.
Extract: a security LLM (Cisco Foundation-Sec, run locally) reads the advisory once and turns the "vulnerable only if..." condition into a structured, human-reviewable YAML rule.
Match: a deterministic engine evaluates each rule against each device's version and config. No LLM runs per device.
Report: every device gets one verdict, ranked by active exploitation (CISA KEV):
Verdict | Meaning |
| Vulnerable version and the vulnerable feature is enabled |
| Vulnerable version, but the feature is not enabled |
| Running a fixed release |
| Vulnerable, but the advisory's workaround is applied |
| Needs runtime data; the exact |
Each verdict comes with the reason, the config lines it was based on (secrets redacted) and a link to the advisory.
Outcomes: one triage table instead of reading N advisories × M devices, evidence that a reviewer can verify in seconds, KEV-first prioritization, and drift alerts when a config change re-opens an exposure. On the 60-pair golden set the reviewed rules get every verdict right (an upper bound, see Known issues).
Architecture
flowchart LR
subgraph Sources
PSIRT["Cisco PSIRT openVuln API<br/>(or local CSAF files)"]
KEV["CISA KEV catalog"]
INV["Device configs + show version<br/>(files or NetBox)"]
end
subgraph Offline["Once per advisory"]
LLM["Local LLM<br/>Foundation-Sec-8B (Ollama)"]
DRAFT["Draft rule (YAML)"]
REVIEW{"Human review"}
end
RULES["rules/*.yaml<br/>status: reviewed"]
PARSE["IOS XE config parser<br/>(ciscoconfparse2)"]
MATCH["Deterministic matcher<br/>exposure + mitigations + version"]
OUT["Verdicts with evidence<br/>CLI table / JSON / Markdown / HTML"]
MCP["MCP server (stdio)<br/>list_devices, list_rules, check, report"]
WATCH["watch: drift vs. last run"]
PSIRT -- advisory text --> LLM --> DRAFT --> REVIEW --> RULES
PSIRT -- affected versions --> MATCH
KEV -- exploited CVEs --> MATCH
INV --> PARSE --> MATCH
RULES --> MATCH
MATCH --> OUT
MATCH --> MCP
MATCH --> WATCHThe LLM only turns advisory text into a rule, once, and a human reviews the rule before check
uses it. Per-device matching has no LLM in the loop: the same inputs always give the same verdicts.
Configs never leave the host; in live mode only software versions are sent to Cisco.
Related MCP server: network-mcp-server
Installation
Requirements: Python 3.11+, git and uv. uv installs a suitable Python if you do not have one. No account or API key is needed for offline mode.
Install uv:
# macOS
brew install uv # or the Linux command below
# Linux and macOS
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows (PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"Clone and install:
git clone https://github.com/faithere1/psirt-exposure
cd psirt-exposure
uv sync
uv run psirt-exposure --helpTested on macOS; CI runs on Linux (Ubuntu). Windows should work with the same uv commands but is
not tested yet.
Configuration
Offline mode needs no configuration. For live data, copy the template and fill in only what you
use (.env is gitignored; never put credentials anywhere else):
cp .env.example .env # Windows: copy .env.example .envVariable | Needed for | Format |
| live advisory/version data ( | Client credentials of a free Cisco API Console app with the PSIRT openVuln API |
|
| Base URL with scheme, no |
|
| NetBox API token: v1 (40 hex chars) or v2 ( |
|
| API key of your OpenAI-compatible server |
| local NetBox lab only | Generated by |
Device data in file mode is one directory per device:
<configs-dir>/<hostname>/running-config.txt # output of "show running-config" (required)
<configs-dir>/<hostname>/show-version.txt # output of "show version" (recommended: PID + version)Without show-version.txt the version and PID are unknown: a disabled feature still gives
NOT_AFFECTED_FEATURE_DISABLED, but an enabled one (or a rule that needs the platform) gives
INDETERMINATE. Point --configs at your own directory; keep real configs outside this repository.
Usage
Check devices (offline)
uv run psirt-exposure check --offline --evidence # all sample devices x all rules
uv run psirt-exposure check --offline --device edge-rtr-01 --format json
uv run psirt-exposure check --offline --configs ~/my-configs --rule webui --rule snmpOffline mode reads rules/ and samples/configs/<hostname>/{running-config,show-version}.txt.
Version status comes from the local CSAF files in tests/fixtures/psirt/ (--advisories DIR):
a version listed as known_affected for "Cisco IOS XE Software" is affected; an unlisted version
is fixed if its release train is listed or newer than every listed train; an older, unlisted
train (Cisco does not evaluate releases past end of maintenance) is INDETERMINATE.
Secrets (SNMP communities, passwords, keys) are redacted in the evidence lines.
Live mode (openVuln + CISA KEV)
cp .env.example .env # then fill in CISCO_CLIENT_ID / CISCO_CLIENT_SECRET
uv run psirt-exposure check --evidenceWithout --offline, check asks the PSIRT openVuln API (OAuth2 client credentials from a
Cisco API Console app) which advisories affect each device's IOS XE
version, one request per distinct version, and flags rule CVEs that are in the
CISA KEV catalog (KEV column).
Mix sources with --versions openvuln|csaf|none and --kev cisa|none|FILE.
Only software versions are sent to Cisco; configs never leave the host.
Live smoke tests are opt-in: PSIRT_EXPOSURE_LIVE=1 uv run pytest -m live.
NetBox inventory
check --netbox reads devices from NetBox instead of --configs: the device type's part number
(or model) is the PID, and two custom fields on devices hold the data that show version and the
config files provide in file mode: software_version (text) and running_config (long text).
Set NETBOX_URL and NETBOX_TOKEN in .env; v1 (Token) and v2 (nbt_…, Bearer) tokens
both work. Narrow the device query with --netbox-filter KEY=VALUE (any NetBox device filter,
repeatable).
A local lab with the sample devices (needs Docker, e.g. brew install colima docker docker-compose && colima start):
uv run python -m psirt_exposure.netbox.secrets >> .env # random lab passwords + API token
docker compose --env-file .env -f netbox/docker-compose.yml up -d --wait
uv run psirt-exposure netbox-seed # idempotent
uv run psirt-exposure check --offline --netbox --netbox-filter site=psirt-labNetBox runs on http://127.0.0.1:8000 (user admin, password NETBOX_SUPERUSER_PASSWORD).
The compose file is a trimmed copy of
netbox-docker; --offline still allows
--netbox, because it only concerns advisory and KEV sources.
Reports
uv run psirt-exposure check --offline --format markdown --output report.md
uv run psirt-exposure check --offline --format html --output report.htmlA report has a verdict summary, a per-device table and one section per AFFECTED,
INDETERMINATE or MITIGATED result: the advisory link and CVEs, CISA KEV dates, the reason, the
redacted evidence lines, the show commands that confirm runtime facts, and the fix. Besides
"upgrade to a fixed release", rules can carry IOS XE config snippets: mitigations[].snippet
applies the advisory's workaround, remediation.snippet removes the exposure (for example
no vstack or no ip http server). <name> marks a value to fill in. The HTML file is
self-contained (no scripts, no external assets).
MCP server
psirt-exposure mcp runs a Model Context Protocol server over
stdio (official mcp Python SDK), so an AI assistant can ask "which of my devices are affected?"
and get deterministic verdicts with evidence.
Tool | Purpose |
| Inventory devices with PID and version ( |
| Reviewed rules: advisory, CVEs, title, exposure summary |
| One verdict per device and rule, with reason, evidence, runtime checks, KEV |
| The Markdown or HTML report described above |
Resource template psirt-exposure://rules/{rule_id} returns a rule as YAML. Tools accept device and
rule names, never file paths; the directories are fixed at start-up (--configs, --rules,
--advisories, --env-file). Defaults are offline (versions="csaf", kev="none"); a client
must ask for versions="openvuln", kev="cisa" or inventory="netbox" explicitly.
Client configuration (Claude Desktop, Claude Code .mcp.json, Cursor, ...):
{
"mcpServers": {
"psirt-exposure": {
"command": "uv",
"args": ["run", "--directory", "/path/to/psirt-exposure", "psirt-exposure", "mcp"]
}
}
}Try it with MCP Inspector:
npx @modelcontextprotocol/inspector uv run --directory "$PWD" psirt-exposure mcp
npx @modelcontextprotocol/inspector --cli uv run --directory "$PWD" psirt-exposure mcp \
--method tools/call --tool-name check --tool-arg only_findings=trueDrift watch
watch re-runs check (same source options) and compares the result with the snapshot of the
previous run, so new exposure from a config change, a new device, updated advisory or version
data, or a new KEV listing shows up as a change:
uv run psirt-exposure watch --offline # first run saves the baseline
uv run psirt-exposure watch --offline # later runs print the changes
uv run psirt-exposure watch --interval 3600 --count 24 # hourly for a day (live sources)The snapshot lives in .psirt-exposure/watch-state.json (--state, gitignored) and holds only
verdicts, exposure, mitigation ids, KEV CVEs and redacted evidence text (no line numbers, so an
unrelated edit higher up in a config is not reported). Change kinds: new, gone, verdict,
evidence, kev. --format json prints machine-readable changes. Exit code: 0 no drift,
1 drift in any run (for cron or CI), 2 setup or state-file error.
Draft rules with an LLM
psirt-exposure extract turns advisory text into a draft rule. The model reply is constrained by
the rule JSON Schema and then validated like a hand-written rule; invalid replies are retried with
the validation error. Advisory IDs, CVEs and titles are copied from the advisory, never generated.
Default backend: Foundation-Sec-8B-Instruct on a local Ollama (advisories stay on the host).
ollama pull hf.co/fdtn-ai/Foundation-Sec-1.1-8B-Instruct-Q4_K_M-GGUF
ollama create foundation-sec-8b-instruct -f ollama/foundation-sec-8b-instruct.Modelfile
uv run psirt-exposure extract # all fixture advisories -> eval/<model>/rules/
uv run psirt-exposure eval-extraction --drafts eval/ollama-foundation-sec-8b-instruct/rulesThe Modelfile adds the chat template that the published GGUF lacks; without it the system prompt
is dropped. Any OpenAI-compatible server works too (key read from OPENAI_API_KEY):
uv run psirt-exposure extract --backend openai --model <name> --base-url http://localhost:8000/v1Drafts are written under eval/, not rules/, so check ignores them until a human reviews
one, sets status: reviewed and moves it into rules/.
eval-extraction runs drafts and hand rules through the same matcher on the sample devices and
writes report.md / report.json next to the drafts.
Evaluation
uv run psirt-exposure eval-golden scores final verdicts on 60 held-out advisory×config pairs
(eval/golden/); see eval/README.md for the metrics table and caveats.
Related Sandbox
Try psirt-exposure on a real IOS XE device with a free DevNet Sandbox, for example an always-on or reservable IOS XE (Catalyst 8000V) or a Cisco Modeling Labs sandbox:
Connect to the device over SSH with the credentials shown on the sandbox page.
Run
terminal length 0, thenshow running-configandshow version.Save the outputs as
~/psirt-lab/<hostname>/running-config.txtand~/psirt-lab/<hostname>/show-version.txt(outside this repository).Run
uv run psirt-exposure check --offline --configs ~/psirt-lab --evidence, or drop--offlineto use live openVuln data.
psirt-exposure only reads the saved text; it never logs in to the device. Sandbox configs contain real addresses and shared credentials, so do not commit them or paste them into issues.
Known issues
Cisco IOS XE only; NX-OS and ASA are not supported yet (the parser and rule interfaces are kept platform-agnostic).
Only advisories with a rule in
rules/get a config-level verdict (10 today). Other openVuln advisories for a device's version are not reported yet.Some facts cannot be read from a config (active L2TP tunnels, UDP 500 reachable, IOx running); those results are
INDETERMINATEand list theshowcommand to run.The golden labels were written by the rule author, so the 60/60 score in eval/README.md is an upper bound. Independent labels are very welcome.
LLM drafts from Foundation-Sec-8B are not good enough to use unreviewed (see the eval report); that is why every rule needs
status: reviewed.No client-side throttling for the openVuln API yet; keep long
watch --intervalruns modest.psirt-exposure never logs in to devices. Treat its output as triage, not a guarantee; confirm with Cisco's advisory and the Cisco Software Checker.
Bugs and wrong verdicts are tracked in GitHub Issues; use the issue templates and attach a minimal fake config, never a real one.
Getting help
Questions, wrong verdicts and feature requests: open a GitHub issue.
Security problems in psirt-exposure: follow SECURITY.md (private reporting, not a public issue).
Getting involved
The most useful contributions right now:
New rules for feature-dependent IOS XE advisories (one YAML file plus tests).
Independent golden labels: devices and expected verdicts written by someone other than the rule author.
NX-OS and ASA support, openVuln client-side throttling, reporting advisories that have no rule.
CONTRIBUTING.md explains the rule workflow and the no-real-data rules. All participants follow the Code of Conduct.
Development checks (the same as CI):
uv sync
uv run ruff check . && uv run ruff format --check . && uv run mypy && uv run pytest
uv run --with pillow python scripts/demo_gif.py # regenerate docs/demo.gifCredits and references
Advisory data: Cisco PSIRT openVuln API and public CSAF documents; exploited-vulnerability data: CISA KEV.
Config parsing: ciscoconfparse2.
Local LLM: Foundation-Sec-8B-Instruct via Ollama.
MCP: the official Model Context Protocol Python SDK.
NetBox lab: trimmed from netbox-docker.
Cisco PSIRT: Security Vulnerability Policy and the Cisco Software Checker.
Licensing info
Copyright 2026 faithere1. Licensed under the Apache License 2.0; see LICENSE and NOTICE (third-party attributions).
psirt-exposure is not affiliated with or endorsed by Cisco Systems, Inc.
Available Tools
4 toolscheckARead-onlyIdempotent
Match advisory rules against device configs; one verdict per device and rule.
Verdicts: AFFECTED, NOT_AFFECTED_FEATURE_DISABLED, FIXED_VERSION, MITIGATED, INDETERMINATE. Each item has the reason, redacted evidence lines, runtime checks to run on the device and CISA KEV entries. devices / rules: names from list_devices / list_rules (default: all). versions: "csaf" (local CSAF files, offline), "openvuln" (live Cisco API) or "none". kev: "none" or "cisa" (download the CISA KEV feed). only_findings: keep only AFFECTED, INDETERMINATE and MITIGATED items.
| Name | Required | Description | Default |
|---|---|---|---|
| kev | No | none | |
| rules | No | ||
| devices | No | ||
| versions | No | csaf | |
| inventory | No | files | |
| netbox_filter | No | ||
| only_findings | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the read-only/idempotent/open-world annotations, the description discloses verdict categories, per-item contents, offline CSAF vs live OpenVuln behavior, CISA KEV feed downloading, and only_findings filtering. These are concrete behavioral traits that materially help an agent invoke the tool correctly.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loads the main action and verdict model before listing parameter notes. The fragment style is efficient, though the inventory/netbox omission means it is concise at the cost of completeness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has 7 parameters, output schema support, and open-world annotations. The description covers the main workflow and outputs, but missing inventory/netbox_filter semantics and prerequisites for live OpenVuln/CISA downloads leave meaningful gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must carry parameter meaning. It explains devices, rules, versions, kev, and only_findings, but omits inventory and netbox_filter entirely, leaving two parameters undocumented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: matching advisory rules against device configs, with one verdict per device/rule. It does not explicitly differentiate itself from siblings such as report, though it does point to list_devices/list_rules for inputs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the purpose and by references to list_devices/list_rules for valid names, plus defaults for devices/rules. However, it does not say when to use check versus report, nor does it state when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_devicesARead-onlyIdempotent
List inventory devices with platform (PID) and IOS XE version.
inventory: "files" (sample config directory) or "netbox". netbox_filter: NetBox device query parameters, e.g. {"site": "psirt-lab"}.
| Name | Required | Description | Default |
|---|---|---|---|
| inventory | No | files | |
| netbox_filter | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, and openWorld, so the safety profile is covered; the description corroborates the external-system aspect with the NetBox query note. It adds little beyond that — no mention of rate limits, pagination, or what happens when netbox_filter matches nothing.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short lines, purpose first, then per-parameter notes — no filler and easily scanned by an agent.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present and annotations covering the safety profile, the description only needed to disambiguate the two inputs, which it does. Minor gap: the interaction between inventory='files' and a supplied netbox_filter is left unexplained.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description carries the load, and it does: it defines the 'inventory' enum values ('files' = sample config directory, 'netbox') and gives a concrete example filter shape for netbox_filter. That meaningfully exceeds the bare schema, though it never states that netbox_filter is ignored or required depending on inventory.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List inventory devices') and even names the fields returned (platform PID, IOS XE version), so the agent knows what it gets. It does not differentiate itself from siblings like check or report, which remain ambiguous by comparison.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains the two inventory sources ('files' as a sample config directory vs 'netbox'), which implies when each mode applies, but it gives no explicit when-to-use-this-vs-alternatives guidance or prerequisite statements relative to check/report.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_rulesARead-onlyIdempotent
List the reviewed advisory rules: id, advisory, CVEs, title and exposure summary.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true and openWorldHint=false, so the safety and determinism profile is covered without prose. The description adds the field set, which is useful orientation, but says nothing about volume, ordering, or whether the list can be empty — gaps that matter for a zero-param list.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with the resource first and the field list after the colon. No filler, no restatement of the name.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description need not document return structure, and a zero-param read tool carries little hidden complexity. What remains unstated — ordering, result volume, empty-list behavior — is minor but non-zero for an agent deciding whether to call this first.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes no parameters, so there is no parameter semantics to explain; the baseline for a parameterless tool applies. The description's field enumeration is technically about return values rather than inputs, but nothing is missing on the input side.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('List the reviewed advisory rules') and enumerates the fields returned (id, advisory, CVEs, title, exposure summary). It never names a sibling or clarifies scope versus list_devices, so differentiation is left to inference, but the purpose itself is unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied by the verb 'List' and the zero-parameter signature; there is no explicit when-to-use, no prerequisite, and no mention of alternatives like check or report. An agent can reasonably guess this is the enumeration entry point for rules, but the description does nothing to route it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
reportBRead-onlyIdempotent
Render a Markdown or HTML report: summary, per-device counts, and findings with evidence and mitigation / remediation config snippets. Arguments as for check.
| Name | Required | Description | Default |
|---|---|---|---|
| kev | No | none | |
| rules | No | ||
| format | No | markdown | |
| devices | No | ||
| versions | No | csaf | |
| inventory | No | files | |
| netbox_filter | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and openWorldHint, so the safety profile is carried. The description usefully discloses the report's section structure, but leaves a material ambiguity: whether rendering returns the document as text or writes a file to disk — a key behavioral fact for an agent deciding whether it can consume the output.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the output shape, with zero filler. The trailing 'Arguments as for check' is tersely efficient, though it is cryptic enough that brevity shades into under-specification.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description needn't describe return values, but for a 7-parameter, zero-coverage tool it should do more than redirect to 'check'. The dependent-argument note is a reasonable shortcut, yet the missing file-vs-text output behavior and any scope caveats leave the definition adequate but incomplete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% across 7 parameters, so the description must compensate and it largely does not. 'Arguments as for check' offloads all semantics to a sibling schema, and opaque parameters such as kev, versions (openvuln/csaf/none) and netbox_filter get no explanation; only the four enum defaults are self-explanatory.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (render) and resource (Markdown or HTML report) and enumerates the report's contents: summary, per-device counts, findings with evidence and remediation snippets. That distinguishes it from a mere scan, though it never explicitly contrasts it with the sibling 'check' beyond deferring its arguments.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The only guidance is 'Arguments as for check', which is a parameter-delegation note, not a when-to-use rule. Nothing tells the agent when to call report instead of check, or whether it is a follow-up step, a standalone scanner, or a superset of check.
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.
4 tool updates
v0.0.1- First observed
check - First observed
list_devices - First observed
list_rules - First observed
report
TDQS
Scored across 4 tools
Each tool has a clearly distinct role: listing advisory rules, listing inventory devices, matching rules to configs, and rendering a report. The check/report pair share arguments, but their outputs and purposes are clearly separated.
Names are all lowercase snake_case and readable, with list_rules/list_devices following a verb_noun pattern. check and report are bare verbs, which is a minor deviation but not confusing.
Four tools are well-scoped for a PSIRT exposure workflow: discover rules, discover devices, analyze exposure, and report findings. Each tool earns its place without redundancy.
The surface covers the core lifecycle from inventory and advisory listing through assessment and reporting. Minor gaps exist, such as no standalone advisory-detail or runtime-check execution tool, but these are not critical for the stated purpose.
Maintenance
Related MCP Connectors
Zero-config MCP security scanner for AI-generated apps. 25K+ vulnerability patterns.
AI-security knowledge as MCP: standards-mapped tools (OWASP, NIST, MITRE) for AI agents.
Security scanner for MCP servers. Detect vulnerabilities, prompt injection, and tool poisoning.
Cited, standards-aware compliance overlay for AI assistants (ISO, NIST, FedRAMP, IRAP), over MCP.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to interact with Cisco IOS-XE network devices over SSH using structured tools. Provides read and write capabilities for network management with built-in validation and security.-
- AlicenseNot gradedqualityDmaintenanceMCP server for network operations that lets AI assistants interact with Cisco/Juniper network devices through safe, well-defined tools like compliance audits and configuration backups.MIT
- AlicenseNot gradedqualityDmaintenanceA production-quality MCP server for querying Cisco security advisories via the PSIRT OpenVuln API v2, enabling LLM-powered interfaces to search, filter, and analyze Cisco vulnerability data.1Apache 2.0
- AlicenseNot gradedqualityFmaintenanceMCP server for Cisco Catalyst SD-WAN Manager (vManage) that exposes REST API as tools for AI assistants to query and manage SD-WAN fabric, including device management, monitoring, templates, and policies.9MIT