MCP inventory for JavaScript and dedicated MCP project scans
September 11, 2026 · View on GitHub
cdxgen can catalog Model Context Protocol (MCP) server surfaces from JavaScript and TypeScript source trees during normal -t js analysis, or via the dedicated -t mcp project type.
By default, -t js now also reports shipped MCP configuration files and AI instruction/skill files that can influence build and post-build lifecycles. Use:
--exclude-type mcpto drop the MCP inventory overlay — config file components, discovered services, and MCP primitives (tools/prompts/resources) — from the final BOM. Genuine MCP SDK dependency packages (for example@modelcontextprotocol/*, PyPImcp,io.modelcontextprotocol.sdk) are real supply-chain components and are always retained.--exclude-type ai-skillto drop AI skill / instruction inventory from the final BOM-t mcpfor an exact MCP-focused BOM. This includes the SDK packages, discovered services, primitives, and MCP config files (for example.vscode/mcp.json);--type mcpand--exclude-type mcpcover the same set of subjects so the two flags stay in lockstep.-t ai-skillfor an exact AI skill / instruction BOM
What cdxgen detects
For high-confidence JavaScript MCP patterns, cdxgen emits:
- components for well-known MCP SDK packages such as
@modelcontextprotocol/* - services for discovered MCP servers
- synthetic components for MCP primitives exposed by those servers:
- tools
- prompts
- resources
- resource templates
- dependency/provides links from the server service to the primitive components it exposes
Current detection scope
- official and non-official MCP SDK imports, including the split
@modelcontextprotocol/serverand@modelcontextprotocol/clientpackages McpServer-style server construction and the statelesscreateMcpHandlerfactoryClient-style MCP client construction- stdio, Streamable HTTP, SSE, and the web-standards Streamable HTTP transports
- MCP tool / prompt / resource registration calls
- prompt / tool / resource client usage call sites
- explicit capability declarations
- authentication helpers for HTTP MCP servers
- OAuth metadata literals and MCP auth-discovery wiring
- explicit provider and model literals such as
provider,providerName,model, andmodelName - provider SDK imports, outbound provider hosts, and MCP gateway patterns for a wide provider set (OpenAI, Anthropic, Google, Azure OpenAI, Mistral, Cohere, DeepSeek, Groq, xAI/Grok, AWS Bedrock, OpenRouter, Cerebras, DashScope/Qwen, NVIDIA NIM, and more)
- AI agent instruction files that reference hidden MCP endpoints or wrappers
- MCP client configuration files such as
.vscode/mcp.json,.cursor/mcp.json,.mcp.json,claude_desktop_config.json,opencode.json,.gemini/settings.json,.windsurf/mcp_config.json,.zed/settings.json,.roo/mcp.json, and the Codex CLI.codex/config.toml([mcp_servers.*]TOML tables) - MCP Registry
server.jsonmanifests, resolving declared distributable packages to purls and separating registry-hosted remotes (markedcomposition=unknown) - MCPB / DXT desktop-extension bundles (
.mcpb,.dxt), summarizing the bundled server, its tools/prompts, declared user-configuration fields, and whether it injects secret configuration as environment variables - MCP 2026-07-28 protocol signals: an explicitly pinned protocol version, deprecated features (legacy SSE transport, dynamic client registration, sampling, logging, roots), and hardened authorization signals (Client ID Metadata Documents, audience-bound tokens)
- community agent tooling layouts such as OpenCode (
opencode.json,.opencode/agents,.opencode/tools,.opencode/skills), Nanocoder (.mcp.json,.nanocoder/agents,.nanocoder/commands), LangGraph (langgraph.json), and common CrewAI project files (agents.py,tasks.py,config/agents.yaml,config/tasks.yaml) - the open Agent Skills standard (
skills/<name>/SKILL.md) and.codex/skills, plus Claude Code plugins (.claude-plugin/plugin.json,.claude-plugin/marketplace.json) and subagents (.claude/agents) - config-derived auth posture, trust profile, dynamic client registration, and inline credential exposure
The analysis is intentionally conservative. cdxgen prefers literal, explainable signals over speculative reconstruction.
Key emitted properties
MCP package components
cdx:mcp:package=truecdx:mcp:official=true|falsecdx:mcp:role=server-sdk|client-sdk|transport-sdk|sdk|integrationcdx:mcp:catalogSource=official-sdk|known-integration|heuristic
MCP server and configured services
cdx:mcp:serviceType=server|client|gateway|endpoint|inferred-endpoint|configured-server|registry-remotecdx:mcp:transport=stdio|streamable-http|sse|websocketcdx:mcp:protocolVersion(e.g.2026-07-28)cdx:mcp:deprecatedFeatures(comma-separated:sse-transport,dynamic-client-registration,sampling,logging,roots)cdx:mcp:composition=unknown(registry remotes with no resolvable local package)cdx:mcp:auth:cimdandcdx:mcp:auth:audienceBound(hardened MCP authorization signals)cdx:mcp:officialSdk=true|falsecdx:mcp:capabilities:*cdx:mcp:toolCountcdx:mcp:promptCountcdx:mcp:resourceCountcdx:mcp:sdkImportscdx:mcp:modelNamescdx:mcp:modelFamiliescdx:mcp:providerNamescdx:mcp:providerFamiliescdx:mcp:outboundHostscdx:mcp:usageSignalscdx:mcp:usageConfidencecdx:mcp:inventorySourcecdx:mcp:exposureTypecdx:mcp:configFormatcdx:mcp:configKeycdx:mcp:commandcdx:mcp:packageRefscdx:mcp:authPosturecdx:mcp:trustProfilecdx:mcp:credentialExposurecdx:mcp:credentialExposureFieldCountcdx:mcp:credentialIndicatorCountcdx:mcp:credentialReferenceCountcdx:mcp:credentialExposedServiceCount(for config file components)cdx:mcp:security:confusedDeputyRiskcdx:mcp:security:tokenPassthroughRiskcdx:mcp:reviewNeededcdx:mcp:auth:*
MCP primitive components
cdx:mcp:role=tool|prompt|resource|resource-templatecdx:mcp:serviceRef=<service bom-ref>cdx:mcp:descriptioncdx:mcp:resourceUricdx:mcp:toolAnnotations
Community agent/tool/skill components
cdx:agent:framework=opencode|nanocoder|langgraph|crewaicdx:agent:inventorySource=community-configcdx:agent:descriptioncdx:agent:modecdx:agent:modelcdx:tool:descriptioncdx:tool:categorycdx:tool:tagscdx:tool:triggerscdx:skill:namecdx:skill:descriptioncdx:skill:licensecdx:langgraph:graphEntryPointcdx:crewai:*
MCP Registry manifest components
For server.json manifests, cdxgen emits a file component
(cdx:file:kind=mcp-server-manifest) plus one component per declared
distributable package and one service per remote endpoint:
cdx:registry:server:name,cdx:registry:server:versioncdx:registry:server:packageCount,cdx:registry:server:remoteCountcdx:registry:server:packageRegistryType=npm|pypi|nuget|oci|mcpbcdx:registry:server:packageTransport
MCPB / DXT bundle components
For .mcpb / .dxt bundles, cdxgen reads only the embedded manifest.json:
cdx:file:kind=mcp-bundlecdx:mcpb:manifestVersion,cdx:mcpb:serverTypecdx:mcpb:toolCount,cdx:mcpb:promptCountcdx:mcpb:userConfigFieldCount,cdx:mcpb:secretFieldCount,cdx:mcpb:injectsSecretEnvcdx:mcpb:platformBinaryCount
Claude Code plugin and subagent components
cdx:file:kind=agent-plugin|agent-plugin-listingcdx:plugin:description,cdx:plugin:authorcdx:plugin:commandCount,cdx:plugin:agentCount,cdx:plugin:skillCount,cdx:plugin:hookCount,cdx:plugin:mcpServerCountcdx:skill:specValid,cdx:skill:hasScripts,cdx:skill:hasReferences,cdx:skill:hasAssets
Example
cdxgen -t mcp /path/to/mcp-server -o bom.json --bom-audit --bom-audit-categories mcp-server
Things to inspect in the resulting BOM:
.services[]for discovered MCP servers.components[] | select(.properties[]?.name == "cdx:file:kind" and .properties[]?.value == "mcp-config")for shipped MCP config files.components[] | select(.properties[]?.name == "cdx:file:kind" and (.properties[]?.value == "agent-instructions" or .properties[]?.value == "skill-file"))for shipped AI instruction/skill files.components[] | select(.properties[]?.name == "cdx:mcp:role")for tools/prompts/resources.dependencies[] | select(.ref | startswith("urn:service:mcp:"))for service-to-primitive links.annotations[]for MCP BOM-audit findings
Security notes
The most important current security checks are:
- unauthenticated Streamable HTTP MCP servers
- unauthenticated MCP tool exposure
- network-exposed servers built on non-official MCP SDKs or wrappers
- networked MCP endpoints discovered only from configuration files
- inline credentials or token-forwarding settings in MCP configs
- dynamic client registration paired with static client identities in MCP configs
- public or tunneled MCP endpoints referenced only from AI agent files
- hidden Unicode in AI agent instruction and skill files
- agent-file MCP references that are not otherwise declared in package or source inventory
- build/post-build BOMs that contain shipped MCP configs or AI instruction/skill files
- configurations still relying on features deprecated by the MCP 2026-07-28 spec (MCP-009)
- MCPB/DXT bundles that inject secret configuration as environment variables (MCP-010)
Recommended release-review commands
Keep and flag the files:
cdxgen -t js \
--bom-audit \
--bom-audit-categories mcp-server,ai-agent \
--tlp-classification AMBER \
-o bom.json \
/path/to/repo
Drop them for a package-only SBOM:
cdxgen -t js \
--exclude-type ai-skill \
--exclude-type mcp \
-o bom.json \
/path/to/repo
HTTP MCP endpoints should be authenticated, Origin-validated, and pinned to trusted SDK provenance before external exposure.
Known limits
- the current implementation is strongest for literal ESM/CJS patterns and explicit object literals
- dynamically generated tool names, endpoints, or capability objects may be missed
- provider/model detection is best-effort and only records explicit literals
- stdio servers are inventoried, but HTTP-centric auth rules intentionally focus on network-exposed servers
Gap analysis: server pinning, transport, and composition
This section records the gap between the current MCP inventory and what an
Agent BOM needs, and what cdxgen does about it. The CycloneDX working group's
agent-BOM proposal is still an open discussion (the #895 reference in earlier
planning does not resolve to a published, ratified standard), so every new
field below is experimental, off by default, and namespaced under cdx:mcp:
so migration is mechanical when a standard lands.
Relationship to -t ai-provenance
-t ai-provenance (aliases ai-authorship, aicode, ai-codegen) is a
distinct concern: it is an opt-in generation-time property injector that
runs collectAiProvenance / collectAiOversight over git history and CI
config and writes cdx:ai:codegen:* / cdx:ai:oversight:* to the document
root. It does not inventory MCP servers. The MCP inventory itself is produced
by the mcp / js / python project types via lib/inventory/mcp*.js and
lib/inventory/analyzer.js. The work below extends that inventory; it does not
change what -t ai-provenance emits.
What already works
- Local npm-package MCP servers are not opaque. A server shipped as an npm
package is produced by the normal language pipeline as one
librarycomponent per installed package (server plus each transitive), each carrying its own purl and ssri integrity from the lockfile. Transitive dependencies are therefore already resolved.enrichComponentWithMcpMetadataonly adds thecdx:mcp:package/cdx:mcp:roletags on top. - Transport and exposure are recorded for discovered services. Config-file
servers (
mcpConfigParser) and source-code servers (analyzer) emitcdx:mcp:transport(stdio/sse/streamable-http/websocket) andcdx:mcp:exposureType(local-only/networked-public).
The two real gaps
- Pinning is decorative without an explicit pinning state. A package component may carry a hash, but nothing says whether a given MCP server is pinned, and an unpinned or unhashable server can serialize identically to a pinned one. A remote server discovered only as a service has no package component and therefore no hash at all, yet nothing records that absence.
- Transport is not recorded on package components.
mcp.jsstamps a role (server-sdk,client-sdk,transport-sdk, …) but never the transport mechanism, so a package-typed server cannot be distinguished from a transport-layer library without reading the service inventory.
What cdxgen does about it (experimental)
Behind --experimental-mcp-pinning (or CDXGEN_EXPERIMENTAL_MCP_PINNING=true),
off by default:
- For every component tagged
cdx:mcp:package, cdxgen records an explicitcdx:mcp:pinningproperty:pinnedwhen the component carries ahashes[]entry (or an ssri_integritythatprocessHashesconverts),unpinnedwhen the component is a package but has no hash,unhashablefor servers discovered only as services with no resolvable package. Absence is never implied: an unhashable server is labelled, not left blank.
- One CycloneDX 1.7 citation covers every pinned package, recording that the integrity values came from the package registry. It is attributed to the cdxgen tool component, the only object in the BOM that can carry the claim; in a document with no cdxgen tool component the citation is omitted rather than pointed at an invented reference.
- Remote servers (no local package) additionally get
cdx:mcp:composition=unknown, so a consumer never mistakes a composition-unknown remote for a fully-resolved local package. - Package-typed servers that can be linked to a discovered service inherit the
service's
cdx:mcp:transport.
These properties are subject to change and will be renamed or removed to match the eventual standard.