The Rabbit Hole Generator gave me agent-bom this week, and it landed on a question I’ve been circling for a while: what does a vulnerability scanner actually owe you once “AI agent” and “MCP server” show up in your stack next to the usual packages and containers?

A traditional scanner tells you a package has a CVE. It stops there. It doesn’t know that package sits inside an MCP server (a small local process that gives an AI agent tools like “read this file” or “run this shell command”), and it definitely doesn’t know that server has an AWS_SECRET_ACCESS_KEY sitting in its environment. agent-bom is built specifically to answer the next question a CVE list never does: from this vulnerable package, what can actually be reached?


What is agent-bom used for in AI infrastructure security?

agent-bom is an open-source scanner that inventories AI agents, the MCP servers they call, the packages those servers depend on, and the credentials sitting in scope, then draws the connections between all of it. Instead of a flat list of CVEs, it produces a chain: vulnerable package leads to the server or agent that uses it, leads to the credentials and tools that server can reach. It runs as a local CLI against a repository, a container image, or an MCP configuration file, with an optional self-hosted dashboard for teams that want scheduled scans and a shared view across a fleet.


The Architecture: reachability, not just detection

The core bet agent-bom makes is that a flat CVE list is the wrong output shape for this problem. Its internal model is a chain: package instance → vulnerability → the service or runtime component that uses it → the tools, permissions, and credential environment variable names that component can reach → the agents connected to it. That chain is what it calls blast radius, and it’s the thing a traditional SBOM (Software Bill of Materials, a package inventory) or SCA (Software Composition Analysis, a dependency-vulnerability scanner) tool doesn’t compute, because they stop at the artifact boundary. A vulnerable npm package is a hygiene issue. The same package inside an MCP server that exposes a shell-execution tool to an agent holding a live database credential is a different category of problem, and agent-bom’s whole design is making that difference visible instead of burying it in a CVE count.

It ships two entry points that share the same evidence model. The CLI (agent-bom scan) reads a target directly: a repo, an MCP config, a Kubernetes manifest, an SBOM file. No account or server required. The self-hosted control plane (agent-bom serve) adds scheduled scans, a shared dashboard, ownership assignment, and connections to cloud providers like AWS or GCP, but it’s explicitly the growth path, not a prerequisite: the CLI alone is meant to prove value on day one.


The Reality Check: where this breaks

Six distinct problems worth naming before you reach for this on real infrastructure. The first three are scale limits; the next two are assumptions baked into how the tool builds its evidence graph; the last is the vulnerability the tool’s own design introduces.

1. Endpoint discovery depends on local workstation state

If agent-bom finds your MCP servers and agent configs by reading local files (Cursor’s config, a .mcp.json, whatever’s on disk), fleet coverage gets inconsistent the moment your environment isn’t uniform: a laptop with a stale config, an ephemeral CI runner that never had one, a managed VDI image that was provisioned differently. The tool can only inventory what it can see, and “what it can see” varies by machine in ways a security team doesn’t always control.

2. Graph enrichment doesn’t stay cheap at real scale

The evidence graph spans package metadata, inventory files, container layers, infrastructure-as-code, and runtime evidence all at once. That’s the whole value proposition, but it also means scan latency and evidence deduplication get expensive fast once you’re pointing this at large monorepos or multi-account cloud estates instead of a single first-run demo. A tool that’s fast against one repo isn’t automatically fast against an organization.

3. Remediation priority is only as good as the inventory behind it

Fix-ordering here comes from inferred blast radius, which means a stale inventory or a partial agent-to-server mapping doesn’t just under-report, it produces false confidence in what to fix first. A tool telling you “fix this one, it clears the most exposure” is trustworthy exactly as far as its map of your infrastructure is current. That’s a harder property to verify than a CVE database being up to date.

4. It assumes MCP and credential metadata are discoverable in a stable, parseable shape

The whole reachability chain depends on being able to parse MCP server definitions, figure out which tools they expose, and recognize credential environment variable names by pattern. That’s a reasonable bet against today’s ecosystem, but MCP tooling is young and configuration formats aren’t settled. A server that names its secret BROWSER_SESSION_TOKEN gets caught by a sensible naming heuristic; one that names it something the heuristic doesn’t expect might not.

5. It assumes what it can see locally resembles what’s actually running in production

A repo checkout or a CI-visible inventory is a snapshot of intent, not a guarantee of runtime topology. The MCP server defined in a config file might not be the one actually deployed, or might be running with a different set of environment variables than what’s checked in. Local and CI-visible evidence is the input agent-bom has to work with, and it’s honest about that boundary in its own docs, but it’s worth restating plainly: this is what the code says should be true, not a live observation of what is true.

6. Introduced Vulnerability

Centralizing credential environment variable names, agent-to-server relationships, tool capabilities, and vulnerable-component mappings into one searchable evidence store creates exactly the kind of high-value target a security tool is supposed to reduce, not add. If the control plane’s access control or tenant isolation has a gap, that gap now exposes a curated map of where every credential and every reachable privileged tool lives across your AI infrastructure. The tool that shows you your blast radius becomes, if compromised, a blast-radius shortcut for someone else.


The 30-Minute Lab: scanning a synthetic AI stack and reading the blast radius

The generator’s challenge asked for a 30-minute lab producing a scan session with at least one CVE, one exposed credential name, and one risky tool name, all in the same output, plus one sentence naming what you’d patch first. I ran it in a throwaway Docker container instead of on my own machine, following this series’ usual rule: contained by default, nothing installed against shared state.

1. Installation, and the command that had already changed

docker run --rm -it python:3.12-slim bash
pip install agent-bom

Straightforward, and it matched the README on PyPI without any surprises. The friction showed up one step later. Notion’s generated challenge said to run:

agent-bom agents --inventory agent-bom-first-run/inventory.json -p agent-bom-first-run --enrich

That errors. agents is still a real subcommand, but it doesn’t take an --inventory flag the way the challenge assumed. The tool’s own CLI has moved on since this week was generated back in May: the current version is v0.103.2, actively maintained (pushed to main the same week I’m writing this), and the scan subcommand absorbed what the challenge expected agents to do. I only found the right command because agent-bom samples first-run, when you run it, prints its own suggested next step in the output:

agent-bom scan --inventory agent-bom-first-run/inventory.json -p agent-bom-first-run --enrich

That’s the actual fix: swap agents for scan. Nothing else about the challenge’s intent was wrong, the tool’s CLI surface had just moved underneath it in four months. Worth remembering any time you’re following a generated (or AI-written) set of setup instructions against a fast-moving tool: verify the command against the tool’s own current output before assuming it’s yours that’s broken.

2. Generating the synthetic environment

agent-bom samples first-run

This writes nine files into agent-bom-first-run/: a synthetic Cursor workspace config, a Claude Desktop-style agent definition, and a couple of small MCP server implementations (browser-helper, research-mcp) with real, deliberately outdated dependencies pinned in their manifests. It’s a curated fixture, not a fake success screen. Nothing here is my real infrastructure; it’s a small, reproducible AI stack built specifically to demonstrate the reachability chain the tool exists to compute.

3. Running the scan, and what the output actually proves

agent-bom scan --inventory agent-bom-first-run/inventory.json -p agent-bom-first-run --enrich

This one hit live vulnerability feeds (OSV, GHSA, and NVD) plus EPSS and KEV enrichment over the network, not a bundled offline sample, so the exact CVE list below is what those feeds actually returned the day I ran it, not a fixed demo output:

────────────────────────────────── Discovery ───────────────────────────────────
  ✓ 2 agent(s) from agent-bom-first-run/inventory.json
  ✓ agent-bom-first-run: 5 package(s) across 2 manifest directories
  Trust: agent-system-prompt.md → BENIGN (1 fail, 3 warn)

────────────────────────────── Vulnerability Scan ──────────────────────────────
  ⚠ Found 42 vulnerabilities across 42 findings
  ✓ EPSS: 44 CVE(s)   ✓ KEV: no actively exploited CVEs
  ⚠ Scan complete — package CVEs: 2 critical · 15 high · 20 medium · 5 low

─────────────────────────────────── Summary ────────────────────────────────────
   Security posture:   CRIT 2   HIGH 21   MED 20   LOW 5
   3 agents · 3 servers · 9 packages
   Posture grade:   F   53/100   Top driver: Credential Hygiene

┌────────┬────────────────┬──────────────┬─────────────────────────────┬──────┐
│ Sev    │ Vulnerability  │ Package      │ Blast Radius                │ EPSS │
├────────┼────────────────┼──────────────┼─────────────────────────────┼──────┤
│  CRIT  │ CVE-2025-27152 │ [email protected] │ Cursor First-Run+1 →        │  0%  │
│        │                │              │ browser-helper →            │      │
│        │                │              │ BROWSER_SESSION_TOKEN       │      │
│  CRIT  │ CVE-2023-30861 │ [email protected]  │ Cursor First-Run+1 →        │  1%  │
│        │                │              │ research-filesystem         │      │
└────────┴────────────────┴──────────────┴─────────────────────────────┴──────┘

  Exiting with code 1: found critical vulnerability (CVE-2025-27152)

A few things in this output are worth stopping on, because pasting it and moving on would skip the actual point of the tool:

The Blast Radius column is the whole product. CVE-2025-27152 is a real, current axios vulnerability (an SSRF and credential-leak bug in how axios follows redirects). A dependency scanner would list it and stop. Here, the chain reads [email protected] → browser-helper → BROWSER_SESSION_TOKEN: the vulnerable package sits inside the browser-helper MCP server, and that server holds a session token in its environment. That’s the difference this whole post has been building toward: not “this package is vulnerable,” but “this package is vulnerable and it can reach a credential.” Compare that to CVE-2023-30861 in the same table, a Flask CVE with no credential at the end of its chain: same severity label, meaningfully different urgency once you can see what’s downstream.

EPSS is a likelihood score, not a severity score. EPSS (Exploit Prediction Scoring System) estimates the probability a given CVE gets exploited in the wild in the next 30 days, based on real observed exploitation activity across the internet. Both CVEs above show EPSS in the low single digits, meaning neither is currently being mass-exploited, which is a genuinely different signal from “critical” severity. Severity says how bad it would be. EPSS says how likely it is to happen. A CRIT finding with 0% EPSS and a real credential at the end of its blast radius is still worth fixing first, but not because it’s about to be exploited at scale; it’s worth fixing because of what it reaches, which is exactly the number the traditional CVSS severity score can’t tell you.

Exit code 1 is a verdict, not a crash. The scan finished cleanly and printed a complete report, then exited non-zero because a critical finding matched a gate. If you’re wiring this into CI, that distinction matters: a non-zero exit here means “the scan worked and found something,” not “the scan broke.” The tool’s own docs are explicit about this exact point, which tells me it’s a common enough point of confusion that they felt the need to say so directly in the output itself.

One line is easy to skim past. Trust: agent-system-prompt.md → BENIGN (1 fail, 3 warn) rolls up to an overall pass, but there are four sub-signals hiding behind that one word: one that actually failed, three that only warned. It’s the same shape as Macaron’s malicious-metadata check, which passed overall in that lab while carrying two failed sub-signals underneath, and it’s worth developing a habit around: an aggregate PASS or BENIGN label is a summary, not a guarantee that nothing underneath it needs a second look.

What I’d patch first: the axios CVE reaching browser-helper, because it’s the only critical finding in this scan with a live credential sitting at the end of its blast radius chain, not because of its EPSS score or its raw severity label alone.


Where this fits in a real environment

This is the right tool to reach for when you already have a working CVE scanner and the actual question keeping you up isn’t “do we have vulnerable packages” (you already know the answer is yes) but “which of these vulnerable packages can actually touch something that matters.” That’s a genuinely different question, and most existing SBOM and SCA tooling doesn’t answer it, per the architecture section above.

It stops being enough the moment your real environment outgrows what local, checked-in configuration can represent. Reality Check points 1 and 5 are the load-bearing ones here: once your MCP servers and agent fleets span more machines than a uniform local scan can see, or once what’s deployed in production drifts from what’s checked into a repo, you’re trusting a snapshot of intent rather than an observation of runtime truth. For a single team’s first look at their own AI stack, that snapshot is genuinely useful. For an organization betting its incident-response priorities on this graph at scale, points 2 and 3 (enrichment cost and inventory staleness) are the ones to pressure-test before you trust the fix-first ranking over your own judgment.


Actionable Takeaways

  • Read the Blast Radius column, not just the severity label. A CRIT finding with nothing downstream and a HIGH finding reaching a live credential are not the same problem, whatever the severity badge says.
  • Verify a generated setup command against the tool’s own current output before assuming the instructions are yours to debug. A fast-moving CLI drifts underneath documentation (and generated challenges) faster than either gets updated.
  • An aggregate pass/fail label is a summary, not the finding. Whether it’s a malicious-metadata heuristic or a trust check on a system prompt, open the sub-signals before you move on.

CVE counts tell you what’s broken somewhere in your stack. Blast radius tells you what actually happens if it gets used, and that’s the number that should be driving what gets fixed first.