Quick path
In this article
Quick read: what changed, why it matters, and what to do next.
The pull request looked clean. Snyk green, Dependabot quiet, GitHub Advanced Security with nothing to say. The reviewer approved it in four minutes and moved on.
What the review never saw was sitting two directories over, in a file none of those tools open. The developer had wired a Git MCP server into their agent host the week before to let Claude Code run repository operations for them. It did the job. It was also pinned to a version with a command-injection flaw in several of its tools, the kind of thing where a crafted argument runs a shell command the developer never typed.
None of that showed up in the dependency scan, because none of it lives in the dependency tree. The MCP server was not a project dependency. It was a sidecar: a separate component the agent loads at runtime, declared in mcp.json and .mcp.json, launched by the agent host, invisible to npm audit because npm was never told it exists.
The repo passed. The workstation that builds the repo was carrying the risk.
This is the seam worth naming. Your software composition analysis reads the application's dependency graph and stops at its edge. The agent stack adds components on the other side of that edge, and right now most teams have nothing inspecting them. A tool published this month tries to close exactly that gap, and the more useful thing it gives you is not the scanner. It is a way to write the gap down.
What software composition analysis was never built to see
Start with what you already trust. A software bill of materials is the now-standard idea that you should be able to enumerate every component that goes into a piece of software, the way CISA describes an SBOM as a nested inventory of ingredients. Your SCA tooling builds and checks that inventory: it walks package.json and the lockfile, resolves the transitive tree, and matches each package against advisory databases.
That machinery is good. It is also pointed at one thing: the dependencies your application code imports.
An AI agent stack is composed differently. The agent host loads behavior from files that are not application dependencies and were never meant to be:
- MCP servers, declared in
mcp.json,.mcp.json, orclaude_desktop_config.json. The Model Context Protocol is an open standard for connecting agents to external tools and data, often described as a USB-C port for AI applications. That framing is the security point, not a marketing line: an MCP server is a pluggable component that grants an agent new powers, frequently with its own credentials and its own bundled dependencies, and it plugs in without touching your lockfile. - Plugins, declared in
.claude-plugin/plugin.jsonand tracked in files likeinstalled_plugins.json. - Skills, defined in
SKILL.mdfiles, which can carry broad capabilities. - Hooks, commands, and subagents, which run on triggers you may not be watching.
- Local agent-host state in places like
.claude/settings.json.
Every one of those can introduce code, network calls, or capabilities. None of them is a row in your dependency manifest. So the honest summary is the one to keep: your SBOM can be clean while your agent sidecar is carrying the risky tool.
The files your scanner never opens
OpenACA is an open-source scanner that calls its job Agent Composition Analysis, by deliberate analogy to software composition analysis. It is V0, early and still evolving, and it is worth treating as a first inspection lane rather than a finished product. But the mechanism is the interesting part, because it does the thing SCA structurally cannot.
It resolves stable identities for agent-stack components, builds an Agent BOM from them, and matches those components against the same advisory sources your dependency scanner already respects: OSV, GHSA, CVE, and malicious-package (MAL) records. The README is blunt about why this is a separate tool: ordinary dependency scanners usually cannot see the plugins, MCP servers, skills, hooks, commands, and bundled dependencies that compose an agent stack.
To do that, it reads the files your SCA walks right past, including .claude/settings.json, .claude-plugin/plugin.json, mcp.json, .mcp.json, claude_desktop_config.json, installed_plugins.json, SKILL.md, plus hooks, commands, and subagents. It runs locally: manifests are read on the machine, then the versioned package and Git coordinates it finds are matched against live OSV advisories unless the network is unavailable.
This is the same lockfile > manifest > source code confidence hierarchy SCA people already think in, ported to the agent stack. OpenACA's coverage model is explicit about it: V0 reads declarative manifests, dependency manifests inside active plugins, and the supported MCP and Claude files. Pinned coordinates get a confident match. Loose ones get a weaker one. What it cannot pin, it still records rather than drops: every component in a file it reads ends up on the list, even when the advisory lookup comes up empty.
The sample advisory match is the pitch in one line. OpenACA's own example output flags @cyanheads/git-mcp-server as vulnerable to command injection in several tools, with a fixed version available. That is a real GHSA record about a real MCP server, surfaced by reading a config file your CI never opens.
Repo clean, laptop dirty
The part that maps cleanly onto how teams already work is that OpenACA runs in two modes, and they answer different questions. Do not collapse them.
Repo scan asks: what agent components are declared in this repository?
openaca scan repo --target /path/to/repo
This is the one that belongs in CI gates, PR checks, and source review, right next to your existing SCA step. It catches the MCP server or skill someone committed into the project so the whole team inherits it.
Endpoint scan asks: what agent components are installed on this machine right now?
openaca scan endpoint
This is for developer laptops and managed runners, the local inventory of what an individual actually has loaded. By default an endpoint scan looks at user-level config only; you add --project /path/to/repo to fold in project context.
The reason you want both is the opening scene. The repo was clean. The endpoint was not. A repo scan would have stayed quiet about that hand-wired Git MCP server, because the developer never committed it. The endpoint scan is what catches what people install for themselves and forget to mention.
When you want the artifact rather than a pass/fail, generate the Agent BOM directly:
openaca bom repo --target /path/to/repo --output openaca-bom.json
Add --include-posture and you also get configuration-hygiene findings, which we'll get to.
The artifact: an Agent BOM intake note
The scanner gives you JSON. What a security or dev lead actually needs to run a review is a short, human-readable note for one repo or one endpoint, the thing you fill out before you install or approve an agent component. Call it the Agent BOM intake note. It is not a roster of your agents and it is not a firewall policy. It is the one-page inventory of the sidecar, scoped to a single host or repo, with an owner and a decision attached to every row.
Here is a version you can copy. Two rows filled in from the scene above:
Scroll sideways to see all 10 columns.
| Found by | Host | Manifest path | Component type | Launch method | Pinned coordinate | Advisory result | Posture signal | Owner | Review action |
|---|---|---|---|---|---|---|---|---|---|
| Endpoint scan | dev-laptop-07 | ~/.claude/mcp.json | MCP server | stdio, agent-launched | @cyanheads/git-mcp-server@1.2.x | GHSA-3q26-f695-pp76, command injection, fix available | unpinned install | A. Rivera | Pin to fixed version, re-scan, then approve |
| Repo scan | acme/checkout-svc | .mcp.json | MCP server | http:// endpoint | internal-tools-mcp, no version | no advisory match | http:// MCP endpoint | platform team | Move to TLS, assign owner, set version |
The columns are doing specific work. Found by records whether a repo scan or an endpoint scan surfaced it, because that tells you who inherits the component. Manifest path and launch method tell you where it lives and how it starts, which is most of what a reviewer needs to reason about blast radius. Pinned coordinate is the difference between a confident advisory match and a guess. Advisory result is the OSV/GHSA/CVE/MAL hit, if any. Posture signal is the hygiene flag. Owner and review action are the two cells that turn an inventory into a decision. A blank in either of those is the finding.

A note on that posture column, because it is easy to over-read. OpenACA's posture findings are configuration-hygiene checks, deliberately kept separate from vulnerability findings. They flag things like unpinned installs, http:// MCP endpoints, endpoint overrides, MCP auto-approval, and broad skill capabilities. The important design choice: posture findings are signal, not gate. They do not affect --fail-on exit codes. So in the intake note, treat posture as the column that tells a human to look harder, not the column that fails a build. An auto-approving MCP server with no advisory hit is not a CVE. It is still the row I'd read first.
The limits: a new lane, not magic coverage
The fastest way to misuse this tool is to treat a clean OpenACA run as proof the agent stack is safe. It is V0, and its own docs are refreshingly direct about the edges.
Current coverage centers on Claude Code and Claude-family filesystem conventions. It does not yet see programmatic SDK configuration embedded in source code; that is on the roadmap as V1, with SDK-aware code extraction not in the OSS scanner today. It does not read non-Claude host state, so Codex CLI, Cursor, Windsurf, and VS Code agent-mode configuration are outside V0. It cannot match local-only or source-less components that have no coordinate to look up. And it does not observe runtime tool invocations at all; runtime observation is simply not implemented in the open-source scanner.
That last one matters for how you frame the artifact. Composition analysis tells you what is declared and installed. It does not tell you what the agent actually did with it at 3am. So the intake note is an inspection lane, not a guarantee. A row that comes back clean means "no known advisory on a coordinate we could resolve," which is exactly what a clean SCA result means too, and we all already know how to hold that lightly.
Keep SCA. Add the sidecar lane.
None of this replaces your existing controls. It slots a missing stage between two you may already have.
First, keep your SBOM and SCA exactly as they are. The application dependency tree still needs Dependabot, Snyk, or whatever you run. That work is not redundant; it is just incomplete for agent-enabled repos.
Second, add the Agent BOM as the sidecar lane. Run a repo scan in CI next to your SCA step, and run endpoint scans on the laptops and runners where agent components actually get installed. Fill the intake note for one repo or one endpoint first. This is also the natural moment to map the workflow around a component, not just the file: the AI workflow security review worksheet is built for taking one agent workflow and writing down what it can access and what evidence it leaves before anyone approves it.
Third, once you know what the components are, the later controls finally have something to bind to. A human roster is a different artifact than a machine-readable composition scan; if you also want the owners-and-shutdown-paths view, that's the agent census, and the two complement each other rather than overlap. And once a component is known and owned, runtime enforcement is the next question, which is where putting an agent firewall in observe mode comes in. The pre-mortem for what these components reach on a developer's own machine is its own topic, covered in why localhost is not a sandbox when the agent can browse.
The order is the point. Composition first, because you cannot govern, gate, or revoke a component you never inventoried.
OpenACA surfaced on Hacker News in mid-June, which is roughly the maturity to expect: useful, early, worth running, not worth trusting blindly. The right way to adopt it is not to bet on the tool. It is to adopt the habit the tool makes cheap.
Start with one host
Do not try to inventory every repo and every laptop. You will stall on completeness and approve the next MCP server before you finish.
Pick one. The repo with the most agent activity, or the laptop of whoever installs the most tooling. Run both scans against it, generate the Agent BOM with --include-posture, and fill the intake note by hand for what comes back. You will hit a blank owner cell or an unpinned MCP server within minutes, and that blank is your real starting point.
The sidecar is part of the supply chain. Your SCA was built before the sidecar existed, and it is doing its job correctly when it ignores mcp.json. The fix is not a better dependency scanner. It is one more lane, one short note, and an owner on every row before the next plugin goes in.
If you want help running the first pass, bring one repo or one endpoint and we will map an Agent BOM intake with you, or scope a single bounded AI workflow through process automation. One filled intake note beats a green dashboard that never looked at the sidecar.
Implementation help
Add the agent sidecar to your supply-chain review
Bring one repo or one developer laptop. BaristaLabs will help you run a composition pass, fill the Agent BOM intake note, and decide what gets an owner, a pin, and a review action.
Best fit for teams already running Dependabot, Snyk, or GitHub Advanced Security that are starting to allow MCP servers, plugins, and skills into developer workflows.
Practical AI Workflow Notes
Want more practical AI operations ideas?
Get short notes on applying AI inside real small-business workflows — from document handling and customer follow-up to internal reporting, compliance, and automation guardrails.
Turn this idea into a pilot
Which workflow should go first?
Use the readiness check to compare impact, effort, risk, owner, and next step before booking a call.
- 3-5 minutes
- Deterministic score
- No sensitive data
Share this post
