Skip to content

ADR-0166: MCP server release artifact channel — PyPI + GitHub release attachment + Sigstore (T7-2)

  • Status: Accepted (SLSA generator provenance superseded by ADR-1356)
  • Date: 2026-04-25
  • Deciders: Lusoris
  • Tags: release, mcp, supply-chain, sigstore, pypi

2026-09-29 amendment (ADR-1356): the wheel and sdist provenance no longer comes from slsa-github-generator. The mcp-provenance job attests both files with the SHA-pinned actions/attest-build-provenance and the release carries vmaf-mcp-provenance.sigstore.json. PyPI Trusted Publishing, the PEP 740 attestations and the cosign bundles are unchanged.

2026-08-31 operational amendment: the repository was transferred to VMAFx/vmafx, and ADR-1127 superseded the suffixed tag scheme with one ordinary vX.Y.Z stream. The exact current Trusted Publisher identity below replaces the historical lusoris/vmaf values.

Context

The fork ships an MCP (Model Context Protocol) server at mcp-server/vmaf-mcp/ that exposes VMAF scoring, model listing, and benchmark runs over JSON-RPC. The server is a Python package (vmaf-mcp, pyproject.toml declares >=3.10, optional eval extras pull in ONNX / pandas / scipy).

Today the package has no release channel:

  • No pypi upload — pip install vmaf-mcp returns "no matching distribution".
  • No GitHub release attachment — release tags publish libvmaf.so + vmaf CLI + models.tar.gz (see supply-chain.yml) but skip the MCP server.
  • No signature, no provenance, no SBOM for the MCP layer.

BACKLOG T7-2 flagged the gap as "PyPI vs GitHub release attachment vs both" — pending an ADR plus release.yml wiring.

Decision

Ship both channels with full Sigstore + SLSA provenance.

  1. PyPI via Trusted Publishing (OIDC, no token in CI).
  2. Distribution name: vmaf-mcp
  3. Trigger: release: published events for ordinary vX.Y.Z tags.
  4. Builds wheel + sdist via python -m build.
  5. Publishes via pypa/gh-action-pypi-publish with attestations (PEP 740 — --attestations flag emits Sigstore-signed provenance the index stores alongside the artifact).
  6. GitHub release attachment alongside the existing libvmaf artifacts.
  7. Same wheel + sdist as PyPI.
  8. Cosign keyless .bundle (matches the libvmaf pattern in supply-chain.yml).
  9. SBOM (SPDX + CycloneDX) covering the Python dependency tree.
  10. SLSA L3 provenance — wheel + sdist hashes feed the existing slsa-github-generator reusable workflow alongside the libvmaf artifacts.

Both channels are wired in the existing supply-chain.yml workflow under a new mcp-build + mcp-publish job pair, not a separate workflow file. Reusing the workflow keeps the per-release matrix (build → SBOM → sign → SLSA → attach) coherent across libvmaf + MCP.

User-facing install paths after this ships:

# Recommended: PyPI
pip install vmaf-mcp

# Pinnable to a libvmaf release (signature-verifiable)
gh release download vX.Y.Z --pattern 'vmaf_mcp-*.whl'
cosign verify-blob \
  --bundle vmaf_mcp-X.Y.Z-py3-none-any.whl.bundle \
  vmaf_mcp-X.Y.Z-py3-none-any.whl
pip install vmaf_mcp-X.Y.Z-py3-none-any.whl

Alternatives considered

  1. GitHub release attachment only. Avoids PyPI namespace reservation + the maintainer burden of a Trusted Publishing configuration. Rejected because it leaves pip install vmaf-mcp broken, which is the discovery path most MCP integrators expect.
  2. PyPI only. Loses the "pin to a libvmaf release" guarantee: PyPI versions advance independently from libvmaf release tags unless we couple them, which adds complexity. The GitHub attachment path is also a no-cost extension of the existing supply-chain.yml so adding it is essentially free.
  3. Conda-forge / homebrew. Out of scope for this ADR; can be added later. PyPI is the canonical Python channel and matches the pyproject.toml declaration already shipped.
  4. Token-based PyPI publish (__token__ + secret). Rejected for OpenSSF Scorecard reasons — Trusted Publishing via OIDC eliminates the long-lived secret entirely. The fork's scorecard.yml already advertises Sigstore-signed dashboard attestations; staying OIDC-only across release surfaces is consistent.

Consequences

Positive:

  • Discovery: pip install vmaf-mcp works from day one of the next release.
  • Provenance: every wheel + sdist carries a Sigstore bundle and PEP 740 attestation; consumers can verify offline.
  • No long-lived secrets: Trusted Publishing requires no PyPI token in repo settings.
  • One workflow surface: extending supply-chain.yml keeps the release matrix coherent.

Negative:

  • One-time setup: a Pending Trusted Publisher entry must be configured on PyPI (organisation VMAFx, repository vmafx, workflow supply-chain.yml, environment pypi-publish). This is a user-driven UI step — see "Operational notes" below.
  • Initial namespace reservation: until the first release, PyPI's vmaf-mcp name is unreserved and could be squatted. The Pending Trusted Publisher first-publish flow reserves it without a token or manual upload.
  • SBOM growth: covering the Python dependency tree adds ~40 kB of metadata per release; insignificant compared to libvmaf.

Operational notes

One-time setup (completed on 2026-08-31 before the first release):

  1. Log in to https://pypi.org/manage/account/publishing/ as the project owner.
  2. Add a Pending Trusted Publisher with:
  3. PyPI Project Name: vmaf-mcp
  4. Owner: VMAFx
  5. Repository: vmafx
  6. Workflow: supply-chain.yml
  7. Environment: pypi-publish

The first release: published event after the publisher is configured completes the publisher binding automatically; subsequent releases require no further user interaction.

References