AI Bill of Materials (AIBOM)
AIBOM is your live inventory of every MCP server identity your agents have ever invoked, with per-tool risk overlay and a drift-advisory loop that fires when a new threat indicator matches something you already use.
If 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 surfaces it in the /app/aibom dashboard — before the agent's next tool call to that server.
Why an AI BOM is different from a software BOM
Traditional SBOM lists the libraries your application ships. AIBOM lists the MCP servers your agents reach out to at runtime — which is a fundamentally 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
- Risk in this space is about behavior (tool capabilities, network egress, credential reach) as much as vulnerabilities
AIBOM captures the runtime view: what your agents actually used, not what they could theoretically use.
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 — no single attribute is sufficient, because adversaries can spoof any one of them:
| Field | Source |
|---|---|
transport | stdio / HTTP / SSE |
publisher | npm/PyPI/Go registry publisher, or HTTP Server: header |
source | Package name + version, or hostname + path |
binary_hash | SHA-256 of the resolved binary (stdio) |
hostname | DNS name (HTTP/SSE) |
tls_cert_san | TLS certificate Subject Alternative Name (HTTP/SSE) |
A new (tenant, identity-tuple) pair creates one tool_inventory row. Subsequent invocations update last_seen_at + invocation_count only — 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|unratedrisk_category—arbitrary_code_execution,repo_access,cloud_admin,cred_exposure, etcrisk_rationale— short human-readable explanation surfaced in the dashboard tooltiprisk_references— structured links (CVE, vendor advisory, MITRITY review note)
The MITRITY security team maintains the catalog. Customer dashboards inherit the overlay automatically — you never write to it directly. If you spot a missing entry or disagree with a rating, ping 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 }
]
}
One advisory per (tenant, indicator). Re-firing the same indicator on the same tenant is idempotent (no duplicate). The advisory carries the full affected-inventory snapshot at fire time so even if you later archive an inventory entry, the historical advisory record stays accurate.
Indicator lifecycle
pending_review → soaking (24h) → active → archived
pending_review— Just curated. Not yet matching customer inventory.soaking— In a 24-hour quiet period. No drift advisories fire yet. Lets the curator catch mistakes before notifications go out.active— Matching customer inventory. Drift advisories fire on the next generator tick (every 5 minutes).archived— Manually retired. Historical advisories preserved.
Notification channels
Every drift advisory fans out across four channels in parallel (each independently configured per tenant):
- Email — Consolidated digest per tenant per tick, sent to
notification_preferences.email_recipients. SOC alias by default. - Slack — Block Kit message to
notification_preferences.slack_webhook_url. Severity emoji, advisory count, click-through to the drift feed. - Webhook — JSON envelope POSTed to every enabled
webhook_destinationsrow. SIEM-shaped — pair with Splunk HEC, syslog CEF (JSON for now), or your own webhook receiver. Carriesadvisory_id+indicator_idfor dedup. - In-portal bell — One row per advisory in the bell dropdown at the top-right of
/app/*. Per-user read state — Alice marking read doesn't hide it from Bob. Full list at/app/notifications.
A failed channel never blocks the others. Errors surface in the destination's last_error column for diagnosis.
See Notification Preferences for per-channel configuration.
The /app/aibom dashboard
Three views into the same data:
- By Tool — Sorted by
last_seen_at. Shows risk badge, agent count, invocation count, drift indicator if any active advisory matches. - By Agent — Pivot showing which tools each agent has used. Useful for "what does Agent X reach into?" audits.
- Drift Feed — 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, either:
- Update the agent's tool permissions to revoke that MCP server (
/app/agents/:id) - Edit the policy that allowed the action (
/app/policies/:id) - Or acknowledge + dismiss the advisory (it stays in the historical record)
API access
Programmatic access — same data, same drift events:
| Endpoint | Purpose |
|---|---|
GET /api/v1/aibom/inventory | List your tool inventory with risk overlay attached |
GET /api/v1/aibom/advisories | List drift advisories with filters (severity, acknowledged, time range) |
POST /api/v1/aibom/advisories/:id/acknowledge | Mark an advisory acknowledged |
Full schemas in the API Reference. The same JSON shape the webhook channel posts is what these endpoints return — write a single parser, use it everywhere.
Tenant isolation
Inventory rows are strictly tenant-scoped — your tool_inventory is invisible to any other tenant. 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.
The MITRITY security team has read access to the global threat catalog (so we can curate) but not to your inventory.
Where new indicators come from
For now, the threat indicator catalog is curated by the MITRITY security team via the admin portal. We're actively building an OSV.dev + CISA KEV auto-ingest pipeline that will:
- Pull vulnerability data from OSV.dev for every MCP server ecosystem (npm, PyPI, Go) — so a CVE drop for
@some-org/server-fooautomatically creates a matchingmcp_tool_riskindicator - Promote CVEs to critical when they appear in CISA's Known Exploited Vulnerabilities catalog
- Skip the manual curation step entirely for ecosystem-package CVEs
That sprint lands later — meanwhile, customer-reported issues route through soc@mitrity.com and become indicators within hours.