Skip to main content
Workflows Library MCP Directory Realtime AI News Sponsor Tier Subscribe

Build an AI Supply-Chain SBOM & Dependency Vetting Workflow with MCP in 2026

Agent runtimes inherit every dependency they touch, and MCP servers are code executed against live data. Build a supply-chain workflow that generates SBOMs, resolves MCP servers, scores components with OSV/Sigstore/Scorecard, and blocks unvetted rollouts.

Deepak Bagada

Deepak Bagada

CEO, SaaSNext

Aug 14, 2026 Published
|
Aug 14, 2026 Updated
|
11 Minutes Reading Time
Core Takeaways for Founders & Builders
  • An MCP server is code your agent executes against live data — it is a supply-chain component that needs SBOM-style vetting.
  • Score components on three signals: known vulnerabilities (OSV), signed provenance (Sigstore), and maintainer health (OpenSSF Scorecard).
  • Unsigned components fail the gate by design: unverifiable code must not reach an agent runtime.
  • Gate the rollout, never the audit trail — every verdict is a signed attestation artifact that security can replay.
  • Re-run on a cadence: nightly drift scans plus pre-deployment gates catch dependencies that went malicious after your last review.

By Deepak Bagada, CEO at SaaSNext & Principal AI Architect.

In June 2026, security researchers watching the package registries logged something that should have been a headline: the software supply chain attacks that used to be a quarterly nuisance — a poisoned npm package, a typosquatted PyPI upload — had become an industrialised, accelerating wave aimed directly at the tooling AI agents run on. npm, PyPI, VS Code extensions, and now MCP servers are being weaponised in the same way: attackers publish a package that looks useful, wait for agents or CI pipelines to trust it, and then use that trust to exfiltrate credentials, inject instructions, or pivot into production. The Model Context Protocol is the newest and most dangerous front, because an MCP server is not just code a developer imports — it is code an autonomous agent executes against live enterprise data on the agent's behalf.

That is why this workflow exists. A software bill of materials (SBOM) is no longer a compliance checkbox; it is the only reliable inventory you can audit before an agent touches a dependency. This guide builds an end-to-end supply-chain SBOM and dependency vetting workflow: it generates CycloneDX SBOMs for your agent runtimes, resolves every MCP server and Python/npm dependency into a component graph, scores each component against vulnerability and provenance signals (Sigstore, OpenSSF Scorecard, OSV), and gates any deployment or agent rollout until the vetting gate passes. The same discipline shows up across our AI workflows library, and the server inventory you will be vetting lives in the MCP directory.

Architecture Overview

graph TD
  subgraph Inventory[Inventory Layer]
    A1[Agent Runtime] --> B1[SBOM Generator]
    A2[MCP Config JSON] --> B2[MCP Resolver]
    A3[requirements.txt / package.json] --> B3[Dep Resolver]
  end
  B1 --> C[Component Graph]
  B2 --> C
  B3 --> C
  subgraph Scoring[Scoring Layer]
    C --> D1[OSV Vulnerability Scan]
    C --> D2[Sigstore Provenance Check]
    C --> D3[OpenSSF Scorecard]
  end
  D1 --> E[Vetting Score 0-100]
  D2 --> E
  D3 --> E
  subgraph Gate[Deployment Gate]
    E --> F{Score >= Threshold?}
    F -- yes --> G[Approve Rollout]
    F -- no --> H[Block + Incident Ticket]
    H --> I[Remediation Queue]
  end

The pipeline has three layers. The inventory layer turns whatever you actually run — the agent process, its MCP server config, and its declared dependencies — into one normalized component graph. The scoring layer computes a single vetting score per component from three independent signals: known vulnerabilities (OSV), signed-provenance integrity (Sigstore / cosign), and project health (OpenSSF Scorecard). The gate layer blocks the rollout of anything below threshold and routes failures into a remediation queue with an incident record. Nothing reaches production through a blind spot, because a component that cannot be scored is treated as a failure, not as a pass.

Part 1 — The inventory layer

.env

SBOM_FORMAT=cyclonedx
SBOM_OUTPUT=./sbom
MCP_CONFIG=~/.config/claude/mcp.json
GATE_THRESHOLD=70
OSV_API=https://api.osv.dev/v1/query
SCORECARD_ENDPOINT=http://scorecard:8080
FAIL_IF_UNSCORED=true
REMEDIATION_QUEUE_URL=https://queue.example.com/jobs

sbom.py

import json, subprocess
from pathlib import Path
from cyclonedx.model import Component, ComponentType
from cyclonedx.model.bom import Bom

def generate_sbom(runtime_dir: str) -> Bom:
    # Inventory agent runtime deps into a CycloneDX BOM.
    bom = Bom()
    # Python deps
    reqs = Path(runtime_dir) / "requirements.txt"
    if reqs.exists():
        for line in reqs.read_text().splitlines():
            line = line.strip()
            if not line or line.startswith("#"):
                continue
            name, _, ver = line.partition("==")
            bom.components.add(Component(
                type=ComponentType.LIBRARY,
                name=name, version=ver or "",
            ))
    return bom

def resolve_mcp_servers(config_path: str) -> list[dict]:
    # Parse mcp.json into component records, one per server.
    cfg = json.loads(Path(config_path).read_text())
    servers = []
    for name, spec in cfg.get("mcpServers", {}).items():
        servers.append({
            "name": name,
            "command": spec.get("command", "npx"),
            "args": spec.get("args", []),
            "source": spec.get("url") or spec.get("command"),
        })
    return servers

The SBOM generator is deliberately boring: it reads what you actually declared and emits a machine-readable bill of materials. The MCP resolver is the part that makes this workflow different from a generic dependency scanner. An MCP server is declared in mcp.json as a command plus args (local) or a URL (remote/stateless), and each of those is a component with its own provenance. If your agent can call a tool, that tool server is in your supply chain — and it gets the same vetting as a pip package, because it carries the same blast radius.

Part 2 — The scoring layer

vet.py

import httpx, json

def osv_query(purl: str) -> list[dict]:
    r = httpx.post("https://api.osv.dev/v1/query",
                   json={"package": {"purl": purl}}, timeout=10)
    return r.json().get("vulns", [])

def sigstore_verify(artifact: str) -> bool:
    # Return True only if a valid signed provenance exists.
    r = subprocess.run(["cosign", "verify", "--certificate-identity",
                        "https://github.com/example/*", artifact],
                       capture_output=True, text=True)
    return r.returncode == 0

def scorecard_score(project: str) -> float | None:
    r = httpx.get(f"http://scorecard:8080/projects/{project}", timeout=10)
    if r.status_code != 200:
        return None
    return r.json().get("score", None)  # 0-10

def vet_component(comp: dict) -> dict:
    vulns = osv_query(comp.get("purl", ""))
    provenance = sigstore_verify(comp.get("source", ""))
    score = scorecard_score(comp.get("repo", ""))
    points = 0.0
    points += 40.0 if not vulns else max(0.0, 40.0 - 10.0 * len(vulns))
    points += 30.0 if provenance else 0.0
    points += (score or 0.0) * 3.0  # 0-30 from Scorecard
    return {**comp, "vetting_score": round(points, 1),
            "vulns": len(vulns), "provenance_ok": provenance}

The scoring model is weighted toward what actually kills deployments: known vulnerabilities are the hard floor (40 points), signed provenance is the integrity signal (30 points), and maintainer health via Scorecard is the leading indicator (30 points). A component with a critical CVE but perfect provenance still scores below the gate. A component with no vulnerabilities but no provenance scores 30 — under threshold by design, because unsigned is unverifiable and unverifiable must not reach an agent runtime. That asymmetry is the whole point: the workflow is strict by default and only learns to trust components that prove themselves.

Part 3 — The LangGraph gate

graph.py

from langgraph.graph import StateGraph, END
from typing import TypedDict, List

class VettingState(TypedDict):
    components: list
    scored: list
    failed: List[str]
    approved: bool

def score_all(state: VettingState) -> VettingState:
    state["scored"] = [vet_component(c) for c in state["components"]]
    return state

def decide(state: VettingState) -> VettingState:
    threshold = float(env("GATE_THRESHOLD", 70))
    state["failed"] = [c["name"] for c in state["scored"]
                       if c["vetting_score"] < threshold]
    if env_bool("FAIL_IF_UNSCORED") and any(
            c.get("vetting_score") is None for c in state["scored"]):
        state["failed"].append("UNSCORED_COMPONENTS")
    state["approved"] = not state["failed"]
    return state

def block_and_ticket(state: VettingState) -> VettingState:
    for name in state["failed"]:
        create_incident({"component": name, "severity": "P1",
                         "reason": "supply-chain vetting gate"})
        enqueue_remediation(name)
    return state

g = StateGraph(VettingState)
g.add_node("score_all", score_all)
g.add_node("decide", decide)
g.add_node("block_and_ticket", block_and_ticket)
g.set_entry_point("score_all")
g.add_edge("score_all", "decide")
g.add_conditional_edges("decide",
    lambda s: "block" if not s["approved"] else "end",
    {"block": "block_and_ticket", "end": END})
g.add_edge("block_and_ticket", END)
app = g.compile()

Retry rules: OSV and Scorecard lookups retry twice with exponential backoff (500ms base, 2x factor) — registry APIs are flaky and a transient 503 should not block a rollout. Never retry a signature verification: if cosign verify fails, the artifact is treated as unsigned; a retry loop on signature checks is how attackers slip a re-signed artifact past a tired pipeline. Gate decisions are final for this run; the remediation queue owns the retry, not the gate. One deliberate design choice: the gate blocks the rollout, never the audit trail — every decision, score, and provenance result is emitted as an attestation artifact so security teams can replay any verdict. That replayability is the same evaluation discipline we apply to model quality across our AI workflows library.

Part 4 — Wiring it together

main.py

import asyncio, json
from sbom import generate_sbom, resolve_mcp_servers
from graph import app

def build_components():
    bom = generate_sbom("./agent_runtime")
    mcp = resolve_mcp_servers(env("MCP_CONFIG"))
    return ([{"name": c.name, "version": c.version,
              "purl": f"pkg:pypi/{c.name}@{c.version}"}
             for c in bom.components] + mcp)

if __name__ == "__main__":
    components = build_components()
    state = app.invoke({"components": components,
                        "scored": [], "failed": [], "approved": False})
    with open("./attestation.json", "w") as f:
        json.dump(state, f, indent=2)
    print("APPROVED" if state["approved"] else "BLOCKED",
          "| failed:", state["failed"])

Run this in CI before every agent deployment and on a nightly cron for drift: dependencies change under you, and an MCP server that was clean last week can ship a malicious update today. The attestation file is the artifact your auditors and your incident responders both want — the full component graph, every score, every failure reason, frozen at decision time. When a new vulnerability drops (OSV publishes fresh data continuously), the same graph re-scores and the gate re-runs, which is how the workflow turns supply-chain risk from a quarterly scare into a continuously verified property.

Production checklist

  1. Treat MCP servers as first-class components. An agent's tool server is code executed against live data — it gets an SBOM entry and a vetting score like any library.
  2. Fail on unscored. A component you cannot score (no SBOM, no provenance, no Scorecard) is a component you cannot trust. Default it to blocked.
  3. Weight provenance as a hard signal. Unsigned dependencies should never pass the gate, regardless of vulnerability count.
  4. Gate the rollout, never the audit trail. Every verdict is an attestation artifact; security teams can replay any decision.
  5. Re-run on a cadence. Nightly drift scans plus pre-deployment gates catch the update that went malicious after your last review.

Frequently Asked Questions

Q: Why do MCP servers need SBOM-style vetting?

A: An MCP server is code your agent executes against your live data and APIs. If it is compromised, the attacker inherits everything the agent can do — which is why the workflow treats each server declared in mcp.json as a component with its own provenance, vulnerability, and health signals.

Q: What if a component has no vulnerabilities but is unsigned?

A: It fails the gate by design. Unsigned means unverifiable, and an agent runtime has no business trusting unverifiable code. The provenance check (Sigstore/cosign) is a hard 30-point block, so an unsigned component tops out well below threshold.

Q: How does this integrate with CI/CD?

A: Run main.py as a pre-deployment gate (fail the build on BLOCKED) plus a nightly drift scan. The attestation JSON is the audit artifact, and the remediation queue receives a P1 incident with the failing component name for every blocked rollout.

Q: Does it handle remote/stateless MCP servers?

A: Yes. Remote servers declared by URL are resolved as components with the URL as their source; signature verification and Scorecard scoring apply the same way, and remote endpoints without published provenance fail the gate just like unsigned local packages.

Q: What is the difference between this and a generic SCA tool?

A: Generic software-composition-analysis tools inventory libraries. This workflow adds the two things agents need: MCP server resolution (tool servers as supply chain) and a deployment gate with attestation, so security verdicts actually block rollouts instead of landing in a report nobody reads.

Executive Briefing

Enjoyed this breakdown? Get our morning dispatch in your inbox.

Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.

Frequently Asked Questions
An MCP server is code your agent executes against your live data and APIs. If compromised, the attacker inherits everything the agent can do, so the workflow treats each server declared in mcp.json as a component with provenance, vulnerability, and health signals.
It fails the gate by design. Unsigned means unverifiable, and an agent runtime must not trust unverifiable code. The provenance check is a hard 30-point block, so an unsigned component tops out well below threshold.
Run main.py as a pre-deployment gate (fail the build on BLOCKED) plus a nightly drift scan. The attestation JSON is the audit artifact, and the remediation queue receives a P1 incident for every blocked rollout.
Yes. Remote servers declared by URL are resolved as components with the URL as source; signature verification and Scorecard scoring apply the same way, and remote endpoints without published provenance fail the gate like unsigned local packages.
Generic SCA tools inventory libraries. This workflow adds MCP server resolution (tool servers as supply chain) and a deployment gate with attestation, so security verdicts block rollouts instead of landing in a report nobody reads.
Deepak Bagada
Author Profile

Deepak Bagada

CEO, SaaSNext

Deepak Bagada is the CEO of SaaSNext and founder of Daily AI World. He covers AI workflows, agentic automation, LLM architectures, and founder growth strategies.

Related Intelligence Analysis

Research Breakdown AI Workflows

The Step-by-Step Guide to Automating Meeting Tasks with Whisper

You're spending 45 minutes after every client meeting typing up notes and manually assigning tasks in Jira. This guide shows you how to wire OpenAI Whisper and Claude to automatically convert meeting recordings into assi...

Deepak Bagada Deepak Bagada
9m read
Research Breakdown AI Workflows

Lovable AI UI-to-Code Pipeline: 2026 Tutorial

Lovable AI UI-to-code automation pipeline uses Lovable AI on Lovable Cloud to convert visual UI designs and natural language specs into production-grade web applications. UI/UX designers and frontend developers bridging...

Deepak Bagada Deepak Bagada
8m read
Breaking AI Workflows

Claude Code's New Browser: 5 Workflows That Save Hours Daily

Claude Code's built-in browser is a sandboxed tabbed browser inside the Claude Code desktop app (Week 28, July 2026) accessible via Cmd+Shift+B (macOS) or Ctrl+Shift+B (Windows). It lets Claude open websites, read docume...

Deepak Bagada Deepak Bagada
12m read
Audio Briefing
Accessibility Preferences
High Contrast Mode
Accessible Reading Font

Keyboard Shortcuts

Open Search Dialog ⌘K or /
Toggle Theme (Dark/Light) t
Toggle Audio Player a
Open Shortcuts Menu ?
Close Active Dialog Esc