The inventory
Start here. Every other page in this section is about writing rules, and the hard part of writing a capability rule is not the syntax — it is knowing what to name.
The inventory is a list of every tool, MCP server and skill your organisation’s agents have been observed carrying, and which client carried it.
Why a repository scan is not enough
Vulnetix already ships an AI-BOM: it scans a repository and reports the AI agents, SDKs and models it finds in config files, environment-variable names, source literals and commit trailers. That answers “what AI is written down here”.
It cannot answer “what is our estate actually running”, because the interesting cases are not written down:
- An MCP server added to a developer’s local
~/.claude.json, holding a long-lived token for the issue tracker. - A tool that exists only inside a coding agent’s own binary.
- An agent a contractor is using that nobody procured.
All three are in every request those agents make. These APIs are stateless, so the client re-declares its complete tool list on every turn, and the firewall is already reading the body to apply guardrails.
Static discovery plus runtime observation is a complete AI asset inventory. Either alone is not.
What is recorded
| Field | What it is |
|---|---|
kind | tool, mcp_server, or skill |
identity | The name — the string a rule’s pattern matches against |
detail | Supporting context: an MCP server’s URL, or a built-in tool’s versioned type |
source | declared, invoked, or inferred — see below |
clientName / clientVersion | The calling agent, from its headers |
providerSlug, wire | Where it was going, and on which surface |
firstSeenAt / lastSeenAt | When |
observations | How many distinct sightings windows — not a request count |
What is not recorded
No prompt. No completion. No tool argument. No tool result. No message content of any kind, and no record that any particular request happened.
Nothing about a request’s text is read to build an inventory row. The gateway
reads the tools array and the request headers, which is the same class of
configuration as the provider slug and model name it already records.
MCP server URLs are stored as scheme://host/path only — query string and
userinfo are discarded before the value leaves the parser. That is a requirement,
not tidiness: the Anthropic mcp_servers entry carries an authorization_token
beside the URL, and a server that authenticates by query parameter is a real
pattern. Nothing token-shaped is allowed into a row that outlives the request.
The inventory is configuration, not usage. It records that your estate offers
a mcp__github__* toolset; it does not record that anybody used it, what they
asked it to do, or what came back.
That is why it runs for every organisation on every plan and is independent of inference logging — the two answer different questions. Logging is about conversations and is a paid opt-in. This is the list your guardrails are written against, in the same way the provider and model catalogs are.
The three sources
source is on every row because how firmly something is known matters, and
flattening the difference would let a naming convention be read later as a
protocol fact.
declared — read verbatim from a field of the request. The tool was in the
tools array. This is the strongest ordinary case.
invoked — the tool appears in the replayed conversation as having actually
been called. Stronger than declared: it was not merely offered to the model, it
ran. It is also what makes a strip unsafe on that name, because removing the
declaration would orphan the call. See rollout.
inferred — derived from a naming convention rather than read. A local
stdio MCP server is only identifiable by its mcp__server__tool tool-name prefix,
and a skill list read off a dispatching tool’s schema is a read of a shape rather
than of a documented field. Treat these as strong hints, not facts.
observations counts windows, not requests
A Claude Code turn declares around forty tools and re-declares the same forty on every request for hours. Recording that per request would be forty rows a second for one unchanged fact.
Instead the gateway hashes the capability set plus the client into a
contextHash, keeps an in-process set of the hashes it has already written, and
issues one statement for the whole set when it sees a new one. Steady-state cost
is one database write per distinct agent configuration per organisation per hour.
So observations is “how many times has this configuration turned up in a fresh
window”, not traffic volume. It is named for what it counts rather than implying
something it does not measure. If you want per-request attribution, that is what
AiFirewallLog.contextHash is for — it joins an individual inference to the exact
configuration in play, and a change in that hash between two consecutive rows is
the moment somebody wired something new in.
Reading it
The Tools & MCP tab of the AI Firewall dashboard.
Filter by kind and client, search by name, and use the per-row Deny or
Allow button to turn an observation into a rule. Rules created that way start
at flag on purpose: you have just discovered the capability, you have not yet
found out what else in the estate depends on it.
# everything
vulnetix ai-firewall inventory
# just the MCP servers — the shadow-MCP question, answered
vulnetix ai-firewall inventory --kind mcp_server
# what one agent carries
vulnetix ai-firewall inventory --client claude-code
Resetting it
The inventory can be cleared, from the dashboard or with
vulnetix ai-firewall inventory --clear.
That exists because an inventory nobody can make current is one people stop trusting and then stop reading. An organisation that has retired an MCP server wants the row gone, not explained. The gateway rebuilds from live traffic within its cache window, so this is a reset rather than a destruction — anything still in use reappears within the hour.
Related
- Rolling out a rule — what to do with what you find.
- MCP servers — why the
mcp_serverrows and themcp__*tool rows are telling you different things. - Opt-in logging — what else the firewall does and does not retain.