Skip to main content
Glama

EchelonGraph CVE & Exposure

Check an SBOM against the advisory corpus

check_sbom
Read-onlyIdempotent

Check a dependency list against EchelonGraph's advisory corpus, one verdict per component. Pass purls (package URLs, up to 2,000 distinct) or sbom (a CycloneDX JSON or SPDX JSON document, as JSON text or as an object, up to 5,000,000 characters). The purls are read from the document by this MCP server and only they are sent to the API, in POST bodies of at most 200 purls each, one after another, never in a URL; the document itself is not sent on. Run from npm, this server is on your machine; over the hosted endpoint (mcp.echelongraph.io) it is EchelonGraph's, and the document is the request body, accepted up to 6 MiB. A component without a purl is counted and not checked. Each purl is mapped to its OSV ecosystem, package name and version and matched against the affected version ranges EchelonGraph holds from OSV.dev advisory records; there is no ranking and no score. data.results holds one row per component sent, in order (index counts across batches), with verdict (affected, not_affected, undetermined or not_assessed), assessed, not_assessed_reason, cve_ids, matches, count, not_affected_count and undetermined_count; data.summary counts the verdicts, summed over the batches (partial is true when any batch's was). not_affected is the only clean verdict. undetermined: advisories name the package but at least one could not be decided at this version and none matched. not_assessed: no verdict at all, because the package is not in the corpus, the purl type has no OSV ecosystem, a deb, apk or rpm purl carries no distro qualifier naming its release (EchelonGraph does not guess one), the version is missing, the lookup failed, or the batch's time budget ran out first (time_budget). Neither undetermined nor not_assessed is clean, and the note gives their counts. The API allows 1,200 components a minute per caller; when it answers 429 with Retry-After, the tool waits as asked and sends the batch again, within 50 seconds per call. When the next wait would pass that, or a batch after the first fails, the tool stops and answers what it has: coverage.not_sent counts the purls not sent and coverage.not_sent_reason says why (time_budget, rate_limited or request_failed), data.not_sent_purls lists them for a later call, and they are not checked and not clean. A list or document with more than 2,000 distinct purls is refused, not truncated: split it. Its structured result carries state (measured when at least one component got a verdict, else not_assessed), measured_at (null: the corpus is read through a cache, so no single read time exists), method, coverage (what the input held, what was sent in how many batches, and what was not sent and why), freshness (null) and notes, with data equal to the API's JSON (for more than one batch, the batches' answers merged); the result's last text block repeats it without data (the first text block) and without the note's sentences (the text block before it), with which notes ends. Past 30,000 characters of JSON, the first text block holds data cut to fit, and the note says what the cut leaves out and where to read it (TEXT CUT); data in the structured result always holds it whole. Cut, each row keeps index, purl, verdict, not_assessed_reason and cve_ids at least, and the rows whose verdict is not_affected, then not_assessed, are left out of the text before any other; not_sent_purls keeps its first 10, and the note says from which position of the input the purls not sent run.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
sbomNoa CycloneDX JSON or SPDX JSON document, as JSON text or as an object; its purls are read by this MCP server and only they are sent to the API; over the hosted endpoint (mcp.echelongraph.io) the document is the request body
purlsNopackage URLs to check, e.g. pkg:npm/lodash@4.17.20 or pkg:deb/debian/openssl@3.0.11-1~deb12u1?distro=debian-12 (up to 2,000 distinct, sent in batches of 200)

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • changedInput schema / properties / sbom / description
      Previous value: -"a CycloneDX JSON or SPDX JSON document, as JSON text or as an object; its purls are read here and only they are sent"New value: +"a CycloneDX JSON or SPDX JSON document, as JSON text or as an object; its purls are read by this MCP server and only they are sent to the API; over the hosted endpoint (mcp.echelongraph.io) the document is the request body"
  2. Changed1 schema field changed
    • changedOutput schema / oneOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "coverage": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "properties": {
      -              "batch_size": {
      -                "description": "The most purls one request carries (the API's cap per request).",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches": {
      -                "description": "Requests the distinct purls make, at batch_size each.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches_sent": {
      -                "description": "Of those, the ones the API answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "components_in_document": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      -              },
      -              "distinct_purls": {
      -                "description": "Distinct purls to check: sent plus not_sent.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "duplicates_removed": {
      -                "description": "Purls that appeared more than once and were sent once.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "input": {
      -                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      -                "enum": [
      -                  "purls",
      -                  "cyclonedx",
      -                  "spdx"
      -                ],
      -                "type": "string"
      -              },
      -              "not_assessed": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Of those sent, the components with no verdict, as the answer counts them."
      -              },
      -              "not_sent": {
      -                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "not_sent_reason": {
      -                "anyOf": [
      -                  {
      -                    "enum": [
      -                      "time_budget",
      -                      "rate_limited",
      -                      "request_failed"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      -              },
      -              "partial": {
      -                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "rate_limit_waits": {
      -                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "sent": {
      -                "description": "Distinct purls sent and answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "waited_ms": {
      -                "description": "Milliseconds spent in those waits.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "with_purl": {
      -                "description": "Of those, the ones carrying a purl.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "without_purl": {
      -                "description": "The ones without a purl: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              }
      -            },
      -            "required": [
      -              "input",
      -              "components_in_document",
      -              "with_purl",
      -              "without_purl",
      -              "duplicates_removed",
      -              "distinct_purls",
      -              "batch_size",
      -              "batches",
      -              "batches_sent",
      -              "sent",
      -              "not_sent",
      -              "not_sent_reason",
      -              "rate_limit_waits",
      -              "waited_ms",
      -              "not_assessed",
      -              "partial"
      -            ],
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "What the answer covers; null where the answer says nothing about it."
      -      },
      -      "data": {
      -        "additionalProperties": {},
      -        "properties": {
      -          "answered_at": {
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "components": {
      -            "type": [
      -              "number",
      -              "null"
      -            ]
      -          },
      -          "match_layer": {
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "not_sent_purls": {
      -            "anyOf": [
      -              {
      -                "items": {
      -                  "type": "string"
      -                },
      -                "type": "array"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "description": "The distinct purls not sent (coverage.not_sent_reason says why): not checked, and not clean. Present only when some were not sent."
      -          },
      -          "results": {
      -            "items": {
      -              "additionalProperties": {},
      -              "properties": {
      -                "advisories_considered": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "assessed": {
      -                  "description": "Whether the matcher produced a verdict for this component. false is never clean.",
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "candidates_capped": {
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "capped": {
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "code": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "cve_ids": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "type": "string"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "ecosystem": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "error": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "index": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "input_kind": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matches": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "additionalProperties": {},
      -                        "properties": {
      -                          "cve_id": {
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          }
      -                        },
      -                        "type": "object"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "not_affected_count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "not_assessed_reason": {
      -                  "description": "Why assessed is false: package_not_in_advisory_corpus, no_decidable_advisory, advisory_lookup_failed, candidate_window_truncated, time_budget, distro_release_unknown, purl_type_unsupported, version_missing or invalid_component.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "package": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "purl": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "undetermined": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "additionalProperties": {},
      -                        "properties": {
      -                          "cve_id": {
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          }
      -                        },
      -                        "type": "object"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "undetermined_count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "verdict": {
      -                  "description": "affected (count > 0); not_affected (assessed, no match, nothing undetermined: the only clean verdict); undetermined (advisories name the package, at least one could not be decided, none matched: not clean); not_assessed (no verdict: not clean).",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "version": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                }
      -              },
      -              "type": "object"
      -            },
      -            "type": "array"
      -          },
      -          "summary": {
      -            "additionalProperties": {},
      -            "properties": {
      -              "affected": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "components": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "corpus_cache_max_age_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "elapsed_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "lookups": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_affected": {
      -                "description": "Components with a decided, clean verdict.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_assessed": {
      -                "description": "Components with no verdict: not clean.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_assessed_by_reason": {
      -                "anyOf": [
      -                  {
      -                    "additionalProperties": {},
      -                    "properties": {
      -                      "advisory_lookup_failed": {
      -                        "type": "number"
      -                      },
      -                      "candidate_window_truncated": {
      -                        "type": "number"
      -                      },
      -                      "distro_release_unknown": {
      -                        "description": "deb, apk or rpm purls without a distro qualifier naming the release, which EchelonGraph does not guess.",
      -                        "type": "number"
      -                      },
      -                      "invalid_component": {
      -                        "type": "number"
      -                      },
      -                      "no_decidable_advisory": {
      -                        "type": "number"
      -                      },
      -                      "package_not_in_advisory_corpus": {
      -                        "type": "number"
      -                      },
      -                      "purl_type_unsupported": {
      -                        "type": "number"
      -                      },
      -                      "time_budget": {
      -                        "description": "Components the batch's time budget ran out before: not clean; check them again.",
      -                        "type": "number"
      -                      },
      -                      "version_missing": {
      -                        "type": "number"
      -                      }
      -                    },
      -                    "type": "object"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "The not_assessed components, counted by not_assessed_reason."
      -              },
      -              "partial": {
      -                "description": "true when the time budget ran out before every component was looked up.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "time_budget_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "undetermined": {
      -                "description": "Components whose advisories could not all be decided and none matched: not clean.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              }
      -            },
      -            "type": "object"
      -          }
      -        },
      -        "type": "object"
      -      },
      -      "freshness": {
      -        "description": "The producing radar's last completed check (last_run_at), where the API serves one.",
      -        "type": "null"
      -      },
      -      "measured_at": {
      -        "anyOf": [
      -          {
      -            "description": "An RFC 3339 instant.",
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "When the underlying observation was made, as the API states it; null when the answer does not say or holds no observation."
      -      },
      -      "method": {
      -        "description": "How the numbers were produced.",
      -        "type": "string"
      -      },
      -      "notes": {
      -        "description": "Caveats, one sentence each.",
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "state": {
      -        "description": "measured: a measurement of what was asked; an exposure count is measured only with measured_at and method. not_assessed: the answer holds no dated measurement of what was asked, so no count in it is presented as one; it can still relay a count, as what the source holds on record, undated, and its notes (and exposure_state, where the result carries it) say what each count is.",
      -        "enum": [
      -          "measured",
      -          "not_assessed"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "state",
      -      "measured_at",
      -      "method",
      -      "coverage",
      -      "freshness",
      -      "notes",
      -      "data"
      -    ],
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "coverage": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "properties": {
      -              "batch_size": {
      -                "description": "The most purls one request carries (the API's cap per request).",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches": {
      -                "description": "Requests the distinct purls make, at batch_size each.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "batches_sent": {
      -                "description": "Of those, the ones the API answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "components_in_document": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      -              },
      -              "distinct_purls": {
      -                "description": "Distinct purls to check: sent plus not_sent.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "duplicates_removed": {
      -                "description": "Purls that appeared more than once and were sent once.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "input": {
      -                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      -                "enum": [
      -                  "purls",
      -                  "cyclonedx",
      -                  "spdx"
      -                ],
      -                "type": "string"
      -              },
      -              "not_assessed": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Of those sent, the components with no verdict, as the answer counts them."
      -              },
      -              "not_sent": {
      -                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "not_sent_reason": {
      -                "anyOf": [
      -                  {
      -                    "enum": [
      -                      "time_budget",
      -                      "rate_limited",
      -                      "request_failed"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      -              },
      -              "partial": {
      -                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "rate_limit_waits": {
      -                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "sent": {
      -                "description": "Distinct purls sent and answered.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "waited_ms": {
      -                "description": "Milliseconds spent in those waits.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "with_purl": {
      -                "description": "Of those, the ones carrying a purl.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "without_purl": {
      -                "description": "The ones without a purl: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              }
      -            },
      -            "required": [
      -              "input",
      -              "components_in_document",
      -              "with_purl",
      -              "without_purl",
      -              "duplicates_removed",
      -              "distinct_purls",
      -              "batch_size",
      -              "batches",
      -              "batches_sent",
      -              "sent",
      -              "not_sent",
      -              "not_sent_reason",
      -              "rate_limit_waits",
      -              "waited_ms",
      -              "not_assessed",
      -              "partial"
      -            ],
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ]
      -      },
      -      "error": {
      -        "additionalProperties": false,
      -        "properties": {
      -          "kind": {
      -            "description": "What failed: the request (network, timeout), the answer (http, not_json, not_object, unexpected_shape), the input, or this server.",
      -            "enum": [
      -              "network",
      -              "timeout",
      -              "http",
      -              "not_json",
      -              "not_object",
      -              "invalid_input",
      -              "internal",
      -              "unexpected_shape",
      -              "radars"
      -            ],
      -            "type": "string"
      -          },
      -          "message": {
      -            "description": "The cause: the API's own message, or what went wrong.",
      -            "type": "string"
      -          },
      -          "path": {
      -            "description": "The API path requested, when a request was made.",
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "status": {
      -            "anyOf": [
      -              {
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "description": "The HTTP status, when the API answered one."
      -          }
      -        },
      -        "required": [
      -          "kind",
      -          "path",
      -          "status",
      -          "message"
      -        ],
      -        "type": "object"
      -      },
      -      "freshness": {
      -        "type": "null"
      -      },
      -      "measured_at": {
      -        "type": "null"
      -      },
      -      "method": {
      -        "type": "null"
      -      },
      -      "notes": {
      -        "description": "Caveats, one sentence each.",
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "state": {
      -        "description": "failed: the lookup did not complete. invalid_input: the input was refused, so nothing was looked up. Neither is a finding.",
      -        "enum": [
      -          "failed",
      -          "invalid_input"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "state",
      -      "measured_at",
      -      "method",
      -      "coverage",
      -      "freshness",
      -      "notes",
      -      "error"
      -    ],
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "coverage": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "properties": {
      +              "batch_size": {
      +                "description": "The most purls one request carries (the API's cap per request).",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches": {
      +                "description": "Requests the distinct purls make, at batch_size each.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches_sent": {
      +                "description": "Of those, the ones the API answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "components_in_document": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      +              },
      +              "distinct_purls": {
      +                "description": "Distinct purls to check: sent plus not_sent.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "duplicates_removed": {
      +                "description": "Purls that appeared more than once and were sent once.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "input": {
      +                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      +                "enum": [
      +                  "purls",
      +                  "cyclonedx",
      +                  "spdx"
      +                ],
      +                "type": "string"
      +              },
      +              "not_assessed": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Of those sent, the components with no verdict, as the answer counts them."
      +              },
      +              "not_sent": {
      +                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "not_sent_reason": {
      +                "anyOf": [
      +                  {
      +                    "enum": [
      +                      "time_budget",
      +                      "rate_limited",
      +                      "request_failed"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      +              },
      +              "partial": {
      +                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "rate_limit_waits": {
      +                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "sent": {
      +                "description": "Distinct purls sent and answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "waited_ms": {
      +                "description": "Milliseconds spent in those waits.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "with_purl": {
      +                "description": "Of those, the ones carrying a purl.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "without_purl": {
      +                "description": "The ones without a purl: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              }
      +            },
      +            "required": [
      +              "input",
      +              "components_in_document",
      +              "with_purl",
      +              "without_purl",
      +              "duplicates_removed",
      +              "distinct_purls",
      +              "batch_size",
      +              "batches",
      +              "batches_sent",
      +              "sent",
      +              "not_sent",
      +              "not_sent_reason",
      +              "rate_limit_waits",
      +              "waited_ms",
      +              "not_assessed",
      +              "partial"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "What the answer covers; null where the answer says nothing about it."
      +      },
      +      "data": {
      +        "additionalProperties": {},
      +        "properties": {
      +          "answered_at": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "components": {
      +            "type": [
      +              "number",
      +              "null"
      +            ]
      +          },
      +          "match_layer": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "not_sent_purls": {
      +            "anyOf": [
      +              {
      +                "items": {
      +                  "type": "string"
      +                },
      +                "type": "array"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "description": "The distinct purls not sent (coverage.not_sent_reason says why): not checked, and not clean. Present only when some were not sent."
      +          },
      +          "results": {
      +            "items": {
      +              "additionalProperties": {},
      +              "properties": {
      +                "advisories_considered": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "assessed": {
      +                  "description": "Whether the matcher produced a verdict for this component. false is never clean.",
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "candidates_capped": {
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "capped": {
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "code": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "cve_ids": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "type": "string"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "ecosystem": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "error": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "index": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "input_kind": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matches": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "additionalProperties": {},
      +                        "properties": {
      +                          "cve_id": {
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          }
      +                        },
      +                        "type": "object"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "not_affected_count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "not_assessed_reason": {
      +                  "description": "Why assessed is false: package_not_in_advisory_corpus, no_decidable_advisory, advisory_lookup_failed, candidate_window_truncated, time_budget, distro_release_unknown, purl_type_unsupported, version_missing or invalid_component.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "package": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "purl": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "undetermined": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "additionalProperties": {},
      +                        "properties": {
      +                          "cve_id": {
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          }
      +                        },
      +                        "type": "object"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "undetermined_count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "verdict": {
      +                  "description": "affected (count > 0); not_affected (assessed, no match, nothing undetermined: the only clean verdict); undetermined (advisories name the package, at least one could not be decided, none matched: not clean); not_assessed (no verdict: not clean).",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "version": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                }
      +              },
      +              "type": "object"
      +            },
      +            "type": "array"
      +          },
      +          "summary": {
      +            "additionalProperties": {},
      +            "properties": {
      +              "affected": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "components": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "corpus_cache_max_age_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "elapsed_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "lookups": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_affected": {
      +                "description": "Components with a decided, clean verdict.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_assessed": {
      +                "description": "Components with no verdict: not clean.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_assessed_by_reason": {
      +                "anyOf": [
      +                  {
      +                    "additionalProperties": {},
      +                    "properties": {
      +                      "advisory_lookup_failed": {
      +                        "type": "number"
      +                      },
      +                      "candidate_window_truncated": {
      +                        "type": "number"
      +                      },
      +                      "distro_release_unknown": {
      +                        "description": "deb, apk or rpm purls without a distro qualifier naming the release, which EchelonGraph does not guess.",
      +                        "type": "number"
      +                      },
      +                      "invalid_component": {
      +                        "type": "number"
      +                      },
      +                      "no_decidable_advisory": {
      +                        "type": "number"
      +                      },
      +                      "package_not_in_advisory_corpus": {
      +                        "type": "number"
      +                      },
      +                      "purl_type_unsupported": {
      +                        "type": "number"
      +                      },
      +                      "time_budget": {
      +                        "description": "Components the batch's time budget ran out before: not clean; check them again.",
      +                        "type": "number"
      +                      },
      +                      "version_missing": {
      +                        "type": "number"
      +                      }
      +                    },
      +                    "type": "object"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "The not_assessed components, counted by not_assessed_reason."
      +              },
      +              "partial": {
      +                "description": "true when the time budget ran out before every component was looked up.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "time_budget_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "undetermined": {
      +                "description": "Components whose advisories could not all be decided and none matched: not clean.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              }
      +            },
      +            "type": "object"
      +          }
      +        },
      +        "type": "object"
      +      },
      +      "freshness": {
      +        "description": "The producing radar's last completed check (last_run_at), where the API serves one.",
      +        "type": "null"
      +      },
      +      "measured_at": {
      +        "anyOf": [
      +          {
      +            "description": "An RFC 3339 instant.",
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "When the underlying observation was made, as the API states it; null when the answer does not say or holds no observation."
      +      },
      +      "method": {
      +        "description": "How the numbers were produced.",
      +        "type": "string"
      +      },
      +      "notes": {
      +        "description": "Caveats, one sentence each.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "state": {
      +        "description": "measured: a measurement of what was asked; an exposure count is measured only with measured_at and method. not_assessed: the answer holds no dated measurement of what was asked, so no count in it is presented as one; it can still relay a count, as what the source holds on record, undated, and its notes (and exposure_state, where the result carries it) say what each count is.",
      +        "enum": [
      +          "measured",
      +          "not_assessed"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "state",
      +      "measured_at",
      +      "method",
      +      "coverage",
      +      "freshness",
      +      "notes",
      +      "data"
      +    ],
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "coverage": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "properties": {
      +              "batch_size": {
      +                "description": "The most purls one request carries (the API's cap per request).",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches": {
      +                "description": "Requests the distinct purls make, at batch_size each.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches_sent": {
      +                "description": "Of those, the ones the API answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "components_in_document": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      +              },
      +              "distinct_purls": {
      +                "description": "Distinct purls to check: sent plus not_sent.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "duplicates_removed": {
      +                "description": "Purls that appeared more than once and were sent once.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "input": {
      +                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      +                "enum": [
      +                  "purls",
      +                  "cyclonedx",
      +                  "spdx"
      +                ],
      +                "type": "string"
      +              },
      +              "not_assessed": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Of those sent, the components with no verdict, as the answer counts them."
      +              },
      +              "not_sent": {
      +                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "not_sent_reason": {
      +                "anyOf": [
      +                  {
      +                    "enum": [
      +                      "time_budget",
      +                      "rate_limited",
      +                      "request_failed"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, before a batch or while one was unanswered, which is then cut off; or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      +              },
      +              "partial": {
      +                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "rate_limit_waits": {
      +                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "sent": {
      +                "description": "Distinct purls sent and answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "waited_ms": {
      +                "description": "Milliseconds spent in those waits.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "with_purl": {
      +                "description": "Of those, the ones carrying a purl.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "without_purl": {
      +                "description": "The ones without a purl: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              }
      +            },
      +            "required": [
      +              "input",
      +              "components_in_document",
      +              "with_purl",
      +              "without_purl",
      +              "duplicates_removed",
      +              "distinct_purls",
      +              "batch_size",
      +              "batches",
      +              "batches_sent",
      +              "sent",
      +              "not_sent",
      +              "not_sent_reason",
      +              "rate_limit_waits",
      +              "waited_ms",
      +              "not_assessed",
      +              "partial"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ]
      +      },
      +      "error": {
      +        "additionalProperties": false,
      +        "properties": {
      +          "kind": {
      +            "description": "What failed: the request (network, timeout), the answer (http, not_json, not_object, unexpected_shape), the input, or this server.",
      +            "enum": [
      +              "network",
      +              "timeout",
      +              "http",
      +              "not_json",
      +              "not_object",
      +              "invalid_input",
      +              "internal",
      +              "unexpected_shape",
      +              "radars"
      +            ],
      +            "type": "string"
      +          },
      +          "message": {
      +            "description": "The cause: the API's own message, or what went wrong.",
      +            "type": "string"
      +          },
      +          "path": {
      +            "description": "The API path requested, when a request was made.",
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "status": {
      +            "anyOf": [
      +              {
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "description": "The HTTP status, when the API answered one."
      +          }
      +        },
      +        "required": [
      +          "kind",
      +          "path",
      +          "status",
      +          "message"
      +        ],
      +        "type": "object"
      +      },
      +      "freshness": {
      +        "type": "null"
      +      },
      +      "measured_at": {
      +        "type": "null"
      +      },
      +      "method": {
      +        "type": "null"
      +      },
      +      "notes": {
      +        "description": "Caveats, one sentence each.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "state": {
      +        "description": "failed: the lookup did not complete. invalid_input: the input was refused, so nothing was looked up. Neither is a finding.",
      +        "enum": [
      +          "failed",
      +          "invalid_input"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "state",
      +      "measured_at",
      +      "method",
      +      "coverage",
      +      "freshness",
      +      "notes",
      +      "error"
      +    ],
      +    "type": "object"
      +  }
      +]
  3. Changed2 schema fields changed
    • changedInput schema / properties / purls / description
      Previous value: -"package URLs to check, e.g. pkg:npm/lodash@4.17.20 or pkg:deb/debian/openssl@3.0.11-1~deb12u1?distro=debian-12 (up to 200)"New value: +"package URLs to check, e.g. pkg:npm/lodash@4.17.20 or pkg:deb/debian/openssl@3.0.11-1~deb12u1?distro=debian-12 (up to 2,000 distinct, sent in batches of 200)"
    • changedOutput schema / oneOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "coverage": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "properties": {
      -              "components_in_document": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      -              },
      -              "duplicates_removed": {
      -                "description": "Purls that appeared more than once and were sent once.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "input": {
      -                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      -                "enum": [
      -                  "purls",
      -                  "cyclonedx",
      -                  "spdx"
      -                ],
      -                "type": "string"
      -              },
      -              "not_assessed": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Of those sent, the components with no verdict, as the answer counts them."
      -              },
      -              "partial": {
      -                "description": "The answer's summary.partial: true when the time budget ran out first.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "sent": {
      -                "description": "Distinct purls sent and checked.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "with_purl": {
      -                "description": "Of those, the ones carrying a purl.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "without_purl": {
      -                "description": "The ones without a purl: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              }
      -            },
      -            "required": [
      -              "input",
      -              "components_in_document",
      -              "with_purl",
      -              "without_purl",
      -              "duplicates_removed",
      -              "sent",
      -              "not_assessed",
      -              "partial"
      -            ],
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "What the answer covers; null where the answer says nothing about it."
      -      },
      -      "data": {
      -        "additionalProperties": {},
      -        "properties": {
      -          "answered_at": {
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "components": {
      -            "type": [
      -              "number",
      -              "null"
      -            ]
      -          },
      -          "match_layer": {
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "results": {
      -            "items": {
      -              "additionalProperties": {},
      -              "properties": {
      -                "advisories_considered": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "assessed": {
      -                  "description": "Whether the matcher produced a verdict for this component. false is never clean.",
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "candidates_capped": {
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "capped": {
      -                  "type": [
      -                    "boolean",
      -                    "null"
      -                  ]
      -                },
      -                "code": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "cve_ids": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "type": "string"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "ecosystem": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "error": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "index": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "input_kind": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "matches": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "additionalProperties": {},
      -                        "properties": {
      -                          "cve_id": {
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          }
      -                        },
      -                        "type": "object"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "not_affected_count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "not_assessed_reason": {
      -                  "description": "Why assessed is false: package_not_in_advisory_corpus, no_decidable_advisory, advisory_lookup_failed, candidate_window_truncated, time_budget, distro_release_unknown, purl_type_unsupported, version_missing or invalid_component.",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "package": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "purl": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "undetermined": {
      -                  "anyOf": [
      -                    {
      -                      "items": {
      -                        "additionalProperties": {},
      -                        "properties": {
      -                          "cve_id": {
      -                            "type": [
      -                              "string",
      -                              "null"
      -                            ]
      -                          }
      -                        },
      -                        "type": "object"
      -                      },
      -                      "type": "array"
      -                    },
      -                    {
      -                      "type": "null"
      -                    }
      -                  ]
      -                },
      -                "undetermined_count": {
      -                  "type": [
      -                    "number",
      -                    "null"
      -                  ]
      -                },
      -                "verdict": {
      -                  "description": "affected (count > 0); not_affected (assessed, no match, nothing undetermined: the only clean verdict); undetermined (advisories name the package, at least one could not be decided, none matched: not clean); not_assessed (no verdict: not clean).",
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                },
      -                "version": {
      -                  "type": [
      -                    "string",
      -                    "null"
      -                  ]
      -                }
      -              },
      -              "type": "object"
      -            },
      -            "type": "array"
      -          },
      -          "summary": {
      -            "additionalProperties": {},
      -            "properties": {
      -              "affected": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "components": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "corpus_cache_max_age_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "elapsed_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "lookups": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_affected": {
      -                "description": "Components with a decided, clean verdict.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_assessed": {
      -                "description": "Components with no verdict: not clean.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "not_assessed_by_reason": {
      -                "anyOf": [
      -                  {
      -                    "additionalProperties": {},
      -                    "properties": {
      -                      "advisory_lookup_failed": {
      -                        "type": "number"
      -                      },
      -                      "candidate_window_truncated": {
      -                        "type": "number"
      -                      },
      -                      "distro_release_unknown": {
      -                        "description": "deb, apk or rpm purls without a distro qualifier naming the release, which EchelonGraph does not guess.",
      -                        "type": "number"
      -                      },
      -                      "invalid_component": {
      -                        "type": "number"
      -                      },
      -                      "no_decidable_advisory": {
      -                        "type": "number"
      -                      },
      -                      "package_not_in_advisory_corpus": {
      -                        "type": "number"
      -                      },
      -                      "purl_type_unsupported": {
      -                        "type": "number"
      -                      },
      -                      "time_budget": {
      -                        "description": "Components the batch's time budget ran out before: not clean; check them again.",
      -                        "type": "number"
      -                      },
      -                      "version_missing": {
      -                        "type": "number"
      -                      }
      -                    },
      -                    "type": "object"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "The not_assessed components, counted by not_assessed_reason."
      -              },
      -              "partial": {
      -                "description": "true when the time budget ran out before every component was looked up.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "time_budget_ms": {
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              },
      -              "undetermined": {
      -                "description": "Components whose advisories could not all be decided and none matched: not clean.",
      -                "type": [
      -                  "number",
      -                  "null"
      -                ]
      -              }
      -            },
      -            "type": "object"
      -          }
      -        },
      -        "type": "object"
      -      },
      -      "freshness": {
      -        "description": "The producing radar's last completed check (last_run_at), where the API serves one.",
      -        "type": "null"
      -      },
      -      "measured_at": {
      -        "anyOf": [
      -          {
      -            "description": "An RFC 3339 instant.",
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "description": "When the underlying observation was made, as the API states it; null when the answer does not say or holds no observation."
      -      },
      -      "method": {
      -        "description": "How the numbers were produced.",
      -        "type": "string"
      -      },
      -      "notes": {
      -        "description": "Caveats, one sentence each.",
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "state": {
      -        "description": "measured: a measurement of what was asked; an exposure count is measured only with measured_at and method. not_assessed: the answer holds no dated measurement of what was asked, so no count in it is presented as one; it can still relay a count, as what the source holds on record, undated, and its notes (and exposure_state, where the result carries it) say what each count is.",
      -        "enum": [
      -          "measured",
      -          "not_assessed"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "state",
      -      "measured_at",
      -      "method",
      -      "coverage",
      -      "freshness",
      -      "notes",
      -      "data"
      -    ],
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "coverage": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "properties": {
      -              "components_in_document": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      -              },
      -              "duplicates_removed": {
      -                "description": "Purls that appeared more than once and were sent once.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "input": {
      -                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      -                "enum": [
      -                  "purls",
      -                  "cyclonedx",
      -                  "spdx"
      -                ],
      -                "type": "string"
      -              },
      -              "not_assessed": {
      -                "anyOf": [
      -                  {
      -                    "maximum": 9007199254740991,
      -                    "minimum": -9007199254740991,
      -                    "type": "integer"
      -                  },
      -                  {
      -                    "type": "null"
      -                  }
      -                ],
      -                "description": "Of those sent, the components with no verdict, as the answer counts them."
      -              },
      -              "partial": {
      -                "description": "The answer's summary.partial: true when the time budget ran out first.",
      -                "type": [
      -                  "boolean",
      -                  "null"
      -                ]
      -              },
      -              "sent": {
      -                "description": "Distinct purls sent and checked.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "with_purl": {
      -                "description": "Of those, the ones carrying a purl.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              "without_purl": {
      -                "description": "The ones without a purl: not checked, and not clean.",
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              }
      -            },
      -            "required": [
      -              "input",
      -              "components_in_document",
      -              "with_purl",
      -              "without_purl",
      -              "duplicates_removed",
      -              "sent",
      -              "not_assessed",
      -              "partial"
      -            ],
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ]
      -      },
      -      "error": {
      -        "additionalProperties": false,
      -        "properties": {
      -          "kind": {
      -            "description": "What failed: the request (network, timeout), the answer (http, not_json, not_object, unexpected_shape), the input, or this server.",
      -            "enum": [
      -              "network",
      -              "timeout",
      -              "http",
      -              "not_json",
      -              "not_object",
      -              "invalid_input",
      -              "internal",
      -              "unexpected_shape",
      -              "radars"
      -            ],
      -            "type": "string"
      -          },
      -          "message": {
      -            "description": "The cause: the API's own message, or what went wrong.",
      -            "type": "string"
      -          },
      -          "path": {
      -            "description": "The API path requested, when a request was made.",
      -            "type": [
      -              "string",
      -              "null"
      -            ]
      -          },
      -          "status": {
      -            "anyOf": [
      -              {
      -                "maximum": 9007199254740991,
      -                "minimum": -9007199254740991,
      -                "type": "integer"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "description": "The HTTP status, when the API answered one."
      -          }
      -        },
      -        "required": [
      -          "kind",
      -          "path",
      -          "status",
      -          "message"
      -        ],
      -        "type": "object"
      -      },
      -      "freshness": {
      -        "type": "null"
      -      },
      -      "measured_at": {
      -        "type": "null"
      -      },
      -      "method": {
      -        "type": "null"
      -      },
      -      "notes": {
      -        "description": "Caveats, one sentence each.",
      -        "items": {
      -          "type": "string"
      -        },
      -        "type": "array"
      -      },
      -      "state": {
      -        "description": "failed: the lookup did not complete. invalid_input: the input was refused, so nothing was looked up. Neither is a finding.",
      -        "enum": [
      -          "failed",
      -          "invalid_input"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "state",
      -      "measured_at",
      -      "method",
      -      "coverage",
      -      "freshness",
      -      "notes",
      -      "error"
      -    ],
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "coverage": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "properties": {
      +              "batch_size": {
      +                "description": "The most purls one request carries (the API's cap per request).",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches": {
      +                "description": "Requests the distinct purls make, at batch_size each.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches_sent": {
      +                "description": "Of those, the ones the API answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "components_in_document": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      +              },
      +              "distinct_purls": {
      +                "description": "Distinct purls to check: sent plus not_sent.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "duplicates_removed": {
      +                "description": "Purls that appeared more than once and were sent once.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "input": {
      +                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      +                "enum": [
      +                  "purls",
      +                  "cyclonedx",
      +                  "spdx"
      +                ],
      +                "type": "string"
      +              },
      +              "not_assessed": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Of those sent, the components with no verdict, as the answer counts them."
      +              },
      +              "not_sent": {
      +                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "not_sent_reason": {
      +                "anyOf": [
      +                  {
      +                    "enum": [
      +                      "time_budget",
      +                      "rate_limited",
      +                      "request_failed"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      +              },
      +              "partial": {
      +                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "rate_limit_waits": {
      +                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "sent": {
      +                "description": "Distinct purls sent and answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "waited_ms": {
      +                "description": "Milliseconds spent in those waits.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "with_purl": {
      +                "description": "Of those, the ones carrying a purl.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "without_purl": {
      +                "description": "The ones without a purl: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              }
      +            },
      +            "required": [
      +              "input",
      +              "components_in_document",
      +              "with_purl",
      +              "without_purl",
      +              "duplicates_removed",
      +              "distinct_purls",
      +              "batch_size",
      +              "batches",
      +              "batches_sent",
      +              "sent",
      +              "not_sent",
      +              "not_sent_reason",
      +              "rate_limit_waits",
      +              "waited_ms",
      +              "not_assessed",
      +              "partial"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "What the answer covers; null where the answer says nothing about it."
      +      },
      +      "data": {
      +        "additionalProperties": {},
      +        "properties": {
      +          "answered_at": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "components": {
      +            "type": [
      +              "number",
      +              "null"
      +            ]
      +          },
      +          "match_layer": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "not_sent_purls": {
      +            "anyOf": [
      +              {
      +                "items": {
      +                  "type": "string"
      +                },
      +                "type": "array"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "description": "The distinct purls not sent (coverage.not_sent_reason says why): not checked, and not clean. Present only when some were not sent."
      +          },
      +          "results": {
      +            "items": {
      +              "additionalProperties": {},
      +              "properties": {
      +                "advisories_considered": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "assessed": {
      +                  "description": "Whether the matcher produced a verdict for this component. false is never clean.",
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "candidates_capped": {
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "capped": {
      +                  "type": [
      +                    "boolean",
      +                    "null"
      +                  ]
      +                },
      +                "code": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "cve_ids": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "type": "string"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "ecosystem": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "error": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "index": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "input_kind": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "matches": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "additionalProperties": {},
      +                        "properties": {
      +                          "cve_id": {
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          }
      +                        },
      +                        "type": "object"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "not_affected_count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "not_assessed_reason": {
      +                  "description": "Why assessed is false: package_not_in_advisory_corpus, no_decidable_advisory, advisory_lookup_failed, candidate_window_truncated, time_budget, distro_release_unknown, purl_type_unsupported, version_missing or invalid_component.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "package": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "purl": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "undetermined": {
      +                  "anyOf": [
      +                    {
      +                      "items": {
      +                        "additionalProperties": {},
      +                        "properties": {
      +                          "cve_id": {
      +                            "type": [
      +                              "string",
      +                              "null"
      +                            ]
      +                          }
      +                        },
      +                        "type": "object"
      +                      },
      +                      "type": "array"
      +                    },
      +                    {
      +                      "type": "null"
      +                    }
      +                  ]
      +                },
      +                "undetermined_count": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "verdict": {
      +                  "description": "affected (count > 0); not_affected (assessed, no match, nothing undetermined: the only clean verdict); undetermined (advisories name the package, at least one could not be decided, none matched: not clean); not_assessed (no verdict: not clean).",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "version": {
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                }
      +              },
      +              "type": "object"
      +            },
      +            "type": "array"
      +          },
      +          "summary": {
      +            "additionalProperties": {},
      +            "properties": {
      +              "affected": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "components": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "corpus_cache_max_age_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "elapsed_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "lookups": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_affected": {
      +                "description": "Components with a decided, clean verdict.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_assessed": {
      +                "description": "Components with no verdict: not clean.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "not_assessed_by_reason": {
      +                "anyOf": [
      +                  {
      +                    "additionalProperties": {},
      +                    "properties": {
      +                      "advisory_lookup_failed": {
      +                        "type": "number"
      +                      },
      +                      "candidate_window_truncated": {
      +                        "type": "number"
      +                      },
      +                      "distro_release_unknown": {
      +                        "description": "deb, apk or rpm purls without a distro qualifier naming the release, which EchelonGraph does not guess.",
      +                        "type": "number"
      +                      },
      +                      "invalid_component": {
      +                        "type": "number"
      +                      },
      +                      "no_decidable_advisory": {
      +                        "type": "number"
      +                      },
      +                      "package_not_in_advisory_corpus": {
      +                        "type": "number"
      +                      },
      +                      "purl_type_unsupported": {
      +                        "type": "number"
      +                      },
      +                      "time_budget": {
      +                        "description": "Components the batch's time budget ran out before: not clean; check them again.",
      +                        "type": "number"
      +                      },
      +                      "version_missing": {
      +                        "type": "number"
      +                      }
      +                    },
      +                    "type": "object"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "The not_assessed components, counted by not_assessed_reason."
      +              },
      +              "partial": {
      +                "description": "true when the time budget ran out before every component was looked up.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "time_budget_ms": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "undetermined": {
      +                "description": "Components whose advisories could not all be decided and none matched: not clean.",
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              }
      +            },
      +            "type": "object"
      +          }
      +        },
      +        "type": "object"
      +      },
      +      "freshness": {
      +        "description": "The producing radar's last completed check (last_run_at), where the API serves one.",
      +        "type": "null"
      +      },
      +      "measured_at": {
      +        "anyOf": [
      +          {
      +            "description": "An RFC 3339 instant.",
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "description": "When the underlying observation was made, as the API states it; null when the answer does not say or holds no observation."
      +      },
      +      "method": {
      +        "description": "How the numbers were produced.",
      +        "type": "string"
      +      },
      +      "notes": {
      +        "description": "Caveats, one sentence each.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "state": {
      +        "description": "measured: a measurement of what was asked; an exposure count is measured only with measured_at and method. not_assessed: the answer holds no dated measurement of what was asked, so no count in it is presented as one; it can still relay a count, as what the source holds on record, undated, and its notes (and exposure_state, where the result carries it) say what each count is.",
      +        "enum": [
      +          "measured",
      +          "not_assessed"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "state",
      +      "measured_at",
      +      "method",
      +      "coverage",
      +      "freshness",
      +      "notes",
      +      "data"
      +    ],
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "coverage": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "properties": {
      +              "batch_size": {
      +                "description": "The most purls one request carries (the API's cap per request).",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches": {
      +                "description": "Requests the distinct purls make, at batch_size each.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "batches_sent": {
      +                "description": "Of those, the ones the API answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "components_in_document": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Components (CycloneDX) or packages (SPDX) in the document; null for a purl list."
      +              },
      +              "distinct_purls": {
      +                "description": "Distinct purls to check: sent plus not_sent.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "duplicates_removed": {
      +                "description": "Purls that appeared more than once and were sent once.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "input": {
      +                "description": "What was passed: a purl list, a CycloneDX JSON document or an SPDX JSON document.",
      +                "enum": [
      +                  "purls",
      +                  "cyclonedx",
      +                  "spdx"
      +                ],
      +                "type": "string"
      +              },
      +              "not_assessed": {
      +                "anyOf": [
      +                  {
      +                    "maximum": 9007199254740991,
      +                    "minimum": -9007199254740991,
      +                    "type": "integer"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Of those sent, the components with no verdict, as the answer counts them."
      +              },
      +              "not_sent": {
      +                "description": "Distinct purls not sent, listed in data.not_sent_purls: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "not_sent_reason": {
      +                "anyOf": [
      +                  {
      +                    "enum": [
      +                      "time_budget",
      +                      "rate_limited",
      +                      "request_failed"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  {
      +                    "type": "null"
      +                  }
      +                ],
      +                "description": "Why not_sent is above 0: time_budget (the call's 50 s budget ran out, or waiting out the API's Retry-After would pass it), rate_limited (a 429 without Retry-After, or a 429 after 10 waits), request_failed (a batch after the first failed; the note quotes how). null when every purl was sent."
      +              },
      +              "partial": {
      +                "description": "true when not every purl was sent (not_sent above 0) or any batch's summary.partial was true (its time budget ran out first); otherwise the answer's summary.partial.",
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "rate_limit_waits": {
      +                "description": "How many times the API answered 429 and the tool waited its Retry-After before sending the batch again.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "sent": {
      +                "description": "Distinct purls sent and answered.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "waited_ms": {
      +                "description": "Milliseconds spent in those waits.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "with_purl": {
      +                "description": "Of those, the ones carrying a purl.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              "without_purl": {
      +                "description": "The ones without a purl: not checked, and not clean.",
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              }
      +            },
      +            "required": [
      +              "input",
      +              "components_in_document",
      +              "with_purl",
      +              "without_purl",
      +              "duplicates_removed",
      +              "distinct_purls",
      +              "batch_size",
      +              "batches",
      +              "batches_sent",
      +              "sent",
      +              "not_sent",
      +              "not_sent_reason",
      +              "rate_limit_waits",
      +              "waited_ms",
      +              "not_assessed",
      +              "partial"
      +            ],
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ]
      +      },
      +      "error": {
      +        "additionalProperties": false,
      +        "properties": {
      +          "kind": {
      +            "description": "What failed: the request (network, timeout), the answer (http, not_json, not_object, unexpected_shape), the input, or this server.",
      +            "enum": [
      +              "network",
      +              "timeout",
      +              "http",
      +              "not_json",
      +              "not_object",
      +              "invalid_input",
      +              "internal",
      +              "unexpected_shape",
      +              "radars"
      +            ],
      +            "type": "string"
      +          },
      +          "message": {
      +            "description": "The cause: the API's own message, or what went wrong.",
      +            "type": "string"
      +          },
      +          "path": {
      +            "description": "The API path requested, when a request was made.",
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "status": {
      +            "anyOf": [
      +              {
      +                "maximum": 9007199254740991,
      +                "minimum": -9007199254740991,
      +                "type": "integer"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "description": "The HTTP status, when the API answered one."
      +          }
      +        },
      +        "required": [
      +          "kind",
      +          "path",
      +          "status",
      +          "message"
      +        ],
      +        "type": "object"
      +      },
      +      "freshness": {
      +        "type": "null"
      +      },
      +      "measured_at": {
      +        "type": "null"
      +      },
      +      "method": {
      +        "type": "null"
      +      },
      +      "notes": {
      +        "description": "Caveats, one sentence each.",
      +        "items": {
      +          "type": "string"
      +        },
      +        "type": "array"
      +      },
      +      "state": {
      +        "description": "failed: the lookup did not complete. invalid_input: the input was refused, so nothing was looked up. Neither is a finding.",
      +        "enum": [
      +          "failed",
      +          "invalid_input"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "state",
      +      "measured_at",
      +      "method",
      +      "coverage",
      +      "freshness",
      +      "notes",
      +      "error"
      +    ],
      +    "type": "object"
      +  }
      +]
  4. First observed

TDQS

A4/5.0
Behavior5/5

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

Annotations cover safety (readOnly/openWorld/idempotent), but the description discloses behavior they cannot: 1,200 components/minute rate limit, 429 Retry-After handling within a 50-second per-call budget, batching of 200 purls, partial-result semantics, and exactly what causes undetermined vs not_assessed. This is unusually rich disclosure of failure and truncation behavior.

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

Conciseness3/5

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

Purpose is front-loaded, but the remainder is one dense run-on paragraph of nested clauses with some redundancy ('not checked and not clean' appears twice). The complexity justifies length, yet headings or bullets would make the rate-limit, verdict and truncation rules far easier to parse.

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

Completeness5/5

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

For a two-param tool with an output schema, this covers everything an agent needs: input limits, batching, verdict taxonomy, partial-coverage fields, retry/stop conditions, and text-truncation behavior including which fields survive the cut. Nothing material is left to inference.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, but the description adds real meaning beyond the schema: purls are parsed from the document server-side and only they are sent, the document is never sent as a URL, and concrete limits (2,000 distinct purls, 5M chars, 6 MiB hosted) are given.

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

Purpose4/5

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

The opening states a specific verb and resource: 'Check a dependency list against EchelonGraph's advisory corpus, one verdict per component,' and the 'one verdict per component' scope distinguishes it from single-package siblings like check_affected. It never names a sibling directly, so differentiation is implicit rather than explicit.

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

Usage Guidelines3/5

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

It gives clear in-tool context: supply purls or an SBOM, and a list over 2,000 distinct purls is refused and must be split. However, it never states when to reach for check_sbom versus check_affected, so the agent must infer the batch-vs-single distinction.

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

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.