# KernelScan

> Linux kernel CVE analyzer: upload a .config, get a CycloneDX VEX report of affecting CVEs.

- name: io.kernelscan/kernelscan
- version: 1.0.0
- connection class: R0 (autonomous)
- trust: 76/100 flags: no-repository
- quality: 76/100 (good)
- owner: not claimed
- descriptor: https://api.protogrid.dev/v1/servers/io.kernelscan%2Fkernelscan
- tools: https://api.protogrid.dev/v1/servers/io.kernelscan%2Fkernelscan/tools

**An agent can connect right now, no human step.** Remote endpoint, no authentication, reachable on the last probe.

## Connection (mcpServers)

```json
{
  "mcpServers": {
    "kernelscan": {
      "type": "http",
      "url": "https://kernelscan.io/mcp/",
      "headers": {
        "Authorization": "Bearer ${KERNELSCAN_TOKEN}"
      }
    }
  }
}
```

Placeholders to fill: `${KERNELSCAN_TOKEN}`.

## Trust

- hygiene: 80
- liveness: 100
- freshness: 65
- provenance: 55
- drivers: +dns-namespace +website +reachable +uptime-100% ~updated-121d-ago -no-repository
- Derived from observable signals (official registry feed and our own credential-free probes); no code audit performed.

## Quality 76/100 (good)

### protocol: 75 (weight 25)

- warn `protocol.modern`: newest supported version is 2025-11-25; 2026-07-28 not supported, older versions are deprecated until 2027-07-28
- n/a `protocol.stateless`: only applies to 2026-07-28 servers
- pass `protocol.transport`: streamable HTTP
- n/a `protocol.list_ttl`: only applies to 2026-07-28 servers

### authorization: n/a (weight 15)

- n/a `auth.prm`, `auth.as_metadata`, `auth.cimd`: no remote uses OAuth
- n/a `auth.secret_in_url`: no templated URL

### tool hygiene: 64 (weight 30)

- pass `tools.descriptions`: every tool has a description
- warn `tools.description_length`: 4 tools have descriptions over 1,024 characters, which crowds the context
- pass `tools.schemas`: every tool has an object input schema
- fail `tools.annotations`: no tool declares readOnlyHint or destructiveHint, so clients cannot tell safe calls from risky ones
- fail `tools.directory_hints`: no tool declares all four of readOnlyHint, destructiveHint, idempotentHint and openWorldHint; missing: create_product (readOnlyHint, destructiveHint, idempotentHint, openWorldHint); search_cves (readOnlyHint, destructiveHint, idempotentHint, openWorldHint); get_cve (readOnlyHint, destructiveHint, idempotentHint, openWorldHint); …
- pass `tools.token_cost`: about 3,898 tokens to load every tool
- pass `tools.api_dump`: tools are not a one-to-one API dump

### stability: 100 (weight 15)

- pass `stability.changes`: 3 tool changes in 30 days
- pass `stability.rug_pull`: no tool changed its meaning under the same name

### dependencies: n/a (weight 15)

- n/a `deps.known_vulns`, `deps.mcp_sdk_version`, `deps.resolvable`: no npm or PyPI package

- tools: 11, about 3,898 tokens to load them all
- tool set hash: 3bd88091eacb0c6d8676597305b78b34
- tools last changed: 2026-10-06T20:51:49.722Z
- badge: [![protogrid quality](https://protogrid.dev/badge/io.kernelscan/kernelscan.svg)](https://protogrid.dev/servers/io.kernelscan/kernelscan)
- Checks run on what our credential-free, read-only probes observe; tools are never called and no code audit is performed.

### Tool changes

- 2026-10-06 `update_product` changed (description)
  - before: Update a product owned by the caller. Re-runs analysis if the kernel_version, arch, or referenced .config changed. To change the .config, first POST the new file to ``/api/configs/uploads`` (see ``create_product`` for the curl recipe) and pass the returned ``config_upload_id`` here. Leave ``config_upload_id`` as ``None`` to keep the existing .config. ``factor_ids=None`` leaves factor selections untouched; an empty list clears them. Same tier gates as PUT /api/products/{id}. A change that re-runs analysis (``kernel_version``, ``arch``, or the ``.config``) spends one unit of the team's shared monthly analysis allowance and can fail with the same durable "Monthly analysis limit reached … [429]" quota error as ``create_product`` (distinct from the transient rate-limit 429 — don't retry it). A rename / description / factor-only edit runs no analysis and is free. ``kernel_version`` takes the same forms as in ``create_product``: a stable version, or a CIP release / full ``uname -r`` such as ``4.19.325-cip136-rt50``, whose ``-cipN`` counter decides which CIP fixes apply. A changed version string the analysis cannot read fails with 400 and changes nothing; re-sending the stored value is always accepted. A CIP release must stay on the base its ``.config`` header names: whichever of the two the update changes is checked against the other ([400], before the upload is consumed).
  - after: Update a product owned by the caller. Re-runs analysis if the kernel_version, arch, or referenced .config changed. To change the .config, first POST the new file to ``/api/configs/uploads`` (see ``create_product`` for the curl recipe) and pass the returned ``config_upload_id`` here. Leave ``config_upload_id`` as ``None`` to keep the existing .config. ``factor_ids=None`` leaves factor selections untouched; an empty list clears them. Same tier gates as PUT /api/products/{id}. A change that re-runs analysis (``kernel_version``, ``arch``, or the ``.config``) spends one unit of the team's shared monthly analysis allowance and can fail with the same durable "Monthly analysis limit reached … [429]" quota error as ``create_product`` (distinct from the transient rate-limit 429 — don't retry it). A rename / description / factor-only edit runs no analysis and is free; the product's VEX is re-evaluated against the new factors in the background (not metered), so ``get_product`` / ``get_product_vex`` show the new verdicts a few seconds later. ``kernel_version`` takes the same forms as in ``create_product``: a stable version, or a CIP release / full ``uname -r`` such as ``4.19.325-cip136-rt50``, whose ``-cipN`` counter decides which CIP fixes apply. A changed version string the analysis cannot read fails with 400 and changes nothing; re-sending the stored value is always accepted. A CIP release must stay on the base its ``.config`` header names: whichever of the two the update changes is checked against the other ([400], before the upload is consumed).
- 2026-10-04 `update_product` changed (description)
  - before: Update a product owned by the caller. Re-runs analysis if the kernel_version, arch, or referenced .config changed. To change the .config, first POST the new file to ``/api/configs/uploads`` (see ``create_product`` for the curl recipe) and pass the returned ``config_upload_id`` here. Leave ``config_upload_id`` as ``None`` to keep the existing .config. ``factor_ids=None`` leaves factor selections untouched; an empty list clears them. Same tier gates as PUT /api/products/{id}. A change that re-runs analysis (``kernel_version``, ``arch``, or the ``.config``) spends one unit of the team's shared monthly analysis allowance and can fail with the same durable "Monthly analysis limit reached … [429]" quota error as ``create_product`` (distinct from the transient rate-limit 429 — don't retry it). A rename / description / factor-only edit runs no analysis and is free.
  - after: Update a product owned by the caller. Re-runs analysis if the kernel_version, arch, or referenced .config changed. To change the .config, first POST the new file to ``/api/configs/uploads`` (see ``create_product`` for the curl recipe) and pass the returned ``config_upload_id`` here. Leave ``config_upload_id`` as ``None`` to keep the existing .config. ``factor_ids=None`` leaves factor selections untouched; an empty list clears them. Same tier gates as PUT /api/products/{id}. A change that re-runs analysis (``kernel_version``, ``arch``, or the ``.config``) spends one unit of the team's shared monthly analysis allowance and can fail with the same durable "Monthly analysis limit reached … [429]" quota error as ``create_product`` (distinct from the transient rate-limit 429 — don't retry it). A rename / description / factor-only edit runs no analysis and is free. ``kernel_version`` takes the same forms as in ``create_product``: a stable version, or a CIP release / full ``uname -r`` such as ``4.19.325-cip136-rt50``, whose ``-cipN`` counter decides which CIP fixes apply. A changed version string the analysis cannot read fails with 400 and changes nothing; re-sending the stored value is always accepted. A CIP release must stay on the base its ``.config`` header names: whichever of the two the update changes is checked against the other ([400], before the upload is consumed).
- 2026-10-04 `create_product` changed (description)
  - before: Create a new product, run analysis, and return its initial stats. ``config_upload_id`` references a previously-staged .config that the caller POSTed to ``/api/configs/uploads`` over plain HTTP — the LLM does NOT emit the config text itself (a real kernel .config is ~100–200 KB and exceeds a single tool-call output budget). Workflow: 1. Caller / wrapper script: ``curl -H "Authorization: Bearer ks_live_..." \ -F "config_file=@.config" \ https://kernelscan.io/api/configs/uploads`` returns ``{config_upload_id, sha256, size_bytes, expires_at}``. 2. Pass that ``config_upload_id`` into this tool. Uploads are per-user, single-use, and expire 30 minutes after upload. Same gates as POST /api/products: free can't create products; paid plans are capped at their resolved product limit — read it (and any per-account override) from ``whoami.product_limit`` rather than assuming a fixed per-tier number. ``factor_ids`` are silently ignored unless the plan allows security factors (``whoami.can_use_factors``). Re-using a product name returns 409. Creating a product RUNS an analysis, so it spends one unit of the team's SHARED monthly analysis allowance (``whoami.monthly_analyses_used`` / ``monthly_analyses_limit``). When the allowance is exhausted the tool fails with "Monthly analysis limit reached (…/month) [429]". This is a durable monthly quota — NOT the transient per-call rate limit that also surfaces as 429: it will not clear until next month, so report it to the user instead of retrying. Check ``whoami`` before a batch of creates.
  - after: Create a new product, run analysis, and return its initial stats. ``config_upload_id`` references a previously-staged .config that the caller POSTed to ``/api/configs/uploads`` over plain HTTP — the LLM does NOT emit the config text itself (a real kernel .config is ~100–200 KB and exceeds a single tool-call output budget). Workflow: 1. Caller / wrapper script: ``curl -H "Authorization: Bearer ks_live_..." \ -F "config_file=@.config" \ https://kernelscan.io/api/configs/uploads`` returns ``{config_upload_id, sha256, size_bytes, expires_at}``. 2. Pass that ``config_upload_id`` into this tool. Uploads are per-user, single-use, and expire 30 minutes after upload. Same gates as POST /api/products: free can't create products; paid plans are capped at their resolved product limit — read it (and any per-account override) from ``whoami.product_limit`` rather than assuming a fixed per-tier number. ``factor_ids`` are silently ignored unless the plan allows security factors (``whoami.can_use_factors``). Re-using a product name returns 409. Creating a product RUNS an analysis, so it spends one unit of the team's SHARED monthly analysis allowance (``whoami.monthly_analyses_used`` / ``monthly_analyses_limit``). When the allowance is exhausted the tool fails with "Monthly analysis limit reached (…/month) [429]". This is a durable monthly quota — NOT the transient per-call rate limit that also surfaces as 429: it will not clear until next month, so report it to the user instead of retrying. Check ``whoami`` before a batch of creates. ``kernel_version`` is the kernel's release. For a stable kernel that is its version (``6.6.67``). For a CIP SLTS kernel pass its CIP release or the full ``uname -r``: ``4.19.325-cip136``, ``4.19.325-cip136-rt50``; a trailing local suffix such as ``-yocto-standard`` is accepted and ignored. The ``-cipN`` counter decides which of CIP's own backported fixes apply, so pass it whenever the device runs a CIP kernel — a bare ``4.19.325`` is analysed as the final 4.19 stable release (the ``.config`` never names the ``-cipN``). The known CIP releases of a series are listed by ``GET /api/kernel-releases?flavor=cip&series=4.19`` (add ``&rt=1`` for the RT tree). The returned ``kernel_release`` shows how the string was read (``flavor``, ``base``, ``series``, ``cip``, ``rt``, ``local``; null when the string is no release). Surrounding whitespace is trimmed. A version string the analysis cannot read (e.g. ``4.19.325cip136``, or anything non-ASCII) fails with "Unrecognized kernel version" [400] before anything is created or the upload is consumed. A CIP release must be on the stable base the uploaded ``.config``'s header names (the header shows the exact base; a CIP tag never changes it): ``4.19.300-cip90`` with a ``4.19.325`` config fails with "… is based on 4.19.300, but the .config header says 4.19.325" [400]. This is checked before the upload is consumed, so the same upload can be used again with the right release. A stable version, or a ``.config`` without a version header, is not checked.

Full history: https://api.protogrid.dev/v1/servers/io.kernelscan%2Fkernelscan/changes

## Remotes

- https://kernelscan.io/mcp/ (streamable-http, auth none, reachable true, uptime30d 1)

## Packages

_none_

## Tools (11)

- `create_product`: Create a new product, run analysis, and return its initial stats.

        ``config_upload_id`` references a previously-staged .config that the
        caller POSTed to ``/api/configs/uploads`` over plain HTTP — the LLM
        does NOT emit the config text itself (a real kernel .config is
        ~100–200 KB and exceeds a single tool-call output budget). Workflow:

        1. Caller / wrapper script:
           ``curl -H "Authorization: Bearer ks_live_..." \
                  -F "config_file=@.config" \
                  https://kernelscan.io/api/configs/uploads``
           returns ``{config_upload_id, sha256, size_bytes, expires_at}``.
        2. Pass that ``config_upload_id`` into this tool.

        Uploads are per-user, single-use, and expire 30 minutes after upload.
        Same gates as POST /api/products: free can't create products; paid
        plans are capped at their resolved product limit — read it (and any
        per-account override) from ``whoami.product_limit`` rather than assuming
        a fixed per-tier number. ``factor_ids`` are silently ignored unless the
        plan allows security factors (``whoami.can_use_factors``). Re-using a
        product name returns 409.

        Creating a product RUNS an analysis, so it spends one unit of the
        team's SHARED monthly analysis allowance (``whoami.monthly_analyses_used``
        / ``monthly_analyses_limit``). When the allowance is exhausted the tool
        fails with "Monthly analysis limit reached (…/month) [429]". This is a
        durable monthly quota — NOT the transient per-call rate limit that also
        surfaces as 429: it will not clear until next month, so report it to the
        user instead of retrying. Check ``whoami`` before a batch of creates.

        ``kernel_version`` is the kernel's release. For a stable kernel that is
        its version (``6.6.67``). For a CIP SLTS kernel pass its CIP release or
        the full ``uname -r``: ``4.19.325-cip136``, ``4.19.325-cip136-rt50``;
        a trailing local suffix such as ``-yocto-standard`` is accepted and
        ignored. The ``-cipN`` counter decides which of CIP's own backported
        fixes apply, so pass it whenever the device runs a CIP kernel — a bare
        ``4.19.325`` is analysed as the final 4.19 stable release (the
        ``.config`` never names the ``-cipN``). The known CIP releases of a
        series are listed by ``GET /api/kernel-releases?flavor=cip&series=4.19``
        (add ``&rt=1`` for the RT tree). The returned
        ``kernel_release`` shows how the string was read (``flavor``, ``base``,
        ``series``, ``cip``, ``rt``, ``local``; null when the string is no
        release). Surrounding whitespace is trimmed. A version string the
        analysis cannot read (e.g. ``4.19.325cip136``, or anything non-ASCII)
        fails with "Unrecognized kernel version" [400] before anything is
        created or the upload is consumed.

        A CIP release must be on the stable base the uploaded ``.config``'s
        header names (the header shows the exact base; a CIP tag never changes
        it): ``4.19.300-cip90`` with a ``4.19.325`` config fails with "… is
        based on 4.19.300, but the .config header says 4.19.325" [400]. This is
        checked before the upload is consumed, so the same upload can be used
        again with the right release. A stable version, or a ``.config``
        without a version header, is not checked.
        
- `get_cve`: Fetch a single Linux kernel CVE by ID (e.g. ``CVE-2024-12345``).

        No API key required: keyless callers get the public representation of a
        CVE, but only for CVEs in the public set (recent high-severity); any
        other id returns ``not found``. Free *keyed* callers get a 404 for CVEs
        published more than 60 days ago. AI risk-summary / analysis fields are
        included for any keyed user on CVEs in the public set, and for pro /
        enterprise on every assessed CVE.
        
- `get_product`: Fetch one product owned by the caller, including the CVE breakdown.

        Returns 404 (not 403) if the product belongs to another user, so
        product existence isn't leaked across accounts.
        
- `get_product_vex`: Return the VEX MANIFEST for one of the caller's products — metadata,
        not the document.

        Multi-megabyte CycloneDX documents (up to 8,000+ vulnerability entries)
        are not safe model-context payloads, so this tool returns a bounded
        manifest: CycloneDX format/spec version, product id, generated/expires
        timestamps, the VEX hash and the composite ETag identity, the
        uncompressed size in bytes, total + per-status vulnerability counts,
        and the authenticated REST download path. Reads from the 24h
        ProductVexCache; if the cache is empty/expired the next call to
        ``get_product`` (or the REST endpoint) will regenerate it.

        The MANIFEST carries the same ``kernelscan.io:exploit_maturity`` /
        ``kernelscan.io:kev`` overlay identity as the REST download
        (backend#337), so ETags compare across transports.

        To inspect the entries themselves, use ``list_product_vex_entries``.
        To retrieve the COMPLETE CycloneDX document, use the authenticated
        REST endpoint ``GET /api/products/{product_id}/vex`` (same ks_live_
        key) — that is the canonical way to retrieve the full artifact; no
        MCP tool returns it.
        
- `list_products`: List the calling user's products with denormalized analysis stats.

        Paid plans only (basic / pro / enterprise). Free callers get a clear
        upgrade message.
        
- `list_product_vex_entries`: Page through the VEX vulnerability entries of one of the caller's products.

        Returns COMPLETE CycloneDX vulnerability objects for one bounded page
        — never the root document, never all entries — plus ``returned``,
        ``total_matching``, and a ``next_cursor`` to continue with. Every
        result is bounded to 256 KiB: the page stops before the byte limit and
        returns a cursor when necessary. Entries are ordered by CVE id
        (ascending) and carry the same live ``kernelscan.io:exploit_maturity``
        / ``kernelscan.io:kev`` properties as the REST download (backend#337).

        Filters (all optional, combinable):
        - ``statuses``: ``affected`` / ``not_affected`` / ``in_triage``
        - ``severities``: ``critical`` / ``high`` / ``medium`` / ``low`` / ``none``
        - ``kev``: true/false — CISA KEV listing only / non-KEV only
        - ``exploit_maturity``: ``poc`` / ``weaponized``
        - ``cve_ids``: exact-match list of CVE ids

        ``limit`` defaults to 25, maximum 100. ``cursor`` is the opaque
        continuation token from a previous page — it is tied to the product,
        the active filters, AND the current document + threat-overlay
        revision: changing filters, a regenerated cache, or a KEV/PoC signal
        that moved since the last page invalidates it (start a fresh page
        without a cursor — re-using a stale one is rejected, never silently
        re-applied). Within one revision, concatenating all pages yields
        each matching CVE exactly once.

        The COMPLETE multi-megabyte CycloneDX document is served by the
        authenticated REST endpoint ``GET /api/products/{product_id}/vex``
        (same ks_live_ key) — the canonical way to retrieve the full artifact.
        
- `request_access`: Explain how to get a KernelScan account (no API key needed).

        Self-registration is open — there is no invitation to wait for and no
        admin in the loop. The user signs up on kernelscan.io themselves (email
        + password, accepting the terms), confirms the verification mail, and
        mints a ks_live_ API key on their account page.

        This tool only hands that path back: it files nothing and sends no
        mail. ``email`` / ``name`` / ``reason`` are still accepted so older
        clients don't break, but they are ignored — never tell the user that a
        request was submitted on their behalf.
        
- `search_cves`: Search Linux kernel CVEs.

        No API key required: keyless callers get the free public tier — recent
        high-severity Linux kernel CVEs (capped at 25 results). Free *keyed*
        callers see only CVEs published in the last 60 days; basic+ keyed
        callers get the full corpus.
        ``query`` matches against CVE id and description (case-insensitive).
        ``severity`` filters by effective severity (``critical``/``high``/``medium``/``low``).
        ``cvss_min`` filters by effective CVSS score.
        ``published_after`` (ISO 8601) returns only CVEs newer than that date.
        Returns up to ``limit`` (max 100) CVEs, newest first.
        
- `submit_support_report`: Send a support / dispute report to KernelScan staff.

        Use this when an automated CVE or factor assessment looks wrong, or
        when you need to hand human-needed context back to the team. The
        caller's API-key user is attached automatically (id, email, plan)
        so support can look the account up.

        ``category`` should be one of:
        - ``cve_assessment`` — wrong AI verdict / CVSS / CWE on a CVE
        - ``factor_assessment`` — wrong factor verdict for a product
        - ``bug`` — broken behavior in the API or UI
        - ``other`` — anything else

        ``cve_id`` / ``product_id`` / ``assessment_id`` are optional but
        recommended — they let support jump straight to the relevant row.
        
- `update_product`: Update a product owned by the caller. Re-runs analysis if the
        kernel_version, arch, or referenced .config changed.

        To change the .config, first POST the new file to
        ``/api/configs/uploads`` (see ``create_product`` for the curl recipe)
        and pass the returned ``config_upload_id`` here. Leave
        ``config_upload_id`` as ``None`` to keep the existing .config.
        ``factor_ids=None`` leaves factor selections untouched; an empty
        list clears them. Same tier gates as PUT /api/products/{id}.

        A change that re-runs analysis (``kernel_version``, ``arch``, or the
        ``.config``) spends one unit of the team's shared monthly analysis
        allowance and can fail with the same durable "Monthly analysis limit
        reached … [429]" quota error as ``create_product`` (distinct from the
        transient rate-limit 429 — don't retry it). A rename / description /
        factor-only edit runs no analysis and is free; the product's VEX is re-evaluated
        against the new factors in the background (not metered), so ``get_product`` /
        ``get_product_vex`` show the new verdicts a few seconds later.

        ``kernel_version`` takes the same forms as in ``create_product``: a
        stable version, or a CIP release / full ``uname -r`` such as
        ``4.19.325-cip136-rt50``, whose ``-cipN`` counter decides which CIP
        fixes apply. A changed version string the analysis cannot read fails
        with 400 and changes nothing; re-sending the stored value is always
        accepted. A CIP release must stay on the base its ``.config`` header
        names: whichever of the two the update changes is checked against the
        other ([400], before the upload is consumed).
        
- `whoami`: Return the caller's identity, plan, and quota state.

        Works without an API key: keyless callers get a lightweight public-tier
        payload (no account) describing how to request access.
        
