New: Agent approvals, red teaming and model scanning. See how
MITRITY
Docs / Security / AI Bill of Materials (AIBOM)

AI Bill of Materials (AIBOM)

AIBOM is your inventory of the parts of your AI system that came from somebody else. Two things fit that description, and a dependency manifest shows neither of them:

  • MCP servers: the tools your agents reach out to at runtime. AIBOM tracks them live, with a per-tool risk overlay and a drift-advisory loop that fires when a new threat indicator matches something you already use.
  • Models: the repositories your agents load weights, configuration and chat templates from, scanned for what is actually in those files. AIBOM also lists the hosted models your agents call through the LLM gateway, as they are used, and cross-checks them against your policies and the curated list of known-unsafe models.

Say a CVE drops tomorrow for git-mcp@1.2.3. AIBOM matches it against your tool inventory, fans the advisory out across your configured channels (email, Slack, webhook, in-portal bell) and shows it in the /app/aibom dashboard, before the agent's next tool call to that server. If a model repository ships a pickle that reaches os.system, model scanning reports it, with the byte offset to check it against.

Why an AI BOM is different from a software BOM

A traditional SBOM (software bill of materials) lists the libraries your application ships. AIBOM lists what your agents reach at runtime, and that is a moving target:

  • Agents discover new MCP servers via tool descriptions and natural-language reasoning
  • A single agent often uses dozens of MCP servers across its lifecycle
  • The MCP server's binary/identity can change without the agent code changing
  • A model repository is pulled by name, so a push to a branch changes what loads without a single line of your code changing
  • Risk in this space is about behavior (tool capabilities, network egress, credential reach, what a checkpoint executes on load) as much as vulnerabilities

AIBOM captures the runtime view: what your agents actually used, not what they could theoretically use.

The MCP server half

The rest of this page covers MCP servers: how their identity is captured, the risk overlay applied to it, and the drift advisories that fire when an indicator matches. The model half has its own page, Model Scanning, because the question there is different. It is not "which version is this, and is it known bad?" but "what is in these files, and what happens when they load?"

How identity is captured

The Mitrity Gateway and Mitrity MCP Sidecar emit a composite identity for every MCP server they speak to. Identity is multi-field, because adversaries can spoof any single attribute:

FieldSource
transportstdio / HTTP / SSE
publishernpm/PyPI/Go registry publisher, or HTTP Server: header
sourcePackage name + version, or hostname + path
binary_hashSHA-256 of the resolved binary (stdio)
hostnameDNS name (HTTP/SSE)
tls_cert_sanTLS certificate Subject Alternative Name (HTTP/SSE)

A new (tenant, identity-tuple) pair creates one tool_inventory row. Later invocations update only last_seen_at + invocation_count, so your inventory is deduplicated automatically.

{
  "id": "inv_8k4m",
  "transport": "stdio",
  "publisher": "github:modelcontextprotocol",
  "source": "@modelcontextprotocol/server-github@2024.11.25",
  "binary_hash": "sha256:a1f4...",
  "first_seen_at": "2026-04-12T08:00:00Z",
  "last_seen_at": "2026-06-05T13:42:18Z",
  "invocation_count": 1247,
  "display_name": "GitHub MCP",
  "risk_level": "medium",
  "risk_category": "repo_access"
}

The risk overlay

Each MCP identity carries a curated risk profile, applied platform-wide:

  • risk_level: critical | high | medium | low | info | unrated
  • risk_category: arbitrary_code_execution, repo_access, cloud_admin, cred_exposure, etc
  • risk_rationale: a short human-readable explanation, shown in the dashboard tooltip
  • risk_references: structured links (CVE, vendor advisory, MITRITY review note)

The MITRITY security team maintains the catalog. Customer dashboards inherit the overlay automatically, and you never write to it directly. If you spot a missing entry or disagree with a rating, contact the MITRITY soc@ alias, and the catalog is updated platform-wide.

Drift advisories

When a new mcp_tool_risk threat indicator matches your existing inventory, MITRITY fires a drift advisory for that (tenant, indicator) pair:

{
  "id": "adv_001",
  "tenant_id": "...",
  "indicator_id": "ind_cve_2026_5042",
  "indicator_name": "CVE-2026-5042",
  "indicator_severity": "high",
  "tool_count": 2,
  "agent_count": 7,
  "published_at": "2026-06-05T09:12:00Z",
  "affected_inventory": [
    { "tool_inventory_id": "inv_8k4m", "tool_display_name": "GitHub MCP", "agent_count": 7 }
  ]
}

There is one advisory per (tenant, indicator). Re-firing the same indicator on the same tenant is idempotent, so it creates no duplicate. The advisory carries the full affected-inventory snapshot at fire time, so the historical advisory record stays accurate even if you later archive an inventory entry.

Indicator lifecycle

pending_review  →  soaking (24h)  →  active  →  archived
  • pending_review: just curated. Not yet matching customer inventory.
  • soaking: a 24-hour alert-only period. Matches against customer inventory already produce drift advisories, so tenants see them before the indicator becomes active.
  • active: the soak is over. Matches against your inventory are recomputed every hour, and once more when an indicator is promoted from soaking.
  • archived: manually retired. Historical advisories are preserved.

Notification channels

Every drift advisory fans out across four channels in parallel, each configured independently per tenant:

  1. Email: a consolidated digest per tenant per tick, sent to notification_preferences.email_recipients. The SOC alias by default.
  2. Slack: a Block Kit message to notification_preferences.slack_webhook_url, with a severity emoji, the advisory count and a click-through to the drift feed.
  3. Webhook: a JSON envelope, sent as an HTTPS POST to every enabled webhook_destinations row. A Splunk HEC destination receives it inside the HEC event wrapper, with the sourcetype mitrity:aibom_drift. Every other destination type, syslog_cef included, receives the JSON envelope as it is: drift advisories are not formatted as CEF. The envelope carries advisory_id + indicator_id for dedup.
  4. In-portal bell: one row per advisory in the bell dropdown at the top-right of /app/*, with per-user read state: Alice marking it read doesn't hide it from Bob. The full list is at /app/notifications.

A failed channel never blocks the others. Errors show in the destination's last_error column for diagnosis.

See Notification Preferences for per-channel configuration.

The /app/aibom dashboard

The dashboard has three views into the same data:

  • By Tool: sorted by last_seen_at. It shows the risk badge, agent count, invocation count, and a drift indicator if an active advisory matches.
  • By Agent: a pivot showing which tools each agent has used. Useful for "what does Agent X reach into?" audits.
  • Drift Feed: a live timeline of advisories, newest first. Each row links to the matched inventory entry and the underlying threat indicator.

The dashboard is read-only. To act on a drift advisory, you can:

  • update the agent's tool permissions to revoke that MCP server (/app/agents/:id);
  • edit the policy that allowed the action (/app/policies/:id);
  • acknowledge and dismiss the advisory, which stays in the historical record.

The model half

Models live at Models, because they are a different kind of inventory with a different kind of result.

An MCP server is identified and then matched against a catalog of known-bad versions: the question is which one is this. A model repository is read. The scanner opens the pickle streams, the archive directories, the safetensors and GGUF headers, the model card, the configuration and the chat template, and reports what is in them. The question is what is in this file, and what happens when it loads.

That produces a different shape of finding. Each finding names the file and, where one exists, the byte offset, the opcode and the import target that the artifact reaches, so you can check the result against the file yourself. Findings are split into security, supply_chain, compliance and hygiene. The dashboard shows a hygiene finding differently from a security one, because the two are not the same kind of result.

Three properties are worth knowing before you rely on it:

  • Nothing is downloaded. Each format is read from the part of the file that carries its structure, so a thirty-gigabyte repository is scanned with about a megabyte of transfer.
  • Incomplete is never reported as clean. A file that the scan could not read to the extent its scanner needs marks the whole result incomplete, on the inventory row and on the repository.
  • A moving revision is stated plainly. A scan of a branch tells you about that branch on the day it ran, and the inventory says whether the repository is pinned to a commit.

Full detail: Model Scanning.

API access

The API gives programmatic access to the same data and the same drift events:

EndpointPurpose
GET /api/v1/aibomList your tool inventory with risk overlay attached
GET /api/v1/aibom/drift-feedList drift advisories with filters (severity, acknowledged, time range)
POST /api/v1/aibom/drift-feed/{id}/acknowledgeMark an advisory acknowledged
GET /api/v1/model-inventoryList your model repositories and hosted models with their open findings by severity; a hosted row carries the usage behind it
GET /api/v1/model-inventory/{id}One model, its findings and its latest scan run, or, for a hosted model, the agents that call it
POST /api/v1/model-inventory/{id}/scanStart a scan of one repository now

The full schemas are in the API Reference. The webhook channel posts its own envelope, which is not the shape these endpoints return. A webhook advisory's advisory_id is the advisory's id in the drift feed, so you can match the two.

Tenant isolation

Inventory rows are strictly tenant-scoped. Your tool inventory and your model repositories are invisible to any other tenant, and so are the findings a scan produces. The threat indicator catalog is global (one row per indicator, shared across all tenants), but matches are per-tenant. You only see advisories for indicators that hit your inventory.

Access tokens for gated or private model repositories are stored write-only. They are never returned by the API and never displayed in the dashboard. You can replace one or clear it, but you cannot read it back.

The MITRITY security team has read access to the global threat catalog, so that we can curate it, but not to your inventory.

Where new indicators come from

Indicators reach the catalog through three routes. Every route ends in the same review-then-soak lifecycle before an indicator starts firing.

  • Ecosystem advisories, ingested automatically. Once a day, MITRITY queries OSV.dev for every package on the curated list of known MCP servers, across the npm, PyPI, Go and crates.io ecosystems. Each advisory that matches becomes a candidate mcp_tool_risk indicator, carrying its CVE identifiers, the affected version ranges and a severity derived from the advisory's CVSS score. A vulnerability published for @some-org/server-foo is therefore on the review queue the next morning, without anyone filing it.
  • CISA Known Exploited Vulnerabilities. During the same run, every candidate's CVE identifiers are checked against CISA's Known Exploited Vulnerabilities catalog. A CVE listed there is promoted to critical and marked as actively exploited, with the date CISA added it. Re-ingestion never lowers a severity that MITRITY's security team has set by hand. A promotion on an indicator that is already live sends it back through the alert-only window described below, before it enforces at the higher severity.
  • Reports to the security team. Anything that is not an ecosystem advisory goes to soc@mitrity.com: a malicious tool description, a compromised server, a package that is not on the curated list yet. The security team curates these by hand and adds the resulting indicator to the catalog, typically within hours.

Whatever the route, a new indicator does not enforce immediately:

  1. It waits in the review queue until the security team approves it.
  2. It then runs alert-only for 24 hours, so tenants see the matches before any action is taken.
  3. Only then does it become active.

Matches against your inventory are recomputed every hour. That is when a newly active indicator produces the drift advisories and notifications described above.