Draugr
OfficialThe Draugr MCP server lets you describe an app in a Saga descriptor, run multi-scanner security scans, and get findings ranked by real-world priority with concrete fixes.
Write and validate descriptors — fetch the Saga JSON Schema (
get_saga_schema), see available controls and scanners (list_controls), generate one automatically from a directory (propose_saga), or discover it from live systems like Kubernetes namespaces and GitHub orgs (survey,list_surveyors).Check readiness — see which external scanners are installed and what's missing (
check_tools), and whether KEV/EPSS/Go vuln feeds are cached or stale (feeds_status).Scan — run enabled controls over declared components and get a pass/fail verdict with priority-ranked findings, suppressed decisions, and what wasn't examined (
scan).Reuse results — read a previous SARIF report and get findings ranked by priority without rescanning (
summarize_report).Compare revisions — diff two reports to see what a change introduced or resolved, and whether a PR gate would fail (
diff_reports).Plan the work — turn a report into prioritized action lists by kind: bump, code, infrastructure, credential, wait, decide (
fix_list).Understand findings — pull a rule's full description and published remediation from the report (
explain_rule).
Verdicts are scoped: they reflect only the controls and components declared, not a full security assessment.
Enables scanning of GitHub repositories, discovery of repositories in an organization, and uploading scan results to GitHub Code Scanning.
Provides a GitHub Action to run Draugr scans in CI, with automated tool provisioning and SARIF upload to code scanning.
Allows discovery of container images from Kubernetes clusters and scanning them for vulnerabilities.
Integrates Trivy as a scanner for container images, filesystem dependencies, and infrastructure-as-code configurations.
Draugr
Know which security findings to fix first, across the scanners you already run. SAST, SCA, secrets, IaC and containers, every finding ranked by exposure, criticality, exploitability and, for Go, reachability.
Describe your app in one file, and Draugr hands back the findings in the order to fix them.
Every application carries problems nobody put there on purpose: a library that turned out to have a hole in it, a password committed by accident, a server setting that leaves a door open. Draugr finds them, works out which ones actually matter for your app, and answers the question you are really asking before a release, is this safe to ship?
It runs the established open-source scanners for you, Trivy, Semgrep, Gitleaks and others, so there is nothing to choose between, wire up, or read five of. You describe what you built, once, in one file: where the repositories are, what images it builds, what it exposes, what infrastructure it runs on. Draugr picks the checks that apply, runs the right tool for each, and produces evidence you can hand to somebody else. Bring the scanners you already pay for, or use the open-source defaults.
Findings are ranked, not listed. A scanner's "critical" describes a flaw in the abstract,
how bad it could be at its worst, anywhere. The same flaw is act-now in the service strangers can
reach and backlog in the internal tool three people use, and no scanner can tell those apart because
the difference is in the file you wrote, not in the code. And draugr diff
gates a pull request on new findings only, so inheriting two hundred existing ones does not
block every change.
Quickstart · See it in action · What it checks · In your pipeline · Documentation · What Draugr doesn't promise · Security
See it in action
Priority (P1–P4) is not severity. Severity says how bad a flaw is at its worst, anywhere. Priority weighs that against how exposed and how important the part of your app it sits in is, which no scanner can work out, because it is not in the code.
draugr-dev/draugr-demo is a deliberately vulnerable app wired to Draugr: every control lights up, findings land in the repo's Security → Code scanning tab, and its example pull requests show the new-vs-fixed diff.
Related MCP server: GitHub MCP Server
Quickstart
curl -fsSL https://draugr.dev/install.sh | shInstalls to ~/.local/bin, no sudo, and installs nothing if the archive fails
verification. The script is readable in the
repo; a download by hand, the GitHub CLI and go install are in the install
guide.
draugr init # describe this project in a draugr.saga.yaml
draugr tools install # fetch the scanners it needs, pinned and verified
draugr scan # scan what the descriptor describestools install takes its answer from the descriptor beside it, so a small service gets three
scanners rather than every one Draugr can provision. --all when you are preparing a machine for
several projects.
Then describe what you actually ship:
project: my-app
release:
version: "1.0"
config:
controls:
images:
enabled: true
components:
- name: web
images:
- image: alpine:3.19draugr scan draugr.saga.yaml # console summary; exits non-zero on fail
draugr scan draugr.saga.yaml -o out/ # also writes report.json + results.sarif
draugr scan draugr.saga.yaml --format markdown # or html, junit, json, sarifYour editor already knows this file. Draugr's
JSON Schema is registered with
SchemaStore, so any *.saga.yaml gets completion, hover docs and
typo warnings on open with nothing to configure.
Or let discovery write the descriptor for you:
draugr survey github repos --org my-org -o draugr.saga.yaml
draugr survey k8s images --namespace prod -o draugr.saga.yamlFull walkthrough: quickstart.
Setting it up on a real repository? Reply to the pinned discussion with what the repository is and which scanners you run, and we'll write the descriptor with you, including the exposure and criticality that take judgment.
What it checks
Each control is backed by a tool Draugr executes rather than bundles, so every scanner stays under its own license, and you can swap it.
Control | Looks for | By default |
| known flaws in the libraries you depend on | Trivy, Grype and Mend opt-in |
| passwords and keys committed by accident, history included | Gitleaks |
| patterns in the code you wrote that let somebody in | Semgrep, gosec opt-in for Go |
| settings that leave a door open, in Terraform, Kubernetes and Dockerfiles | Trivy |
| what is baked into your container images | Trivy, Grype opt-in |
| terms attached to code you did not write | Trivy |
| problems only visible from outside a running app | Nuclei, authenticated, and from an OpenAPI spec |
| how your site answers a browser | native |
| certificates and encryption | native |
| your Kubernetes cluster, against the CIS benchmarks | native, kube-bench opt-in |
| your cloud account as it is, against the CIS benchmarks | Prowler |
| whether anything you talk to is on a public blocklist | abuse.ch URLhaus |
| whether an image is signed by the builder you expect | Sigstore cosign |
Every scanner, what it sends and whose terms it carries: integrations catalog.
Alongside them: content-hash caching, an SBOM per repository and image, KEV/EPSS enrichment, per-control gate thresholds, and suppressions that stay in the report with the reason someone gave rather than disappearing.
In your pipeline
The first-party GitHub Action installs Draugr, and with tools: true the scanners the descriptor
needs. On a pull request it compares the head with the base, posts the findings the change adds and
fixes as one comment, and fails on a new P1 finding. On a push to main it runs the full scan,
fails on any P1 finding, and sends every finding to the Security tab as one Draugr tool.
name: Security
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
pull-requests: write # the pull-request comment
security-events: write # the Security tab, on a push
jobs:
draugr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # the comparison needs the base commit
- id: draugr
uses: draugr-dev/draugr@v0 # pin @vX.Y.Z for reproducible CI
with:
saga: draugr.saga.yaml
tools: true # provision the scanners the controls need
fail-on-new: P1 # fail a pull request on a new P1 finding
- if: always() && github.event_name == 'push' # even when the gate fails
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ${{ steps.draugr.outputs.sarif }}Save it as .github/workflows/draugr.yml and commit it with the draugr.saga.yaml that
draugr init wrote. The upload needs code scanning, which public repositories have and private ones
get with GitHub Advanced Security; without it, delete the last step. To block a merge on a failing
run, make the draugr check a required status
check
in the branch's protection rule or ruleset.
GitHub Actions · Code scanning: findings in the Security tab · GitLab: an include, GitLab's own report formats, a sticky merge-request comment · Azure Pipelines: a step template
From an AI coding assistant. Ask one to check a change and it will, using whatever scanner it
finds over a scope it chose. draugr mcp serves Draugr over the Model Context
Protocol so it reads your committed descriptor instead. A scan
that does more than read the checkout, such as probing a live host or sending data to a third
party, asks you first.
claude mcp add draugr -- draugr mcpSee use Draugr from an AI coding assistant.
Documentation
Quickstart: install, first scan, first survey, CI
Concepts: the descriptor, controls, scanners, the verdict
Saga schema · CLI reference: every field, every flag
Integrations catalog: every scanner, with licenses and terms
What Draugr doesn't promise
A passing verdict means the controls you configured found nothing they were looking for. It is not a statement that your software is secure. It is silent about anything your descriptor does not declare, controls you did not enable, and whatever the underlying scanners miss. License findings are information, not legal advice. Draugr is provided under Apache-2.0 without warranty.
The details, including whose terms the scanners carry and your responsibility for authorization when scanning live endpoints: scope and disclaimer.
Security & supply chain
A security tool should hold itself to what it checks. Draugr does:
Standard output. Every finding is normalized to SARIF 2.1.0 (OASIS), so results flow into GitHub / GitLab / Azure DevOps code scanning and any SARIF-aware tool.
Signed releases and provenance. Release archives'
checksums.txtis keyless-signed with cosign (Sigstore) into achecksums.txt.sigstore.jsonbundle, and each release publishes SLSA build-provenance attestations (gh attestation verify …); verify before installing (recipe).SBOMs. A Syft SBOM is published for every release archive.
Verified tooling.
draugr tools installfetches scanners pinned by SHA-256 and, where the upstream signs them, verifies the cosign signature too, and cosign itself is installable, so verification is self-sufficient.We scan ourselves. Draugr runs its own scan on every pull request to this repository, static analysis included (Semgrep and gosec through the
sastcontrol, and gosec again ingolangci-lint), and the findings are in the repository's Security tab. Supply-chain posture is tracked on the OpenSSF Scorecard.Report a vulnerability. See SECURITY.md.
Development
Requires Go 1.26+. make build builds ./bin/draugr; make gate runs the full local gate (fmt,
vet, lint, race tests with coverage and govulncheck). See CONTRIBUTING.md.
License
Draugr is licensed under the Apache License 2.0.
Available Tools
13 toolscheck_toolsA
Report which external scanners Draugr can find on this machine, and what to run if any are missing. Pass the path of the Saga descriptor to check only the tools a scan of it runs, the ones draugr doctor requires; without a path, every tool Draugr knows about is checked. Call this when a scan fails or before suggesting one: a control whose scanner is absent cannot run, and Draugr reports that as a failure rather than a pass. This only looks, it will not install anything.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | path to the Saga descriptor about to be scanned; without it, every tool Draugr knows about is checked, including ones this project never runs |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | Yes | |
| ready | Yes | true when nothing required is missing |
| tools | Yes | |
| remedy | No | |
| missing | No | required tools that are absent |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must define the safety profile. It does: "This only looks, it will not install anything" establishes a non-mutating, read-only operation. It also explains a non-obvious behavior, that missing scanners are reported as failures rather than skips, though it says nothing about how detection is performed or how costly it is.
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 dense and front-loaded, stating purpose first, then parameter modes, then usage triggers, and finally the read-only guarantee. The clause "the ones `draugr doctor` requires" is slightly awkward, but every sentence still contributes useful information.
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?
An output schema is present, so the return format does not need to be explained. For a single optional parameter, the description covers both invocation modes and the failure semantics that motivate calling the tool. The only gap is detection mechanics and expected latency.
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 100%, so the single parameter is already documented. The description still adds meaning: with a path, only the tools a scan of that descriptor runs are checked; without a path, everything Draugr knows about, including tools this project never runs, is checked.
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 first sentence gives a specific verb and resource: report which external scanners Draugr can find on this machine. It also distinguishes the tool from siblings like `scan` and `validate_saga`, because the point is detection only, with no installation and no scanning.
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 tells the agent exactly when to call this tool: when a scan fails, or before suggesting a scan. It also gives a supporting reason: a control whose scanner is absent is reported as a failure rather than a pass. It never names a concrete alternative such as `scan` or `validate_saga`, so it stops short of full when/when-not routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
diff_reportsA
Compare two scans and report what a change introduced and what it resolved. Ask this rather than summarize_report when the question is whether a change made things worse: a project with inherited findings answers 'what is wrong' the same way before and after, which says nothing about the change. Give failOnNew to learn whether a pull request gate would fail.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | maximum new findings to return; defaults to 20 | |
| basePath | Yes | path to the results.sarif from the base revision | |
| headPath | Yes | path to the results.sarif from the revision being proposed | |
| failOnNew | No | report whether a new finding at or above this would fail a gate, in either vocabulary: a priority band (P1-P4), which folds in the component's declared exposure and criticality, or a severity (critical, high, medium, low) |
Output Schema
| Name | Required | Description |
|---|---|---|
| new | No | |
| note | No | |
| fixed | No | |
| newCount | Yes | findings present in head and absent from base |
| wouldFail | No | true when a new finding meets failOnNew; only meaningful when failOnNew was given |
| fixedCount | Yes | findings present in base and absent from head |
| movedCount | No | findings in both scans under a different component, as splitting or renaming a component reports them; neither new nor fixed, and no gate reads them |
| gateApplied | No | the threshold wouldFail was computed against |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the safety and behavior burden. It clearly describes comparison and reporting behavior and adds useful nuance about inherited findings and PR gate evaluation, though it does not explicitly state read-only status or side-effect profile beyond the inherent diff semantics.
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 sentences, front-loaded with the core action and immediately followed by the sibling-routing condition and a parameter use case. Every sentence contributes specific guidance with no filler.
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 is a report-diff operation with an output schema and fully described inputs, so return values and parameter formats do not need to be repeated. The description supplies the selection rationale, behavioral nuance, and gate-evaluation purpose an agent needs to invoke it correctly.
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 100%, so the baseline is 3 and the schema already documents basePath, headPath, limit, and failOnNew. The description adds meaningful intent for failOnNew by tying it to whether a pull request gate would fail, exceeding the schema's parameter definition.
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: compare two scans and report introduced versus resolved findings. It also explicitly distinguishes the tool from summarize_report by naming the question it answers better, so an agent can select it without opening the schema.
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?
Gives a clear when-to-use rule: use this rather than summarize_report when the question is whether a change made things worse. It explains the inherited-findings failure mode and adds a concrete use case for failOnNew, leaving little ambiguity about routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
explain_ruleA
Explain what a finding means and how to fix it, from the scan's own report. Returns the check in full and the remediation the scanner published. Prefer this over fetching a rule's help URI: the answer is already on disk, and for a benchmark that URI is often a registration form in front of a PDF.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | path to a results.sarif produced by draugr scan | |
| ruleId | Yes | the rule to explain, in full or by the part that is unambiguous: 4.3.1 finds kube-bench/cis/4.3.1 |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | |
| ruleId | Yes | |
| foundIn | No | where this rule fired, capped |
| helpUri | No | |
| description | No | what the check is, in full rather than the truncated line a report shows |
| remediation | No | how to fix it, as the scanner published it |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral burden and does well: it discloses that the answer comes from the scan report on disk, what is returned, and why the alternative help URI is often worse. It does not explicitly state read-only safety or error behavior, but the read-from-disk framing strongly implies a non-mutating operation.
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 three tightly written sentences. It front-loads the core purpose, then return behavior, then usage guidance, with no redundant or filler content.
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?
For a two-parameter tool with full schema coverage and an output schema, the description provides everything needed: what it does, what it returns, where the data comes from, and when to prefer it over the help URI alternative.
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 100%, and both parameters already have clear schema descriptions, including an example of partial ruleId matching. The tool description reinforces the report source but does not add new syntax or format details beyond the schema, so the baseline of 3 is appropriate.
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 uses the specific verb 'explain' with the resource 'rule/finding' and scopes it to the scan's own report. It also states that it returns the full check and scanner-published remediation, which clearly distinguishes it from sibling reporting and listing tools.
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?
It explicitly says to prefer this tool over fetching a rule's help URI and explains why: the answer is already on disk and the URI may lead to a registration form or PDF. This gives a clear usage decision, not just implied context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
feeds_statusA
Report whether each dataset a scan reads from the local feed cache (CISA's KEV catalog, FIRST's EPSS scores, the Go vulnerability database) is cached, when it was fetched, and whether it is stale. Call this when a report marks a feed stale, when govulncheck fails offline, or before relying on exploitability ranking. It only reads, and when a feed is missing or stale it returns the draugr feeds update command for the user to run.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| dir | Yes | the feed cache directory |
| next | No | the command that fetches every missing or stale feed; empty when each feed is cached and current |
| note | Yes | |
| feeds | Yes | |
| stale | No | feeds whose cached copy is older than maxAge |
| maxAge | Yes | the age past which a feed is reported stale, and a scan's limit when config.exploitability.maxAge is unset |
| missing | No | feeds with no cached copy |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and does so: it declares read-only behavior ('It only reads') and discloses the remediation path – returning a `draugr feeds update` command rather than mutating state itself. It stops short of noting cost/latency or cache-location details, but for a zero-param status read it covers the safety-relevant behavior.
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 sentences, front-loaded with what is reported, then when to call it, then the behavioral note. Every sentence adds distinct information with no repetition of the name or padding.
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?
An output schema exists, so return values need not be explained here. Combined with explicit triggers and the read-only/remediation disclosure, an agent has everything needed to decide to call it and to interpret the result.
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?
Zero parameters, so the baseline is 4; there is nothing for the description to disambiguate. The text correctly implies a no-argument, whole-cache check by saying 'each dataset a scan reads from'.
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 (report cache status per dataset) and enumerates the exact feeds involved (CISA KEV, FIRST EPSS, Go vuln DB). No sibling tool covers this ground, so the agent can route to it unambiguously.
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?
Gives three explicit trigger conditions: a report marking a feed stale, govulncheck failing offline, and before relying on exploitability ranking. That is when-to-use guidance with concrete conditions rather than vague implication.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fix_listA
Turn a report into the things to do about it, one list per kind of work (bump a dependency, change the code, change the infrastructure, rotate a credential, wait for a release, decide), each most urgent first, each action saying how many findings it clears and what it ranks by. Every action is returned unless limit is set. Name the head of each list that applies: the lists are in a fixed order of kind, only priority compares an action in one list with an action in another, and which list to work from is the reader's choice. Name every action tied with the head of a list, not only the first. Actions tie when they have the same priority, exploitation, severity and number of findings cleared, and their order among themselves carries no meaning. Ask this rather than summarize_report when the question is what to change: one upgrade usually clears many findings, and an image somebody else publishes is one action however many packages are wrong inside it.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | only the list for one kind of work: bump (upgrade a dependency), code (change the application's code or its server's configuration), infrastructure (change infrastructure code or a live setting), credential (remove and rotate a committed secret), wait (for somebody else's release) or decide (accept or replace, e.g. a license). Omit for every list | |
| path | Yes | path to a results.sarif produced by draugr scan | |
| limit | No | maximum actions per list; every action when omitted |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | |
| lists | Yes | one list per kind of work, ordered bump, code, infrastructure, credential, wait, decide; a kind with no action has no list |
| clears | Yes | findings the returned actions clear between them, each counted once |
| totalClears | Yes | findings the actions in every list clear between them, each counted once |
| totalActions | Yes | actions in every list, including any a limit left out |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses that lists come back in a fixed order of kind, that only priority is comparable across lists, that actions tie on priority/exploitation/severity/findings-cleared with meaningless intra-tie order, and that every action is returned unless limit is set. It stops short of saying whether the call reads only or has side effects, but the framing as report analysis makes the read-only nature evident.
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 purpose is front-loaded, but the prose is dense and run-on, with clauses like "each action saying how many findings it clears and what it ranks by" and the tie-breaking sentence stacking four conditions. The second half drifts between output semantics and tool-selection advice, which could be split for faster scanning.
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?
An output schema exists, so return shape needn't be described, and the description still covers ordering, cross-list comparability, tie semantics, and filtering behavior. Combined with the fully documented three-parameter schema, an agent has nearly everything needed; only an explicit statement of read-only behavior is missing.
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 100% and the kind enum and limit default are already documented in the schema, so the description adds little parameter-specific meaning beyond restating that limit bounds results. The one useful addition is that omitting kind yields every list, which the schema also implies. Baseline 3 is appropriate.
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?
"Turn a report into the things to do about it, one list per kind of work" states a specific transformation plus the grouping, and the parenthetical enumerates the kinds (bump, code, infrastructure, credential, wait, decide). It also explicitly names the sibling it is not (summarize_report), so an agent can separate the two without reading either schema.
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?
"Ask this rather than summarize_report when the question is what to change" gives an explicit alternative and the condition that selects it, reinforced by the rationale that one upgrade clears many findings and a shared image is one action. It also notes "which list to work from is the reader's choice", so no implicit workflow is assumed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_saga_schemaA
Return the JSON Schema for the Saga descriptor (*.saga.yaml) that this build of Draugr enforces. Use it to write or correct a descriptor rather than guessing at field names, the schema is the authority, and it rejects unknown keys.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| hint | Yes | |
| schema | Yes | the JSON Schema for a Saga descriptor |
| draugrVersion | Yes | the Draugr build this schema came from |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and does disclose meaningful traits: the schema is pinned to the running build, unknown keys are rejected, and the schema is authoritative over guesses. It doesn't state that the call is side-effect free or versioned, but the 'Return' verb plus output schema largely covers that.
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 with the core purpose front-loaded and no filler. The second sentence is a comma splice ('...field names, the schema is the authority...') that slightly muddles the strictness point, but the content earns its place.
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?
For a zero-parameter read tool with an output schema, the description supplies what structured fields can't: the versioned nature of the schema and its strictness. A note on how it relates to validate_saga would close the last gap.
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?
Zero parameters, so the baseline of 4 applies. The description correctly adds nothing about parameters and instead spends its text on what the returned document governs.
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: 'Return the JSON Schema for the Saga descriptor (*.saga.yaml)'. It also scopes the schema to 'this build of Draugr', which distinguishes it from validate_saga and propose_saga in the sibling list.
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?
'Use it to write or correct a descriptor rather than guessing at field names' gives a clear triggering context and names the anti-pattern it replaces. It stops short of naming sibling alternatives such as validate_saga, which an agent might reasonably confuse it with.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_controlsA
List the security controls Draugr can run: what each one checks, which scanner backs it, and whether it applies per component or to the whole project. Call this before writing or editing a Saga descriptor, so the controls you enable are ones that exist.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| hint | Yes | |
| controls | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry the behavioral burden. 'List' implies a read-only operation and the description discloses the kind of information returned, but it does not explicitly state side effects, auth requirements, or rate-limit behavior. For a simple zero-parameter list tool this is adequate, but not rich.
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 two tightly written sentences with no wasted words. Purpose and return content are front-loaded, followed immediately by the key usage instruction. It is appropriately sized for a simple list tool.
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 takes no parameters and has an output schema, so the description does not need to enumerate return fields or parameter details. It covers what the tool lists, what the agent will learn, and when to call it. Nothing critical is missing for correct invocation.
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?
This tool has zero parameters, so parameter semantics are inherently minimal. The description correctly avoids introducing nonexistent inputs. Baseline for zero parameters is 4.
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 gives a specific verb and resource: list the security controls Draugr can run. It also previews the returned fields, which helps an agent understand what kind of controls these are. Sibling differentiation is only implicit through the word 'controls' and the Saga workflow context, so it falls short of a perfect 5.
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?
It explicitly says to call this before writing or editing a Saga descriptor and explains the reason: so enabled controls are ones that exist. That gives clear usage context. It does not name when-not-to-use conditions or specific alternative tools, so it is not a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_surveyorsA
Name the surveyors this build has and what each discovers. Call it before survey, so the surveyor you ask for is one that exists.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| hint | Yes | |
| surveyors | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full disclosure burden and it does deliver the essentials: the return content (names plus what each discovers) and a hard prerequisite relative to survey. It stops short of stating that the call is side-effect free or cheap to repeat, which for a zero-parameter list is a minor residual gap rather than a risk.
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 short sentences, zero filler, with the enumeration purpose front-loaded and the sequencing advice second. Every clause carries distinct information.
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?
For a no-argument, read-style enumeration tool with an output schema covering the return shape, the description supplies the only things the structured fields cannot: what is listed and when to call it relative to survey. Nothing an agent needs in order to invoke it correctly is missing.
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?
Zero parameters, so there is nothing to disambiguate and the baseline applies. The description correctly adds no pseudo-parameter detail, though it also doesn't note that the scope is implicitly "this build" as opposed to all builds.
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 ("Name") and resource ("the surveyors this build has") plus the payload ("what each discovers"). This is immediately distinguishable from the survey sibling, which performs the survey rather than enumerating available surveyors.
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?
Explicitly prescribes ordering ("Call it before survey") and gives the reason ("so the surveyor you ask for is one that exists"). It names the alternative tool and the condition that should route the agent here first, leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
propose_sagaA
Return the Saga descriptor draugr init would write for a directory, as YAML, with the controls its dependency files, infrastructure code and Dockerfiles call for. Call this when a project has no descriptor, before writing one from get_saga_schema. It writes nothing; show the proposal to the user, who decides whether and where to save it.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | the directory to propose a descriptor for, absolute or relative to the directory the server was started in; defaults to that directory | |
| perDirectory | No | one component per directory that holds its own dependency file, and a root component for the rest of the tree |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | |
| path | Yes | the file to save the descriptor as; its repository URLs are relative to this directory |
| saga | Yes | the descriptor draugr init would write for this directory, as YAML, with a comment naming the files behind each scanner it enables |
| warning | No | |
| controls | No | |
| existing | No | descriptors already in this directory; the project's committed scope, which this proposal does not replace |
| components | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral burden and clearly states that the tool writes nothing and only returns a proposal. It also explains the intended user-review workflow, though it does not detail file-read permissions or failure behavior.
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 sentences, front-loaded with the core purpose and followed by usage and safety context. Every sentence contributes distinct information with no repetition.
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 description is complete for this tool given the rich input schema and the presence of an output schema. It covers purpose, usage condition, side-effect profile, and user workflow without needing to restate return structure.
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 100%, so the schema already documents both parameters, including `path` and `perDirectory`. The description adds only general context about proposing for a directory and does not add parameter-specific meaning beyond the schema.
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: return the Saga descriptor `draugr init` would write for a directory, as YAML. It distinguishes this from writing a descriptor and names the sibling tool `get_saga_schema`, so the agent can tell what this tool is and is not.
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?
It gives an explicit usage condition: call this when a project has no descriptor, before writing one from `get_saga_schema`. It also tells the agent what to do with the result: show the proposal to the user, who decides whether and where to save it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
scanA
Run a scan for a Saga descriptor and return the verdict with findings ranked by priority. This is expensive and has side effects: it clones repositories, executes external scanners and reaches the network. Prefer summarize_report when a recent report already exists. The scan merges draugr.config.yaml from the server's working directory, as draugr scan does.
Scope: this runs the controls the descriptor declares over the components it declares, and nothing else. It is a reproducible floor, not a substitute for reading the code. It does not reason about trust boundaries, whether a build context carries secrets into an image, how credentials are passed to subprocesses, protocol assumptions, or anything a control was not enabled for. If the question was whether a repository is safe to ship rather than whether it passes this gate, keep looking after this returns, the findings here are the part that can be checked the same way every time, not the whole answer. A scan whose scanners do more than read, such as probing a live host or sending data to a third party, or whose results are delivered off this machine, asks the user to approve it first.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | path to the Saga descriptor to scan | |
| limit | No | maximum findings to return; defaults to 20 | |
| minPriority | No | only return findings at this priority or above: p1, p2, p3 or p4 |
Output Schema
| Name | Required | Description |
|---|---|---|
| next | Yes | the first thing to do: of the first actions at or above minPriority in fix_list's lists for this report, the one with the highest priority, the earlier list on a tie; the action's title is quoted |
| note | No | |
| feeds | No | exploitability datasets the scan consulted; absent means no finding was checked against KEV or EPSS |
| total | Yes | findings the report judged, before any priority filter; excludes suppressed |
| counts | Yes | |
| unread | No | dependency files no scanner took packages from, Terraform files calling a module no scanner loaded, files the secrets scanner skipped, and source files Semgrep skipped or did not finish; what they declare or hold was not checked |
| verdict | Yes | pass or fail, by the same policy the CI gate applies |
| accepted | No | the suppressed findings with why, until when and where each decision is written, most severe first, capped at limit; report each as a decision, never as work to fix |
| controls | Yes | the controls this scan ran; nothing outside them was examined |
| findings | Yes | |
| returned | Yes | |
| delivered | No | where this run's reports were delivered, from the descriptor's config.publishers |
| uncovered | No | surfaces this descriptor declares that no enabled control examined |
| suppressed | No | findings a recorded decision took out of the ranking; not part of total |
| unexamined | Yes | what a Draugr scan does not examine, whatever the verdict |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations supplied, the description carries the full burden and does so thoroughly: it discloses cost ('expensive'), concrete side effects (clones repositories, executes external scanners, reaches the network), an implicit config merge from the server's working directory, and a human-approval gate for scanners that probe live hosts or exfiltrate results. That is exactly the behavioral context an agent needs before invoking a side-effecting tool.
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 core purpose, cost and alternative are front-loaded well, but the middle section is a long run-on ('If the question was whether a repository is safe to ship rather than whether it passes this gate, keep looking after this returns, the findings here are the part that can be checked...') that restates the earlier 'not a substitute for reading the code' point. The description would be stronger with that redundancy cut.
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?
An output schema exists, so return-value explanation is unnecessary, and the description instead spends its length on the things structured fields cannot express: scope limits, side effects, and when results are not the whole answer. Nothing an agent needs to decide whether and how to call this is missing.
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 100%, so path, limit and minPriority are already documented in the schema; the description adds no syntax, defaults, or interaction detail for them. The one parameter-adjacent fact it adds, the draugr.config.yaml merge from the working directory, is not an input parameter at all.
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 ('Run a scan for a Saga descriptor') plus the return shape ('verdict with findings ranked by priority'). It also distinguishes itself from the sibling summarize_report by naming it as the cheaper alternative when a recent report exists.
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?
Explicit when-to-use routing ('Prefer summarize_report when a recent report already exists'), explicit scope boundary ('runs the controls the descriptor declares... and nothing else'), and explicit exclusions of what the scan will not answer (trust boundaries, secrets in build context, credential passing, protocol assumptions). The approval prerequisite for side-effecting scanners is also stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
summarize_reportA
Read the results.sarif an earlier draugr scan wrote and return its findings ranked by priority, deduplicated, with the rule documentation link for each. Answers 'what should I fix first?' from a scan that already ran, without starting a new one. A finding the project accepted comes back in accepted, with why, until when and the file and line that record the decision; report it as a decision and do not propose fixing it. The git history of a descriptor rule's line records who wrote it, and report.json carries that history in descriptor.exclusions[].gitHistory. It covers the controls that scan ran and nothing else, so treat it as a floor to build on rather than a complete account of a codebase's security. Given a report.json, read the results.sarif in the same directory, since report.json holds the verdict and the fix list and names each finding only by its fingerprint.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | path to the results.sarif draugr scan wrote; report.json beside it holds the verdict and the fix list, and names each finding only by its fingerprint | |
| limit | No | maximum findings to return; defaults to 20 | |
| minPriority | No | only return findings at this priority or above: p1, p2, p3 or p4 |
Output Schema
| Name | Required | Description |
|---|---|---|
| next | Yes | the first thing to do: of the first actions at or above minPriority in fix_list's lists for this report, the one with the highest priority, the earlier list on a tie; the action's title is quoted |
| note | No | |
| feeds | No | exploitability datasets the scan consulted; absent means no finding was checked against KEV or EPSS |
| total | Yes | findings the report judged, before any priority filter; excludes suppressed |
| counts | Yes | |
| accepted | No | the suppressed findings with why, until when and where each decision is written, most severe first, capped at limit; report each as a decision, never as work to fix |
| findings | Yes | |
| returned | Yes | |
| suppressed | No | findings a recorded decision took out of the ranking; not part of total |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does real work: it discloses deduplication, priority ranking, the presence of documentation links, and the important caveat that coverage is a 'floor' limited to what the scan ran. It also explains how to treat accepted findings ('report it as a decision and do not propose fixing it'). It stops short of stating the read-only/non-mutating guarantee outright or what happens when the named file is absent.
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 core purpose and the usage caveat are front-loaded well, but the middle sentences drift into return-value documentation ('descriptor.exclusions[].gitHistory', fingerprint naming) that the output schema already covers. The git-history sentence is dense and tangential to invoking the tool, and the report.json/sarif sentence duplicates the path parameter description.
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?
Output shape is covered by an output schema, so the description only needed purpose, routing, and caveats — all present, including the scope limitation and accepted-finding handling. Missing only an explicit statement that the operation is read-only and what surfaces if the SARIF file is missing or malformed.
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 100%, so path, limit and minPriority are already documented, including the report.json-beside-it relationship that the description repeats almost verbatim. The description adds no new syntax, default, or interaction detail (for example how minPriority interacts with the returned ranking) beyond the schema. Baseline 3 applies when the schema does the heavy lifting.
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 opening sentence gives a specific verb and resource ('Read the results.sarif an earlier draugr scan wrote') plus the output shape (ranked, deduplicated, with rule documentation links). The clause 'without starting a new one' cleanly separates it from the sibling 'scan' tool, and 'what should I fix first?' pins the intent.
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?
It states the use case clearly ('from a scan that already ran') and rules out the obvious alternative (starting a new scan), and it tells the agent to pass report.json's sibling results.sarif. It does not reference other plausible siblings such as fix_list, diff_reports or explain_rule, so the routing guidance stops one step short of complete.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
surveyA
Discover what an application is made of, the images running in a Kubernetes namespace, the repositories in an organization, and return a Saga descriptor for it. Prefer this over writing a descriptor from the schema: which namespaces exist and which images are actually running, at which digest, is not something to guess at. It reads a live system with credentials this machine already has, and returns YAML rather than writing a file.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | project name for the descriptor | |
| surveys | Yes | the surveyors to run; results merge into one descriptor | |
| version | No | release version for the descriptor; omitted when empty |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | |
| saga | Yes | the descriptor this survey produced, as YAML |
| warning | No | |
| controls | No | |
| components | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses that the tool reads a live system using credentials already present on the machine, and that it returns YAML rather than writing a file, establishing a read-only, non-destructive profile. It stops short of stating failure behavior, credential prerequisites, or latency for live API calls.
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 sentences, front-loaded with the purpose, followed by the selection rationale and the operational profile. No filler; every sentence adds decision-relevant information.
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?
An output schema exists so return values needn't be detailed, and all three parameters are documented. Combined with the safety and credential disclosure, an agent has nearly everything needed; only credential setup and error behavior are unaddressed.
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 100%, including per-field notes for ref, surveyor, name, and version, so the baseline is 3. The description adds the merge semantic implicitly (multiple surveys combine into one descriptor) but nothing about parameter syntax or format beyond what the schema already states.
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 names a specific verb and outcome — discover the real composition of a system (namespaces, images and digests, repos) and return a Saga descriptor. It is clearly distinguishable from siblings like list_surveyors, propose_saga, and get_saga_schema, since it produces a descriptor from live observation rather than from a provided schema.
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?
"Prefer this over writing a descriptor from the schema" gives an explicit alternative and the reason to choose this tool (real namespaces, images, and digests cannot be guessed). It lacks an explicit when-not or the prerequisite step of calling list_surveyors first to learn surveyor names, which is only implied via the schema.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
validate_sagaA
Check a Saga descriptor against the schema and report what's wrong. Give either a path on disk or the YAML content directly. Validating is free and has no side effects, so prefer it over assuming an edit was correct.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | path to a Saga descriptor on disk | |
| content | No | the descriptor's YAML content, for validating an edit before writing it |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | |
| valid | Yes | |
| controls | No | |
| components | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so well: 'free and has no side effects' explicitly declares a read-only, safe operation, and 'report what's wrong' sets expectations for the result. It stops short of describing granularity of findings, but an output schema exists to cover the return shape.
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 tight sentences with zero filler. The core action is front-loaded, followed by input options and then the usage recommendation in priority order.
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?
For a two-optional-param tool with full schema coverage and an output schema, the description covers purpose, input alternatives, and safety profile — enough to invoke correctly. Only marginal detail, such as what happens if both inputs are supplied, is left unaddressed.
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 100%, establishing a baseline of 3. The description adds value by framing path and content as mutually exclusive alternatives ('give either... or...'), resolving the ambiguity of both being optional; the content parameter's pre-write use case is reinforced in prose.
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 pair: check a Saga descriptor against the schema and report what's wrong. This is clearly distinguishable from siblings like get_saga_schema (retrieves the schema) or explain_rule (explains rules), though it never names an alternative explicitly.
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?
Explains the two input modes (path on disk vs. YAML content) and gives a clear usage directive: 'prefer it over assuming an edit was correct.' That establishes when to call it, but there is no explicit when-not condition or named alternative for edge cases.
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.
3 tool updates
v0.166.0- Changed
fix_list2 fields changed- changed
Output schema / properties / lists / items / properties / actions / items / properties / exploitation / descriptionPrevious value: -"the strongest exploitation evidence among the findings it clears, from the escalation of one of them; absent where KEV and EPSS raised none"New value: +"the strongest exploitation evidence among the findings it clears: a finding on KEV, whether or not KEV raised it, else one EPSS raised; absent where none is on KEV and EPSS raised none" - changed
Output schema / properties / lists / items / properties / actions / items / properties / exploitation / properties / ruleId / descriptionPrevious value: -"the rule of the finding the signal raised, a CVE"New value: +"the rule of the finding the signal applies to, a CVE"
- Changed
scan4 fields changed- changed
Output schema / properties / accepted / descriptionPrevious value: -"the suppressed findings with who decided and why, most severe first, capped at limit; report each as a decision, never as work to fix"New value: +"the suppressed findings with why, until when and where each decision is written, most severe first, capped at limit; report each as a decision, never as work to fix" - removed
Output schema / properties / accepted / items / properties / acceptedByRemoved value: -{ - "description": "a name the descriptor's deprecated acceptedBy states, which nothing verifies; the git history of the exclusion's line in source records who wrote it", - "type": "string" -} - added
Output schema / properties / accepted / items / properties / lineAdded value: +{ + "description": "for a rule in the descriptor, the line in source it starts at; report.json carries that line's git history in descriptor.exclusions[].gitHistory", + "type": "integer" +} - changed
Output schema / properties / unread / items / properties / reason / descriptionPrevious value: -"no lockfile, no pinned versions, no packages read, modules not loaded, name has an archive extension, no files read, 'over 1 MB, not scanned', or a count of rules that timed out with their ids"New value: +"no lockfile, no pinned versions, a count of requirements not pinned or not read with their names where few, for a pom.xml a count of dependency versions not resolved or transitive dependencies not resolved, no packages read, 'not parsed:' and the parser's error, modules not loaded, name has an archive extension, no files read, 'over 1 MB, not scanned', or a count of rules that timed out with their ids"
- Changed
summarize_report3 fields changed- changed
Output schema / properties / accepted / descriptionPrevious value: -"the suppressed findings with who decided and why, most severe first, capped at limit; report each as a decision, never as work to fix"New value: +"the suppressed findings with why, until when and where each decision is written, most severe first, capped at limit; report each as a decision, never as work to fix" - removed
Output schema / properties / accepted / items / properties / acceptedByRemoved value: -{ - "description": "a name the descriptor's deprecated acceptedBy states, which nothing verifies; the git history of the exclusion's line in source records who wrote it", - "type": "string" -} - added
Output schema / properties / accepted / items / properties / lineAdded value: +{ + "description": "for a rule in the descriptor, the line in source it starts at; report.json carries that line's git history in descriptor.exclusions[].gitHistory", + "type": "integer" +}
2 tool updates
v0.162.1- Changed
scan1 field changed- changed
Output schema / properties / accepted / items / properties / acceptedBy / descriptionPrevious value: -"who accepted it; empty means the decision is unattributed"New value: +"a name the descriptor's deprecated acceptedBy states, which nothing verifies; the git history of the exclusion's line in source records who wrote it"
- Changed
summarize_report1 field changed- changed
Output schema / properties / accepted / items / properties / acceptedBy / descriptionPrevious value: -"who accepted it; empty means the decision is unattributed"New value: +"a name the descriptor's deprecated acceptedBy states, which nothing verifies; the git history of the exclusion's line in source records who wrote it"
3 tool updates
v0.160.0- Changed
check_tools1 field changed- changed
Input schema / properties / path / descriptionPrevious value: -"path to a Saga descriptor; without it, every tool Draugr knows about is checked"New value: +"path to the Saga descriptor about to be scanned; without it, every tool Draugr knows about is checked, including ones this project never runs"
- Changed
list_controls1 field changed- changed
Output schema / properties / controls / items / properties / optInScanners / descriptionPrevious value: -"additional scanners, each enabled explicitly under controllers.<control>.<scanner>.enabled"New value: +"additional scanners, each enabled explicitly under config.controls.<control>.<scanner>.enabled"
- Changed
scan2 fields changed- changed
Output schema / properties / unread / descriptionPrevious value: -"dependency files no scanner took packages from, and Terraform files calling a module no scanner loaded; what they declare was not checked"New value: +"dependency files no scanner took packages from, Terraform files calling a module no scanner loaded, files the secrets scanner skipped, and source files Semgrep skipped or did not finish; what they declare or hold was not checked" - changed
Output schema / properties / unread / items / properties / reason / descriptionPrevious value: -"no lockfile, no pinned versions, no packages read, or modules not loaded"New value: +"no lockfile, no pinned versions, no packages read, modules not loaded, name has an archive extension, no files read, 'over 1 MB, not scanned', or a count of rules that timed out with their ids"
3 tool updates
v0.157.0- Changed
fix_list7 fields changed- added
Input schema / properties / kindAdded value: +{ + "description": "only the list for one kind of work: bump (upgrade a dependency), code (change the application's code or its server's configuration), infrastructure (change infrastructure code or a live setting), credential (remove and rotate a committed secret), wait (for somebody else's release) or decide (accept or replace, e.g. a license). Omit for every list", + "type": "string" +} - changed
Input schema / properties / limit / descriptionPrevious value: -"maximum actions to return; defaults to 20"New value: +"maximum actions per list; every action when omitted" - removed
Output schema / properties / actionsRemoved value: -{ - "description": "things to do, most urgent first; each says how many findings it clears", - "items": { - "additionalProperties": false, - "properties": { - "after": { - "type": "string" - }, - "beyond": { - "type": "string" - }, - "clears": { - "type": "integer" - }, - "component": { - "type": "string" - }, - "control": { - "type": "string" - }, - "ecosystem": { - "type": "string" - }, - "findings": { - "items": { - "type": "string" - }, - "type": [ - "null", - "array" - ] - }, - "fixedVersions": { - "items": { - "type": "string" - }, - "type": [ - "null", - "array" - ] - }, - "from": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kind": { - "type": "string" - }, - "locations": { - "items": { - "additionalProperties": false, - "properties": { - "kind": { - "type": "string" - }, - "line": { - "type": "integer" - }, - "path": { - "type": "string" - }, - "repository": { - "type": "string" - } - }, - "required": [ - "path", - "kind" - ], - "type": "object" - }, - "type": [ - "null", - "array" - ] - }, - "package": { - "type": "string" - }, - "policy": { - "type": "string" - }, - "policyApplies": { - "type": [ - "null", - "boolean" - ] - }, - "priority": { - "type": "string" - }, - "ruleIds": { - "items": { - "type": "string" - }, - "type": [ - "null", - "array" - ] - }, - "summary": { - "type": "string" - }, - "target": { - "type": "string" - }, - "title": { - "type": "string" - }, - "upstream": { - "type": "boolean" - }, - "versioning": { - "type": "string" - }, - "where": { - "items": { - "type": "string" - }, - "type": [ - "null", - "array" - ] - }, - "withinPolicy": { - "type": [ - "null", - "boolean" - ] - } - }, - "required": [ - "id", - "title", - "kind", - "clears" - ], - "type": "object" - }, - "type": [ - "null", - "array" - ] -} - added
Output schema / properties / listsAdded value: +{ + "description": "one list per kind of work, ordered bump, code, infrastructure, credential, wait, decide; a kind with no action has no list", + "items": { + "additionalProperties": false, + "properties": { + "actions": { + "description": "things to do, most urgent first: by priority, then exploitation, then severity, then how many findings each clears", + "items": { + "additionalProperties": false, + "properties": { + "after": { + "type": "string" + }, + "beyond": { + "type": "string" + }, + "clears": { + "type": "integer" + }, + "component": { + "type": "string" + }, + "control": { + "type": "string" + }, + "ecosystem": { + "type": "string" + }, + "exploitation": { + "additionalProperties": false, + "description": "the strongest exploitation evidence among the findings it clears, from the escalation of one of them; absent where KEV and EPSS raised none", + "properties": { + "asOf": { + "description": "the day the dataset was obtained, YYYY-MM-DD", + "type": "string" + }, + "detail": { + "description": "the specific fact, e.g. on KEV or EPSS 0.87", + "type": "string" + }, + "ruleId": { + "description": "the rule of the finding the signal raised, a CVE", + "type": "string" + }, + "signal": { + "description": "kev (observed exploitation) or epss (predicted probability of exploitation)", + "type": "string" + } + }, + "required": [ + "signal", + "ruleId" + ], + "type": [ + "null", + "object" + ] + }, + "findings": { + "items": { + "type": "string" + }, + "type": [ + "null", + "array" + ] + }, + "fixedVersions": { + "items": { + "type": "string" + }, + "type": [ + "null", + "array" + ] + }, + "from": { + "type": "string" + }, + "id": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "locations": { + "items": { + "additionalProperties": false, + "properties": { + "kind": { + "type": "string" + }, + "line": { + "type": "integer" + }, + "path": { + "type": "string" + }, + "repository": { + "type": "string" + } + }, + "required": [ + "path", + "kind" + ], + "type": "object" + }, + "type": [ + "null", + "array" + ] + }, + "package": { + "type": "string" + }, + "policy": { + "type": "string" + }, + "policyApplies": { + "type": [ + "null", + "boolean" + ] + }, + "priority": { + "type": "string" + }, + "ruleIds": { + "items": { + "type": "string" + }, + "type": [ + "null", + "array" + ] + }, + "severity": { + "description": "the worst severity among the findings it clears, as ranked after KEV or EPSS raised one or reachability analysis lowered one: critical, high, medium or low", + "type": "string" + }, + "severityFrom": { + "description": "the worst severity the scanners reported, present where KEV, EPSS or reachability analysis changed it", + "type": "string" + }, + "summary": { + "type": "string" + }, + "target": { + "type": "string" + }, + "title": { + "type": "string" + }, + "upstream": { + "type": "boolean" + }, + "versioning": { + "type": "string" + }, + "where": { + "items": { + "type": "string" + }, + "type": [ + "null", + "array" + ] + }, + "withinPolicy": { + "type": [ + "null", + "boolean" + ] + } + }, + "required": [ + "id", + "title", + "kind", + "clears" + ], + "type": "object" + }, + "type": [ + "null", + "array" + ] + }, + "kind": { + "description": "bump, code, infrastructure, credential, wait or decide", + "type": "string" + }, + "name": { + "description": "the list's name as the console prints it", + "type": "string" + }, + "totalActions": { + "description": "actions in this list, including any a limit left out", + "type": "integer" + }, + "totalClears": { + "description": "findings this list's actions clear between them, each counted once", + "type": "integer" + } + }, + "required": [ + "kind", + "name", + "actions", + "totalActions", + "totalClears" + ], + "type": "object" + }, + "type": [ + "null", + "array" + ] +} - changed
Output schema / properties / totalActions / descriptionPrevious value: -"actions the report implies, returned or not"New value: +"actions in every list, including any a limit left out" - changed
Output schema / properties / totalClears / descriptionPrevious value: -"findings every action clears between them, each counted once"New value: +"findings the actions in every list clear between them, each counted once" - changed
Output schema / requiredPrevious value: -[ - "actions", - "clears", - "totalActions", - "totalClears" -]New value: +[ + "lists", + "clears", + "totalActions", + "totalClears" +]
- Changed
scan1 field changed- changed
Output schema / properties / next / descriptionPrevious value: -"what to do with this result"New value: +"the first thing to do: of the first actions at or above minPriority in fix_list's lists for this report, the one with the highest priority, the earlier list on a tie; the action's title is quoted"
- Changed
summarize_report1 field changed- changed
Output schema / properties / next / descriptionPrevious value: -"what to do with this result"New value: +"the first thing to do: of the first actions at or above minPriority in fix_list's lists for this report, the one with the highest priority, the earlier list on a tie; the action's title is quoted"
13 tool updates
v0.1.0- First observed
check_tools - First observed
diff_reports - First observed
explain_rule - First observed
feeds_status - First observed
fix_list - First observed
get_saga_schema - First observed
list_controls - First observed
list_surveyors - First observed
propose_saga - First observed
scan - First observed
summarize_report - First observed
survey - First observed
validate_saga
TDQS
Scored across 13 tools
Each tool has a reasonably distinct role: descriptor authoring (get_saga_schema, propose_saga, validate_saga), scanning (scan, summarize_report, diff_reports, fix_list), environment (check_tools, feeds_status, list_controls, list_surveyors), and discovery (survey). The main risk zone is the report-consuming cluster (summarize_report vs fix_list vs diff_reports vs explain_rule), but the descriptions explicitly disambiguate with 'ask this rather than X when…' guidance, which keeps misselection low. propose_saga and survey both emit Saga YAML, though the no-descriptor vs live-system split is clearly stated.
Most names follow verb_noun (get_saga_schema, list_controls, validate_saga, explain_rule, diff_reports), but the set deviates with bare verbs (scan, survey) and noun-first names (fix_list, feeds_status). The convention is readable but mixed rather than predictable, and the noun/verb mix on fix_list vs summarize_report obscures the fact that both consume reports.
13 tools is well within the comfortable range and each one covers a distinct facet of a scan-orchestration workflow: schema, discovery, validation, execution, reporting, diffing, and environment checks. No tool appears redundant or padded.
Coverage is strong across the descriptor lifecycle, scanning, and environment diagnostics, with clear entry points for a project that has no descriptor yet. The notable gap is that accepted findings and descriptor exclusions are surfaced in reports but no tool lets an agent record an acceptance or edit exclusions, and there is no way to list prior reports beyond supplying a path.
Maintenance
Related MCP Connectors
Security scanner for AI-written code: SAST, secrets, dependencies, IaC, containers and DAST.
Security scanner for AI agents: prompt injection, leaked secrets, PII, and SSRF-risk URL detection.
Pay-per-call cybersecurity for AI agents: vuln scans, threat intel, compliance, code security.
Security reviews for coding agents: diffs checked against your org policy and live infrastructure.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceA powerful and flexible Kubernetes MCP server implementation with support for OpenShift.2,208GoApache 2.0
- MIT
- AlicenseAqualityCmaintenanceEnables AI agents to scan code for security vulnerabilities using multiple static analysis tools, with support for filtering, deduplication, and CI/CD integration.2760 PyPI4MIT

Azure DevOps MCP Serverofficial
AlicenseNot gradedqualityAmaintenanceProvides Azure DevOps tooling for AI agents, enabling interaction with projects, work items, repositories, and pipelines through natural language.171,094 npm2,074MIT