# Open Source Supply Chain Risk MCP (`ryanclinton/open-source-supply-chain-risk-mcp`) Actor

OSS supply chain risk intelligence with dependency graph propagation, bus-factor analysis, and typosquat detection.

- **URL**: https://apify.com/ryanclinton/open-source-supply-chain-risk-mcp.md
- **Developed by:** [ryan clinton](https://apify.com/ryanclinton) (community)
- **Categories:** AI, Developer tools
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Open Source Supply Chain Risk MCP

Open source supply chain risk analysis via MCP — give your AI agent direct access to CVE records, CISA KEV exploits, maintainer bus-factor scores, and typosquat detection across 7 live intelligence sources. Built for security engineers, DevSecOps teams, and AI-assisted workflows that need SBOM-grade risk intelligence without leaving their AI client.

This MCP server exposes 8 specialized tools that query GitHub, NVD, CISA KEV, Hacker News, StackExchange, ArXiv, and Censys in parallel — then merge results into a typed OSS dependency network graph. A 5-minute in-process cache means repeated queries on the same package cost nothing extra, making it practical for interactive AI sessions that circle back to the same library during a conversation.

### What data can you access?

| Data Point | Source | Example |
|------------|--------|---------|
| 📦 Repository metadata, stars, forks, language | GitHub | `pallets/flask`, 68k stars, Python |
| 🔒 CVE records with CVSS scores | NVD | `CVE-2024-45805`, CVSS 9.1, critical |
| 🚨 Actively exploited vulnerabilities | CISA KEV | `CVE-2021-44228` (Log4Shell), CISA BOD 22-01 binding |
| 👤 Maintainer identity and bus factor | GitHub | 1 active maintainer, last push 18 months ago |
| ⚠️ Typosquat candidates | GitHub cross-ref | `reqeusts` vs `requests`, Levenshtein distance 1 |
| 💬 Supply chain incident discussions | Hacker News | "xz-utils backdoor discovered in 5.6.0" |
| ❓ Developer migration and adoption signals | StackExchange | "migrating from requests to httpx — security concerns" |
| 🔬 Academic security research papers | ArXiv | "Typosquatting in PyPI: a systematic study (2024)" |
| 🌐 Internet-facing infrastructure exposure | Censys | Hosts running vulnerable Log4j versions |
| 📊 Dependency network graph | All sources | Nodes + edges: repo, maintainer, CVE, KEV, discussion |
| 🏥 Community health index | GitHub + HN + SE | Fork/star sentiment, issue response ratio, 0-1 score |
| 📋 SBOM-aware risk report | All 7 sources | Categorized findings with severity + CISA remediation steps |

### Why use Open Source Supply Chain Risk MCP?

Manual OSS supply chain audits are expensive and slow. A security engineer reviewing 50 transitive dependencies by hand — cross-referencing NVD, checking GitHub commit history, scanning PyPI for lookalikes — takes 2-3 days. CISA KEV updates daily. ArXiv publishes new vulnerability research weekly. No team keeps pace manually.

This MCP automates the entire process. Your AI agent calls one tool and gets a scored, graph-backed risk assessment from 7 live sources in under 30 seconds. A complete due-diligence workflow on a single package — risk score, blast radius, maintainer health — costs $0.105. Commercial alternatives charge far more.

- **Always-current data** — queries live APIs at call time; 5-minute TTL cache prevents duplicate charges within a session
- **API access** — connect from Claude Desktop, Cursor, Windsurf, Cline, or any HTTP MCP client
- **Parallel execution** — all 7 data sources run concurrently via `Promise.all`; typical response under 30 seconds
- **Monitoring** — Apify platform alerts on failed runs; Webhooks notify Slack on critical KEV detections
- **Integrations** — connect to Zapier, Make, LangChain, LlamaIndex, or direct HTTP calls from any language

### Features

- **Typed OSS dependency network graph** — constructs a graph with 7 node types (`repo`, `maintainer`, `cve`, `kev`, `package`, `discussion`, `paper`, `host`) and 6 edge types (`depends_on`, `maintains`, `affects`, `discusses`, `researches`, `hosts`) from merged multi-source data; up to 80 repositories, 60 CVEs, and 40 KEVs per run
- **Maintainer bus-factor scoring** — counts confirmed `maintains` edges per repository; bus factor = `min(1, maintainerCount / 5)`; combines with activity recency via a 730-day abandonment decay model using `pushedAt`/`updatedAt` metadata
- **CVE blast radius estimation** — BFS graph traversal up to depth 6 from each CVE/KEV node through all edge types; counts reached `repo:` prefixed nodes; reports `blastRadius` as a 0-1 proportion of total tracked repositories
- **CISA KEV cross-referencing** — KEV nodes receive fixed severity 0.95 and trigger `active_exploitation` factor weight of 0.9 in the multi-factor risk score; generates CISA BOD 22-01 remediation guidance automatically
- **5-factor weighted supply chain risk scoring** — vulnerability exposure (30%), bus factor (20%), active exploitation (20%), popularity/attack surface (15%), community signal (15%); risk levels at thresholds 0.75/0.50/0.25
- **Levenshtein typosquat detection** — O(n²) pairwise edit distance across all discovered repository short names; flags pairs with distance 1-2 and star ratio below 0.3 as high-suspicion; suspicion formula: `(1 - dist/3) * 0.5 + (1 - starRatio) * 0.5`; threshold 0.6 to count as "suspicious"
- **Community health indexing** — fork-to-star ratio as sentiment proxy, issue-to-star ratio for response quality, and Hacker News/StackExchange discussion volume combined into a single 0-1 health index per repository
- **Security research landscape tracking** — extracts 13 security topic keywords from ArXiv titles and HN posts; trend score = recent papers (last 90 days) / total papers; surfaces pre-disclosure threat research typically 4-8 weeks before formal CVE assignment
- **SBOM-aware report generation** — categorizes findings into Known Exploited Vulnerabilities, Critical CVEs (CVSS ≥ 9.0), and Bus Factor Risk (≤ 1 maintainer); sorted by severity with per-finding evidence strings and actionable remediation recommendations
- **5-minute in-process cache** — `Map`-based TTL cache keyed by `source:query`; multiple tool calls on the same package within a session reuse upstream data and return near-instantly
- **Dual MCP transport** — SSE transport (`/sse` + `/messages`) for legacy clients; Streamable HTTP (`/mcp`) for modern clients; `/health` endpoint for readiness probes
- **Per-tool source configuration** — every tool accepts a `sources` array; defaults are tuned per tool (all 7 for `generate_oss_risk_report`; `arxiv` + `hackernews` + `stackexchange` for `assess_research_security_landscape`)

### Use cases for open source supply chain risk

#### DevSecOps pipeline security gates

A platform engineering team integrates this MCP with their Claude-based code review agent. Before merging a PR that adds a new npm dependency, the agent calls `score_supply_chain_risk` on the package name. A score above 0.6, or any linked KEV node, blocks the PR until the security lead approves. The entire gate runs in under 30 seconds and costs $0.035 per check — far cheaper than a false-positive incident.

#### SBOM audit and vendor risk management

A procurement team evaluating a SaaS vendor asks their AI assistant to run `generate_oss_risk_report` on the vendor's declared key dependencies. The report surfaces CISA KEV-listed vulnerabilities in third-party libraries, single-maintainer repositories at abandonment risk, and SBOM remediation steps aligned with CISA BOD 22-01. Due diligence that previously took a week of manual research completes in one AI conversation.

#### Incident response triage

When a new critical CVE drops — a Log4Shell-class event — the incident response team calls `detect_vulnerability_propagation` with the CVE identifier. The blast radius analysis shows which internal repositories depend on affected packages and the propagation depth through the dependency graph. The team immediately knows which systems need emergency patching and in what priority order.

#### Package vetting before adoption

An engineering lead evaluating three candidate libraries calls `assess_maintainer_risk` on each. Two show bus factors above 0.4 and recent activity. One has a single maintainer with a last push 18 months ago and abandonment risk of 0.82. The team chooses one of the healthier alternatives. The $0.105 total cost is a fraction of the engineering time saved.

#### Supply chain attack early warning

A security research team monitoring OSS ecosystems runs weekly calls to `assess_research_security_landscape` for their technology stack keywords. When a new ArXiv paper on npm supply chain attacks trends, or Hacker News discussion volume spikes around a specific package, they get an alert before formal CVE disclosure. Research papers precede CVE assignment by weeks on average.

#### Typosquat monitoring for package registries

A registry security team uses `detect_typosquat_indicators` to scan recently published packages against their catalog of popular packages. The Levenshtein engine finds candidates like `colourama` (vs `colorama`) or `reqeusts` (vs `requests`) with high suspicion scores. Flagged packages are quarantined for manual review before reaching developer machines.

### How to connect this MCP server

1. **Get your Apify API token.** Sign up at [apify.com](https://apify.com) and copy your token from Account Settings. The free plan includes $5 of monthly credits — enough for roughly 140 tool calls.

2. **Add the server to your MCP client.** Use the Streamable HTTP endpoint: `https://open-source-supply-chain-risk-mcp.apify.actor/mcp`. Pass your token in the `Authorization: Bearer` header. See the client-specific configs in the API section below.

3. **Start with `generate_oss_risk_report`.** Call it with the package or repository name you want to assess. The report queries all 7 sources and returns severity-sorted findings in one call.

4. **Drill down with focused tools.** If the report surfaces a high-risk maintainer, follow up with `assess_maintainer_risk`. If a KEV is present, call `detect_vulnerability_propagation` to understand blast radius. Each focused call reuses the cached upstream data from step 3.

### MCP tools

| Tool | Price | Default sources | Description |
|------|-------|-----------------|-------------|
| `map_dependency_network` | $0.035 | github, cve, kev, hackernews | Map OSS dependency graph: nodes, edges, connection counts |
| `assess_maintainer_risk` | $0.035 | github, cve, kev, hackernews | Bus factor, abandonment risk, last activity date per repo |
| `detect_vulnerability_propagation` | $0.035 | github, cve, kev, hackernews | CVE/KEV blast radius via BFS through dependency graph |
| `analyze_community_health` | $0.035 | github, cve, kev, hackernews | Fork/star sentiment, issue response, discussion volume |
| `score_supply_chain_risk` | $0.035 | github, cve, kev, hackernews, stackexchange | 5-factor weighted risk score with per-factor breakdown |
| `detect_typosquat_indicators` | $0.035 | github, cve, kev, hackernews | Levenshtein + star ratio pairwise typosquat analysis |
| `assess_research_security_landscape` | $0.035 | arxiv, hackernews, stackexchange | ArXiv paper trends + community discussion signals |
| `generate_oss_risk_report` | $0.035 | All 7 sources | Comprehensive SBOM-aware report with findings and recommendations |

#### Tool parameters

Every tool accepts the same two parameters:

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `query` | string | Yes | — | Package name, repository slug, or OSS project to investigate (e.g., `log4j`, `pallets/flask`, `numpy`) |
| `sources` | array of enum | No | Varies by tool | Data sources to query. Options: `github`, `cve`, `kev`, `hackernews`, `stackexchange`, `arxiv`, `censys` |

#### Input examples

**Single package risk score (most common):**
```json
{
  "query": "log4j",
  "sources": ["github", "cve", "kev", "hackernews", "stackexchange"]
}
````

**Full SBOM risk report across all sources:**

```json
{
  "query": "com.fasterxml.jackson.core:jackson-databind",
  "sources": ["github", "cve", "kev", "hackernews", "stackexchange", "arxiv", "censys"]
}
```

**Fast typosquat check (GitHub only):**

```json
{
  "query": "requests",
  "sources": ["github"]
}
```

**Research landscape for a technology ecosystem:**

```json
{
  "query": "npm supply chain",
  "sources": ["arxiv", "hackernews", "stackexchange"]
}
```

#### Input tips

- **Use exact package names** — `log4j-core` returns more precise CVE matches than generic terms like `java logging`
- **Start with `score_supply_chain_risk`** — it gives a 0-1 score with factor breakdown to decide whether deeper analysis is needed
- **Add `censys` for KEV-confirmed packages only** — Censys queries are slower than the other sources; reserve them for packages where active exploitation is already confirmed
- **Use `arxiv` for pre-disclosure threat hunting** — research papers on specific ecosystems (npm, PyPI, Maven) often appear 4-8 weeks before formal CVE assignment
- **Structure AI sessions to group calls on the same package** — the 5-minute cache means a second tool call on the same query within a session reuses upstream data and costs only the $0.035 event charge

### Output examples

#### `score_supply_chain_risk` response

```json
{
  "assessments": [
    {
      "entity": "apache/log4j",
      "riskScore": 0.8125,
      "riskLevel": "critical",
      "factors": [
        { "factor": "vulnerability_exposure", "weight": 0.30, "score": 1.0 },
        { "factor": "bus_factor",             "weight": 0.20, "score": 0.6 },
        { "factor": "popularity",             "weight": 0.15, "score": 0.12 },
        { "factor": "active_exploitation",    "weight": 0.20, "score": 0.9 },
        { "factor": "community_signal",       "weight": 0.15, "score": 0.3 }
      ]
    },
    {
      "entity": "apache/log4j-extras",
      "riskScore": 0.4830,
      "riskLevel": "high",
      "factors": [
        { "factor": "vulnerability_exposure", "weight": 0.30, "score": 0.4 },
        { "factor": "bus_factor",             "weight": 0.20, "score": 0.8 },
        { "factor": "popularity",             "weight": 0.15, "score": 0.35 },
        { "factor": "active_exploitation",    "weight": 0.20, "score": 0.1 },
        { "factor": "community_signal",       "weight": 0.15, "score": 0.7 }
      ]
    }
  ],
  "avgRisk": 0.6478,
  "criticalCount": 1
}
```

#### `generate_oss_risk_report` response

```json
{
  "overallRisk": "critical",
  "reposScreened": 14,
  "findings": [
    {
      "category": "Known Exploited Vulnerabilities",
      "finding": "2 CISA KEV-listed vulnerability(ies) in OSS dependencies",
      "severity": "critical",
      "evidence": "CVE-2021-44228, CVE-2021-45046",
      "recommendation": "Immediate dependency update or mitigation per CISA BOD 22-01"
    },
    {
      "category": "Critical CVEs",
      "finding": "3 critical vulnerability(ies) affecting tracked repositories",
      "severity": "critical",
      "evidence": "CVE-2021-44228, CVE-2022-23302, CVE-2019-17571",
      "recommendation": "Emergency patching and dependency lockfile review"
    },
    {
      "category": "Bus Factor Risk",
      "finding": "5 repo(s) with single-maintainer bus factor",
      "severity": "high",
      "evidence": "log4j-extras, log4j-scan, log4j-detector",
      "recommendation": "Identify backup maintainers or fork critical single-maintainer dependencies"
    }
  ],
  "recommendations": [
    "SBOM audit and KEV remediation",
    "Automated dependency scanning in CI/CD",
    "Maintainer diversity programme for critical dependencies"
  ]
}
```

#### `detect_typosquat_indicators` response

```json
{
  "indicators": [
    {
      "package": "reqeusts",
      "similarTo": "requests",
      "editDistance": 1,
      "starRatio": 0.0002,
      "suspicionScore": 0.8332
    },
    {
      "package": "colourama",
      "similarTo": "colorama",
      "editDistance": 2,
      "starRatio": 0.0041,
      "suspicionScore": 0.6620
    }
  ],
  "totalChecked": 28,
  "suspiciousCount": 2,
  "avgSuspicion": 0.4812
}
```

#### `detect_vulnerability_propagation` response

```json
{
  "propagations": [
    {
      "cve": "CVE-2021-44228",
      "severity": 1.0,
      "affectedRepos": 11,
      "propagationDepth": 4,
      "blastRadius": 0.7857
    },
    {
      "cve": "CVE-2022-23302",
      "severity": 0.88,
      "affectedRepos": 3,
      "propagationDepth": 2,
      "blastRadius": 0.2143
    }
  ],
  "totalVulns": 5,
  "criticalCount": 2,
  "avgBlastRadius": 0.3820
}
```

### Output fields

#### `score_supply_chain_risk`

| Field | Type | Description |
|-------|------|-------------|
| `assessments[].entity` | string | Repository name (max 50 chars) |
| `assessments[].riskScore` | number | Weighted composite risk score, 0-1 |
| `assessments[].riskLevel` | string | `low` / `medium` / `high` / `critical` |
| `assessments[].factors[].factor` | string | Factor name: `vulnerability_exposure`, `bus_factor`, `popularity`, `active_exploitation`, `community_signal` |
| `assessments[].factors[].weight` | number | Factor weight in composite score |
| `assessments[].factors[].score` | number | Factor raw score, 0-1 |
| `avgRisk` | number | Mean risk score across all assessed repositories |
| `criticalCount` | number | Count of repositories at critical risk level |

#### `generate_oss_risk_report`

| Field | Type | Description |
|-------|------|-------------|
| `findings[].category` | string | Finding category (e.g., Known Exploited Vulnerabilities) |
| `findings[].finding` | string | Human-readable finding summary |
| `findings[].severity` | string | `low` / `medium` / `high` / `critical` |
| `findings[].evidence` | string | Supporting CVE IDs or repository names |
| `findings[].recommendation` | string | Actionable remediation step |
| `overallRisk` | string | Aggregate report risk level |
| `reposScreened` | number | Count of repositories analyzed |
| `recommendations` | array | Top-level remediation recommendations list |

#### `detect_typosquat_indicators`

| Field | Type | Description |
|-------|------|-------------|
| `indicators[].package` | string | The suspected typosquat package name |
| `indicators[].similarTo` | string | The popular package it resembles |
| `indicators[].editDistance` | number | Levenshtein edit distance (1 or 2) |
| `indicators[].starRatio` | number | `stars(suspect) / stars(popular)`, 0-1 |
| `indicators[].suspicionScore` | number | Combined suspicion score, 0-1 |
| `totalChecked` | number | Total repository names compared |
| `suspiciousCount` | number | Count with `suspicionScore` above 0.6 |
| `avgSuspicion` | number | Mean suspicion score across all indicators |

#### `detect_vulnerability_propagation`

| Field | Type | Description |
|-------|------|-------------|
| `propagations[].cve` | string | CVE or KEV identifier |
| `propagations[].severity` | number | CVSS-derived severity, 0-1 |
| `propagations[].affectedRepos` | number | Downstream repos reachable via BFS |
| `propagations[].propagationDepth` | number | BFS depth reached (max 6) |
| `propagations[].blastRadius` | number | `affectedRepos / totalRepos`, 0-1 |
| `totalVulns` | number | Total CVE + KEV nodes analyzed |
| `criticalCount` | number | Propagations with severity above 0.8 |
| `avgBlastRadius` | number | Mean blast radius across all vulnerabilities |

#### `assess_maintainer_risk`

| Field | Type | Description |
|-------|------|-------------|
| `risks[].repo` | string | Repository name |
| `risks[].maintainerCount` | number | Confirmed maintainer relationships |
| `risks[].busFactor` | number | `min(1, maintainerCount / 5)`, 0-1 |
| `risks[].lastActivity` | string | ISO date of last push (YYYY-MM-DD) |
| `risks[].abandonmentRisk` | number | Combined bus factor and recency score, 0-1 |
| `totalRepos` | number | Total repositories assessed |
| `highRiskCount` | number | Repos with `abandonmentRisk` above 0.6 |
| `avgBusFactor` | number | Mean bus factor across all repos |

#### `map_dependency_network`

| Field | Type | Description |
|-------|------|-------------|
| `entries[].entity` | string | Node name (repo, CVE, maintainer, etc.) |
| `entries[].type` | string | Node type: `repo`, `maintainer`, `cve`, `kev`, `package`, `discussion` |
| `entries[].connections` | number | Total edge count (in + out) |
| `entries[].stars` | number | Star count (repos only; 0 for other types) |
| `entries[].severity` | number | CVSS severity 0-1 (CVE/KEV nodes only) |
| `totalEntities` | number | Total nodes in the network |
| `totalEdges` | number | Total edges in the network |
| `avgConnections` | number | Mean edge degree across all nodes |

#### `analyze_community_health`

| Field | Type | Description |
|-------|------|-------------|
| `assessments[].repo` | string | Repository name |
| `assessments[].discussionVolume` | number | Count of Hacker News/StackExchange discussions linked to this repo |
| `assessments[].sentimentScore` | number | Fork-to-star ratio proxy, 0-1 |
| `assessments[].issueResponseTime` | number | Inverse issue-to-star ratio proxy, 0-1 |
| `assessments[].healthIndex` | number | Composite health score, 0-1 |
| `totalAssessed` | number | Total repositories assessed |
| `unhealthyCount` | number | Repos with `healthIndex` below 0.3 |
| `avgHealth` | number | Mean health index across all repos |

#### `assess_research_security_landscape`

| Field | Type | Description |
|-------|------|-------------|
| `topics[].topic` | string | Security topic keyword |
| `topics[].paperCount` | number | Total papers and discussions mentioning this topic |
| `topics[].recentActivity` | number | Papers/discussions published in the last 90 days |
| `topics[].trendScore` | number | `recentActivity / paperCount`, 0-1 |
| `topics[].keyFindings` | array | Up to 3 representative paper/discussion titles |
| `totalPapers` | number | Total papers and discussions analyzed |
| `trendingCount` | number | Topics with `trendScore` above 0.5 |
| `avgTrend` | number | Mean trend score across all topics |

### How much does it cost to run open source supply chain risk analysis?

This MCP uses **pay-per-event pricing** — you pay **$0.035 per tool call**. Platform compute costs are included.

| Scenario | Tool calls | Cost per call | Total cost |
|----------|-----------|---------------|------------|
| Quick test (single package risk score) | 1 | $0.035 | $0.035 |
| Full risk report (all 7 sources) | 1 | $0.035 | $0.035 |
| Complete due diligence (score + maintainer + blast radius) | 3 | $0.035 | $0.105 |
| Daily monitoring of 20 packages | 20 | $0.035 | $0.70 |
| Weekly full audit of 100 dependencies | 100 | $0.035 | $3.50 |

You can set a **maximum spending limit** per run to control costs. The actor stops when your budget is reached, preventing unexpected charges during long AI sessions.

Compare this to Snyk at $25-98/month or Mend (WhiteSource) at $10k+/year for enterprise teams — most users running OSS supply chain checks with this MCP spend under $5/month with no subscription commitment. Apify's free tier includes $5 of monthly credits, covering roughly 140 tool calls per month.

### How to connect using the API

#### Claude Desktop

Add to your `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "open-source-supply-chain-risk": {
      "url": "https://open-source-supply-chain-risk-mcp.apify.actor/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_APIFY_TOKEN"
      }
    }
  }
}
```

#### Python

```python
import httpx, json

MCP_URL = "https://open-source-supply-chain-risk-mcp.apify.actor/mcp"
TOKEN   = "YOUR_APIFY_TOKEN"

payload = {
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
        "name": "generate_oss_risk_report",
        "arguments": {
            "query": "log4j",
            "sources": ["github", "cve", "kev", "hackernews", "stackexchange", "arxiv", "censys"]
        }
    },
    "id": 1
}

response = httpx.post(
    MCP_URL,
    json=payload,
    headers={"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"},
    timeout=60
)

result = response.json()
report = json.loads(result["result"]["content"][0]["text"])

print(f"Overall risk: {report['overallRisk']}")
print(f"Repos screened: {report['reposScreened']}")
for finding in report["findings"]:
    print(f"[{finding['severity'].upper()}] {finding['category']}: {finding['finding']}")
```

#### JavaScript

```javascript
const MCP_URL = "https://open-source-supply-chain-risk-mcp.apify.actor/mcp";
const TOKEN   = "YOUR_APIFY_TOKEN";

const response = await fetch(MCP_URL, {
    method: "POST",
    headers: {
        "Authorization": `Bearer ${TOKEN}`,
        "Content-Type": "application/json"
    },
    body: JSON.stringify({
        jsonrpc: "2.0",
        method: "tools/call",
        params: {
            name: "score_supply_chain_risk",
            arguments: {
                query: "axios",
                sources: ["github", "cve", "kev", "hackernews", "stackexchange"]
            }
        },
        id: 1
    })
});

const data = await response.json();
const result = JSON.parse(data.result.content[0].text);

for (const assessment of result.assessments) {
    console.log(`${assessment.entity}: ${assessment.riskLevel} (${assessment.riskScore.toFixed(3)})`);
}
console.log(`Critical packages: ${result.criticalCount}`);
```

#### cURL

```bash
## Call score_supply_chain_risk
curl -X POST "https://open-source-supply-chain-risk-mcp.apify.actor/mcp" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_APIFY_TOKEN" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "score_supply_chain_risk",
      "arguments": {
        "query": "express",
        "sources": ["github", "cve", "kev", "hackernews", "stackexchange"]
      }
    },
    "id": 1
  }'

## List all available tools
curl -X POST "https://open-source-supply-chain-risk-mcp.apify.actor/mcp" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_APIFY_TOKEN" \
  -d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":1}'
```

### How Open Source Supply Chain Risk MCP works

#### Phase 1: Parallel data collection with caching

When a tool is called, the server checks a `Map`-based in-process cache for each `source:query` composite key. Cache entries are valid for 5 minutes (TTL = 300,000 ms). On a cache miss, the server fires parallel calls to the required Apify wrapper actors — up to 7 running simultaneously via `Promise.all`. Each wrapper actor runs with a 180-second timeout and returns up to 500 items from its Apify dataset.

The 7 data source actors:

- `github` — `ryanclinton/github-repo-search` — returns repository metadata including `fullName`, `stars`, `owner`, `language`, `pushedAt`, `forksCount`, `openIssuesCount`
- `cve` — `ryanclinton/nvd-cve-vulnerability-search` — returns CVE records with `cveId`, `cvssScore`, `description`, affected configurations
- `kev` — `ryanclinton/cisa-kev-search` — returns CISA KEV entries with `cveID` and confirmed exploitation status
- `hackernews` — `ryanclinton/hackernews-search` — returns story titles and metadata for supply chain discussions
- `stackexchange` — `ryanclinton/stackexchange-search` — returns question titles signaling developer adoption and migration patterns
- `arxiv` — `ryanclinton/arxiv-paper-search` — returns paper titles and publication dates for emerging threat research
- `censys` — `ryanclinton/censys-search` — returns host records for internet-facing infrastructure exposure

#### Phase 2: Typed network graph construction

`buildOSSNetwork()` in `dependency-network.ts` merges all source results into a typed graph. Up to 80 repositories become `repo` nodes with star counts and raw metadata. Each repository's owner becomes a `maintainer` node with a `maintains` edge at weight 0.9. Repository language becomes a `package` node with a `depends_on` edge at weight 0.5.

CVE nodes are cross-referenced against repository names by substring search in CVE description text (repository short names longer than 3 characters). A match triggers an `affects` edge at weight 0.7. KEV nodes link to their corresponding CVE nodes at weight 1.0, enabling full CVE-to-KEV traversal in blast radius analysis.

Hacker News and StackExchange items become `discussion` nodes. Discussion titles are matched against repository short names (the component after `/`) to create `discusses` edges at weight 0.5. ArXiv papers create `paper` nodes processed by the research landscape function.

#### Phase 3: Analytical model application

Each of the 8 tools applies a distinct model to the unified network graph:

**`score_supply_chain_risk`** — applies the 5-factor weighted formula per `repo` node. Vulnerability exposure = `min(1, vulnLinks * 0.2)`. Bus factor = `max(0, 1 - maintainerCount * 0.2)`. Popularity = `1 - min(1, log10(max(1, stars)) / 5)`. Active exploitation = 0.9 if any KEV node is linked, else 0.1. Community signal = 0.3 if discussions exist, else 0.7. Risk level thresholds: critical ≥ 0.75, high ≥ 0.50, medium ≥ 0.25.

**`detect_vulnerability_propagation`** — BFS from each CVE/KEV node through the full edge set (all 6 edge types), up to depth 6. Counts reached `repo:` prefixed node IDs. Blast radius = affected repo count / total repo nodes.

**`detect_typosquat_indicators`** — O(n²) pairwise Levenshtein distance across all `repo` short names (split on `/`, lowercased). Pairs with distance 1-2 and absolute length difference ≤ 2 are evaluated. The `suspicious` package is the lower-star repository in each pair. Results sorted by suspicion score descending; top 20 returned.

**`generate_oss_risk_report`** — synthesizes findings in three categories: KEV presence triggers a `critical` finding with CISA BOD 22-01 guidance; CVSS ≥ 9.0 CVEs (`severity ≥ 0.9` after normalization) trigger a `critical` finding; more than 3 repos with ≤ 1 `maintains` edge trigger a `high` finding. Findings sorted by `{critical: 0, high: 1, medium: 2, low: 3}` enum order.

#### Phase 4: Response assembly

All tools return structured JSON wrapped in an MCP `content` array with `type: "text"`. Results are capped at 30 entries for most tools to keep response payloads LLM-friendly. The server handles upstream actor failures gracefully — a failed or timed-out actor returns an empty array and analysis proceeds with the remaining sources.

### Tips for best results

1. **Run `generate_oss_risk_report` first.** It queries all 7 sources and returns severity-sorted findings in one $0.035 call. Use the findings to decide which focused tools to call next — only spend the additional $0.035 per tool when the report identifies a risk category worth drilling into.

2. **Query by exact package name, not project name.** `log4j-core` matches more CVE descriptions than `log4j`. `pallets/flask` finds the correct repository faster than just `flask`, which may match unrelated repositories with similar names.

3. **Use the 5-minute cache deliberately.** If you call `score_supply_chain_risk` and then `assess_maintainer_risk` on the same package within 5 minutes, the second call reuses cached upstream data and costs only the $0.035 event charge. Structure your AI session to group calls on the same package together.

4. **Add `censys` only for KEV-confirmed vulnerabilities.** Censys data is useful for understanding real-world deployment of vulnerable versions but adds latency to the call. Reserve it for packages where the report has already confirmed active exploitation.

5. **Use `arxiv` for pre-disclosure threat hunting.** Research papers on specific ecosystems (npm, PyPI, Maven Central) often appear 4-8 weeks before formal CVE assignment. A weekly scheduled call to `assess_research_security_landscape` can give your security team advance warning of emerging attack patterns.

6. **Cross-reference typosquat results manually before blocking.** The Levenshtein algorithm flags statistical anomalies, not confirmed malware. Always verify a high-suspicion package on its registry page before adding it to a CI/CD block list. A new legitimate fork of a popular library will look identical to a typosquat by this metric.

7. **Use broadcast queries for ecosystem monitoring.** Queries like `npm`, `PyPI`, or `Maven` return a diverse set of repositories and CVEs, enabling broader dependency network mapping than single-package queries. Useful for periodic ecosystem-level risk assessments.

### Combine with other Apify actors

| Actor | How to combine |
|-------|----------------|
| [Website Tech Stack Detector](https://apify.com/ryanclinton/website-tech-stack-detector) | Detect which OSS libraries a vendor's website uses, then pass those library names to `score_supply_chain_risk` for immediate risk assessment |
| [Company Deep Research](https://apify.com/ryanclinton/company-deep-research) | Combine vendor OSS supply chain risk with broader company intelligence during third-party due diligence workflows |
| [WHOIS Domain Lookup](https://apify.com/ryanclinton/whois-domain-lookup) | Cross-check suspicious typosquat package domains against domain registration data to identify malicious ownership patterns |
| [Website Content to Markdown](https://apify.com/ryanclinton/website-content-to-markdown) | Convert a vendor's security advisories page to markdown and feed it alongside MCP risk data to your AI agent for a unified assessment |
| [Trustpilot Review Analyzer](https://apify.com/ryanclinton/trustpilot-review-analyzer) | Combine developer community trust signals from review platforms with OSS supply chain risk scores for a fuller vendor evaluation |
| [B2B Lead Qualifier](https://apify.com/ryanclinton/b2b-lead-qualifier) | Screen prospective technology partners by their OSS dependency health before investing in a partnership evaluation |
| [Website Change Monitor](https://apify.com/ryanclinton/website-change-monitor) | Monitor a vendor's security advisory page for changes; trigger `generate_oss_risk_report` automatically when new advisories appear |

### Limitations

- **No static code analysis** — the server analyzes repository metadata, vulnerability databases, and community signals, not source code. For code-level SAST scanning, use dedicated tools like Semgrep or CodeQL alongside this MCP.
- **GitHub-centric repository discovery** — the dependency graph is built from GitHub search results. Private packages, internal mirrors, and registries without significant GitHub presence (private npm scopes, internal Maven repositories) are not visible.
- **No transitive dependency resolution** — the server identifies which repositories are associated with a query term; it does not parse `package.json`, `requirements.txt`, or `pom.xml` to recursively walk the full dependency tree.
- **Levenshtein typosquat detection requires multiple results** — typosquat analysis compares packages within the search result set. Queries returning fewer than 5 repositories have limited pairwise coverage. Use ecosystem-level queries (`requests python`) rather than single-package queries for better coverage.
- **CVE-to-repo matching is text-based** — the server matches CVE description text against repository short names as substrings. Repositories with generic names (`core`, `utils`) may generate false positive CVE associations. Repositories whose names never appear in CVE text will show no vulnerability exposure even if genuinely affected.
- **Censys data reflects scanning snapshots** — Censys results represent hosts detected at scan time, not a real-time deployment inventory. Rapidly patched deployments may not yet be reflected in scan results.
- **Cache is per-instance** — the 5-minute TTL cache is in-process memory. Each new Apify Standby instance starts cold. High-concurrency environments with many simultaneous AI sessions may see elevated costs from cache misses across parallel instances.
- **Up to 500 items per source** — each upstream actor returns a maximum of 500 items. Very popular packages (`linux`, `openssl`) may have thousands of related repositories and CVEs; analysis is limited to the first 500 results per source.

### Integrations

- [Apify API](https://docs.apify.com/api/v2) — call this MCP server programmatically from any language via direct HTTP POST to the `/mcp` endpoint
- [Webhooks](https://docs.apify.com/platform/integrations/webhooks) — trigger Slack or PagerDuty alerts when `generate_oss_risk_report` returns `overallRisk: critical` for a monitored package
- [Zapier](https://apify.com/integrations/zapier) — connect new CISA KEV catalog additions to automated supply chain risk scoring workflows without writing code
- [Make](https://apify.com/integrations/make) — build multi-step security workflows: detect new CVEs, score affected packages, create Jira tickets for high-risk findings
- [LangChain / LlamaIndex](https://docs.apify.com/platform/integrations) — integrate this MCP as a callable tool in your LLM agent for automated security research and dependency vetting pipelines
- [Claude Desktop](https://claude.ai/download) — add the MCP URL to `claude_desktop_config.json` for interactive supply chain risk conversations directly in Claude

### Troubleshooting

- **Tool returns empty `assessments` array** — the query returned no GitHub repositories matching the search term. Try a shorter or more generic query: `log4j` instead of `org.apache.logging.log4j:log4j-core`. Confirm that `github` is included in the `sources` array, since most analysis tools require `repo` nodes to generate assessments.

- **Typosquat analysis shows no indicators** — the search returned fewer than 2 repositories, making pairwise comparison impossible. Use a broader query that matches an ecosystem or package family. For example, `colorama` returns the popular package plus related projects for comparison.

- **Risk scores appear lower than expected for a known-vulnerable package** — CVE-to-repo matching is text-based and may miss packages whose repository name does not appear in CVE description text. Try calling `detect_vulnerability_propagation` directly with the CVE ID as the query (e.g., `CVE-2021-44228`) to anchor the analysis on the specific vulnerability node.

- **Tool call times out** — one of the 7 upstream actors failed to respond within the 180-second timeout. The server returns empty arrays for failed actors gracefully; partial results are still returned. Retry the call. Reduce `sources` to the 4 fastest sources (`github`, `cve`, `kev`, `hackernews`) if latency is a persistent constraint.

- **`spending limit reached` error in tool response** — your Apify account has hit the per-run spending limit or monthly credit cap. Go to [Apify Console](https://console.apify.com) to add credits or increase the spending limit. The $5 free tier covers roughly 140 tool calls per month.

### Responsible use

- This MCP only accesses publicly available data from open databases (NVD, CISA KEV, GitHub public repositories, ArXiv, Hacker News, StackExchange, Censys).
- NVD and CISA KEV are official US government databases provided for security research and remediation purposes.
- Do not use this tool to identify vulnerabilities in systems you do not have authorization to assess.
- Typosquat detection results are statistical indicators, not confirmed malicious package designations. Do not publicly accuse package authors of malicious intent based solely on Levenshtein similarity scores.
- For guidance on web scraping and data collection legality, see [Apify's guide](https://blog.apify.com/is-web-scraping-legal/).

### FAQ

**How many tool calls does a complete open source supply chain risk audit require?**
A thorough audit of a single package typically uses 3-4 tool calls: `generate_oss_risk_report` for the overview ($0.035), `score_supply_chain_risk` for the factor breakdown ($0.035), and `detect_vulnerability_propagation` if KEVs are present ($0.035). Total: $0.105-$0.14 per package. A 50-package quarterly audit costs roughly $5-$7.

**Does this MCP scan actual package source code for malware or backdoors?**
No. It analyzes metadata, vulnerability databases, and community signals — not source code. The server detects risk indicators such as high CVSS scores, CISA KEV status, low bus factor, and naming anomalies. For code-level inspection, combine this MCP with a dedicated SAST tool like Semgrep or CodeQL.

**How does the open source supply chain risk score work exactly?**
The `score_supply_chain_risk` tool applies a 5-factor weighted model per repository: vulnerability exposure (30%), bus factor (20%), active exploitation via CISA KEV (20%), popularity/attack surface (15%), and community discussion signal (15%). Each factor produces a 0-1 score. The weighted sum determines risk level: critical ≥ 0.75, high ≥ 0.50, medium ≥ 0.25, low < 0.25.

**How is this different from Snyk or Mend (WhiteSource)?**
Snyk and Mend are CI/CD-integrated SCA platforms with proprietary vulnerability databases, license compliance scanning, and IDE plugins. This MCP is an AI-native intelligence tool for interactive risk assessment, research, and due diligence workflows. It connects to your AI agent rather than your build pipeline. The two approaches are complementary: use Snyk/Mend in CI/CD and this MCP for investigative analysis in AI sessions.

**How accurate is the blast radius estimation?**
Blast radius is a graph-theoretic approximation based on available public data. It reflects connectivity in the observed dependency graph, not the complete transitive dependency tree across all package registries. Packages with many GitHub forks and community discussions show higher blast radius than equally risky packages with low public visibility. Treat it as a relative risk indicator, not an exact downstream impact count.

**Can I use this MCP to monitor open source supply chain risk on a schedule?**
Yes. Scheduled monitoring is best done via the Apify API: POST to the actor endpoint with your target packages, process the JSON output, and route high-severity findings to your alerting system via webhooks. You can also use a standard scheduled Apify actor to call this MCP's HTTP endpoint at regular intervals.

**Is it legal to collect this open source security data?**
Yes. All data sources are publicly available. NVD and CISA KEV are official US government databases. GitHub public repository metadata is openly accessible per GitHub's Terms of Service for research and development. Hacker News, StackExchange, and ArXiv are open public platforms. Censys provides internet scanning data under a research-use license.

**What happens if one of the 7 data sources fails during a tool call?**
The server handles upstream failures gracefully. If an actor times out or returns an error, its result is replaced with an empty array and analysis continues with the remaining sources. You receive partial results rather than an error response. The tool event charge still applies since the tool executed.

**How does the caching work and does it affect my costs?**
The 5-minute in-process cache prevents duplicate upstream charges within a single AI session. If you call `score_supply_chain_risk` on `log4j` and then `assess_maintainer_risk` on `log4j` within 5 minutes, the second call reuses the cached upstream data from the first call. You pay the $0.035 tool event charge per call, but not a second round of upstream data fetching. Cache is per-instance; each new Apify Standby instance starts cold.

**Can I connect this to Cursor, Windsurf, or other coding assistants?**
Yes. Any MCP-compatible client that supports Streamable HTTP transport works with the `/mcp` endpoint. For SSE-based clients (older MCP implementations), use the `/sse` endpoint instead. Pass your Apify token in the `Authorization: Bearer` header.

**What is the difference between open source supply chain risk analysis and a traditional SBOM tool?**
Traditional SBOM tools (CycloneDX, SPDX generators) enumerate your dependencies from build manifests. This MCP scores the risk of those dependencies using live intelligence: what vulnerabilities are actively exploited today, which maintainers have gone inactive, which packages exhibit typosquat characteristics. Use an SBOM tool to list what you have; use this MCP to score how risky it is.

**How does open source supply chain risk analysis detect typosquats across ecosystems?**
The `detect_typosquat_indicators` tool computes pairwise Levenshtein edit distance across all repository short names returned for a query. Pairs with distance 1-2 and a large star count disparity (low `starRatio`) score high on suspicion. A query like `requests` surfaces similar package names from the GitHub search results; the tool then compares each pair and ranks by suspicion. For broader ecosystem scanning, use queries like `requests python` to pull in more comparison candidates.

### Help us improve

If you encounter issues, you can help us debug faster by enabling run sharing in your Apify account:

1. Go to [Account Settings > Privacy](https://console.apify.com/account/privacy)
2. Enable **Share runs with public Actor creators**

This lets us see your run details when something goes wrong, so we can fix issues faster. Your data is only visible to the actor developer, not publicly.

### Support

Found a bug or have a feature request? Open an issue in the [Issues tab](https://console.apify.com/actors/open-source-supply-chain-risk-mcp/issues) on this actor's page. For custom integrations or enterprise security workflows, reach out through the Apify platform.

# Actor input Schema

## Actor input object example

```json
{}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("ryanclinton/open-source-supply-chain-risk-mcp").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("ryanclinton/open-source-supply-chain-risk-mcp").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call ryanclinton/open-source-supply-chain-risk-mcp --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=ryanclinton/open-source-supply-chain-risk-mcp",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Open Source Supply Chain Risk MCP",
        "description": "OSS supply chain risk intelligence with dependency graph propagation, bus-factor analysis, and typosquat detection.",
        "version": "1.0",
        "x-build-id": "u3AylwuAB9MQXLKDD"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/ryanclinton~open-source-supply-chain-risk-mcp/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-ryanclinton-open-source-supply-chain-risk-mcp",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/ryanclinton~open-source-supply-chain-risk-mcp/runs": {
            "post": {
                "operationId": "runs-sync-ryanclinton-open-source-supply-chain-risk-mcp",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/ryanclinton~open-source-supply-chain-risk-mcp/run-sync": {
            "post": {
                "operationId": "run-sync-ryanclinton-open-source-supply-chain-risk-mcp",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {}
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
