Software supply chain security in MCP packages
Software supply chain security, applied to an MCP server, is the work of proving where the code came from before an agent runs it. That splits into three separate proofs: an inventory of what is inside the artefact, a signature binding the artefact to the source that produced it, and a vulnerability result over the pieces the first two identified. An artefact can carry any one of them without the others, and collapsing the three into one word is where most of the confusion starts.
The middle one is the one we can measure. On 17 August 2026, 1,514 of the 7,264 npm package components we could attribute a verdict to published verified build provenance, 20.8%. Across 750 attributable OCI container components on the same day, the figure was 0.
What does software supply chain security actually mean for an MCP server?
An MCP server you install is a dependency with execution rights on the machine your agent runs on. Three separable pieces of evidence tell you something about it, and each answers a different question.
An SBOM answers “what is inside”. A software bill of materials is a machine-readable inventory of the components an artefact contains, usually as SPDX or CycloneDX JSON. It is a list, and a list is only as good as the process that generated it.
A build attestation answers “where did these bytes come from”. A signed statement, produced by a build platform, that this exact artefact was built from this commit in this repository by this CI run. It is a cryptographic claim by a third party (the build platform), not by the publisher.
A vulnerability result answers “what is known-broken”. A scan of the components against an advisory database such as OSV. It is a point-in-time answer that changes without the artefact changing at all, which is why it is the most perishable of the three.
The three degrade independently. A package can carry a perfect SBOM, be built on a laptop with a stolen publish token, and have no known CVEs. Another can carry an impeccable attestation and pull in fourteen abandoned transitive dependencies.
Is an SBOM the same thing as a build attestation?
No, and the difference is who is making the claim.
An SBOM is an inventory. By default it is unsigned, and it is written by the publisher about the publisher’s own artefact. Nothing in the format stops a build pipeline from emitting an SBOM that omits a component, lists a version that is not the one shipped, or was generated from a different tree than the one published. Formats such as SPDX and CycloneDX describe the structure of the claim; they do not establish who made it or whether it is true.
A build attestation is a signature. A SLSA provenance attestation binds a specific artefact digest to a source repository, a commit and the workflow run that produced it, signed by the build platform’s identity. In practice that is usually the in-toto statement format signed through Sigstore, which is what npm and PyPI use, though SLSA recommends both rather than requiring them. How hard the result is to forge is a property of the platform, not of the format: SLSA’s build requirements put “unforgeable” at Build L3, while L2 asks only that the provenance be authentic and the build hosted.
What is a software attestation?
A software attestation is a signed statement about a named software artefact, binding a claim about that artefact to its cryptographic digest so a consumer can check both the claim and the signer. Build provenance is one kind of software attestation; a signed SBOM is another; a signed test result is a third. The signature is the whole distinction: it moves the claim from the party with the incentive to the party with the keys.
Our scoring draws a line here. Where an OCI image has an SBOM attached, we read it, and we use it to resolve the dependency tree so dependency health can be assessed at all. We will not credit a clean vulnerability result derived from that same SBOM, because a clean CVE scan over an inventory the publisher wrote about themselves proves nothing about the artefact: an omitted component produces a clean result for free.
Where can an MCP package even attach an SBOM?
In our scan of the official MCP registry on 18 August 2026, exactly one of the five package ecosystems has a registry-level slot an SBOM can be attached to.
| Ecosystem | Components on 18 August 2026 | Registry-level SBOM attachment point |
|---|---|---|
| npm | 7,469 | None |
| PyPI | 3,298 | None |
| OCI | 726 | OCI 1.1 Referrers API, with a sha256-<digest>.sbom tag fallback |
| MCPB | 847 | None |
| NuGet | 102 | None |
The Referrers API lets an artefact be attached to an image by digest, so an SBOM becomes a first-class referrer discoverable by anyone who can pull the image. The tag convention is the fallback on registries that predate the API, which was added in distribution-spec 1.1. That is a real, standard attachment point, and it is why our OCI resolver looks for one at all (it parses both SPDX 2.x JSON and CycloneDX).
npm, PyPI and NuGet have no equivalent. There is no registry field, no sidecar artefact, and no conventional filename that a consumer can fetch to obtain a published package’s SBOM. Publishers can and do commit SBOMs to their repositories or attach them to GitHub releases, but that is a different artefact in a different place, not something bound to the package you installed.
A headline of the “0% of MCP packages ship an SBOM” kind would therefore be a category error across four of the five ecosystems: there is nowhere for the answer to be yes.
How many MCP packages publish verified build provenance?
The table below is the whole result, measured on 17 August 2026.
| Ecosystem | Verified | Checked and absent | Attributable denominator | Verified share |
|---|---|---|---|---|
| npm | 1,514 | 5,750 | 7,264 | 20.8% |
| OCI | 0 | 750 | 750 | 0.0% |
The denominator is package components with a completed build-attestation verdict on that day, split by ecosystem. Components where the check did not complete are excluded from it, and counted separately in the next section.
Both ecosystems have a working, verifiable mechanism, and we verify both the same way. npm publishers run npm publish --provenance from CI with an OIDC token, Sigstore signs it, and roughly one in five MCP npm components did so on the day we measured. OCI publishers can sign and attest an image with cosign attest, and we check for exactly that: none of the 750 OCI components we could attribute a verdict to passed, and a further 18 could not be checked at all. Those 18 are in neither number.
Two things the npm figure does not say. It counts components, not servers, and a server can list several. And attestations are per-published-version: a package that attested a release two years ago and has published untagged since is not what this counts, because we verify the latest published version.
Evidence is day-keyed and scoring runs in hourly cohorts, so the current day is always partial. The figures above therefore cite 17 August 2026, not the 18 August 2026 date carried by the rest of our registry statistics.
What can we not measure?
Three populations sit behind every figure above. On 18 August 2026 the registry listed 23,066 servers. 22,667 carry at least one component we could score. 395 carry no component at all, so they can never be scored, and they sit outside every percentage on this page rather than dragging one down.
“Unmeasured” is its own number, and it never gets folded into a zero. On 17 August 2026 we could not complete the provenance check for 24 npm components and 18 OCI components. Those are excluded from both the numerator and the denominator above. Rolling them into “absent” would let our own network failures and rate limits masquerade as publisher negligence, which is one way a supply-chain statistic becomes dishonest.
Three ecosystems have a provenance figure we refuse to publish, because the zero measures us. PyPI, NuGet and MCPB all read zero verified in our data, and in all three cases that number describes the state of our verifier rather than the state of the ecosystem:
- PyPI: our extraction of PEP 740 attestation bundles is stubbed, so a package that published perfectly good attestations reads as absent to us. PEP 740 is Final, PyPI genuinely supports it, and publishers genuinely use it.
- NuGet: our verifier is presence-only. It can tell that a package signature exists and structurally cannot return a verified result, so a zero there is arithmetic, not evidence. (NuGet author signing proves the signer’s identity in any case, not the build, which is a separate limitation of the ecosystem rather than of us.)
- MCPB: the bundle format has no signing mechanism at all, so there is nothing to verify and nothing for a publisher to do.
Our snapshot accessor throws rather than returning those counts, so a figure that would read as an adoption rate cannot reach published copy by accident. The counts stay in the underlying data so the gap remains auditable. When we cannot verify something we record it as inconclusive and never guess a pass, and an unverified signal still counts its full weight against a score rather than being given the benefit of the doubt.
What does SLSA add that an SBOM does not?
SLSA (Supply-chain Levels for Software Artifacts) is a framework for how much you can trust the build, and its current specification is v1.2, approved. Its build track is four cumulative levels:
| Level | What it requires | What it defends against |
|---|---|---|
| Build L0 | Nothing. The absence of SLSA | Nothing |
| Build L1 | Provenance is generated automatically and distributed to consumers | Mistakes and undocumented build processes. No tamper protection |
| Build L2 | The build runs on a hosted platform that generates and signs the provenance, and consumers verify it | Tampering after the build, by an unsophisticated adversary |
| Build L3 | The platform is hardened so builds cannot influence each other and secret material is unreachable from user-defined build steps | Tampering during the build itself |
The levels are about the process, so they say something about builds you have not inspected, not just the one artefact in front of you. The attestation format on the routes above is in-toto, so the claim is a signed statement about a specific artefact digest rather than a document sitting next to it. And Sigstore keyless signing removes the long-lived private key that used to make signing infrastructure the weakest part of the chain: the signing identity is a short-lived certificate bound to the CI workload’s OIDC identity, logged to a public transparency log.
The practical reading for a publisher: npm publish --provenance from GitHub Actions, or pypa/gh-action-pypi-publish with Trusted Publishing, runs your build on a hosted platform that generates and signs the provenance itself, which meets the Build L2 requirements as SLSA describes them provided the job actually runs on a hosted runner. A self-hosted GitHub Actions runner is your machine, not a hosted platform, and the isolation requirement is about the platform rather than the workflow file. Neither route asks you to manage a key. That is the step our measurement above is counting.
How does MCP compare with npm and PyPI at large?
Nobody knows.
No rigorous registry-wide SBOM or build-provenance adoption rate exists for any major package ecosystem that we could find a primary source for. The figures that circulate are almost always attestation counts, or measurements against a filtered subsample such as “the top 1,000 packages by downloads”, and neither supports a sentence beginning “x% of npm packages”.
The two closest real measurements, both from primary sources and both without an ecosystem denominator:
- Sigstore’s npm provenance GA announcement (3 October 2023) reported “over 3,800 projects have adopted build provenance (including 134 high-impact projects)” during the public beta from April to September 2023. That is a count of adopters, not a share of npm.
- PyPI’s digital attestations announcement (14 November 2024) reported “more than 20,000 attestations already published”. That is a count of attestations, not of projects, and again has no denominator.
What should you do if you publish an MCP server?
Three things, in order of how much they move a score:
- Publish from CI with an OIDC token and emit a signed attestation.
- Keep your dependency tree resolvable and current.
- If you ship a container image, attach an SBOM as a referrer and attest the image with
cosign. On 17 August 2026, no OCI component in the registry with a completed verdict had done the last of these.
Where to go next
- The full checklist, with a command and a pass condition per row: MCP security best practices.
- Fixing the supply-chain signals that move a score most: build provenance, dependency health and vulnerabilities.
- What a score is and is not: how we score, including why an unverifiable signal is never given the benefit of the doubt.
Ask the three questions separately: what is inside, where the bytes came from, and what is known-broken. Most published MCP packages can answer the third, some can answer the first, and the second is the one this post set out to measure.