PI

Pinner MCP Server: Pin GitHub Actions, Image SHAs

Pin GitHub Actions and container base images to immutable SHA hashes with an MCP server to prevent supply chain attacks.

Quick Install
npx -y @safedep/pinner-mcp

Overview

Pinner MCP Server provides a simple, local network service to resolve and lock external dependencies used in CI/CD and model deployment workflows. It pins GitHub Actions and container base images to immutable commit SHAs or image digests, returning a stable reference that can be consumed by tooling and automated pipelines. By converting floating tags (for example, actions@v2 or ubuntu:22.04) into immutable SHAs, the server helps reduce the risk of supply-chain changes breaking builds or introducing malicious updates.

The server implements a small HTTP API compatible with Model Context Protocol (MCP) patterns, letting clients ask for a pinned reference and optionally obtain a verification record. It can be run as a single binary or in a container, and is designed to fit into existing CI workflows (GitHub Actions, build pipelines, container build systems) so teams can enforce reproducible, auditable dependency resolution.

Features

  • Pin GitHub Actions to commit SHAs (e.g., actions/checkout@v2 -> actions/checkout@)
  • Pin container base images to digest (e.g., ubuntu:22.04 -> ubuntu@sha256:)
  • HTTP API compatible with MCP-style resolution calls
  • Local cache of resolved pins to reduce network lookups and speed CI runs
  • Optional TTL and refresh configuration for pins
  • CLI and Docker images for easy adoption in CI/CD
  • Simple JSON responses suitable for automated tooling and scripts
  • Audit-friendly outputs (timestamp, source, resolved SHA/digest)
  • Installation / Configuration

    Prerequisites: Go 1.18+ to build from source, or Docker to run prebuilt container.

    Build from source:

    git clone https://github.com/safedep/pinner-mcp.git
    cd pinner-mcp
    make build                 # or `go build ./cmd/pinner-mcp`
    ./pinner-mcp --help
    

    Run with Docker:

    docker run -d --name pinner-mcp \
      -p 8080:8080 \
      -e PINNER_CACHE_DIR=/data/cache \
      -v $(pwd)/data:/data \
      ghcr.io/safedep/pinner-mcp:latest
    

    Example Docker Compose:

    version: "3.7"
    services:
      pinner:
        image: ghcr.io/safedep/pinner-mcp:latest
        ports:
          - "8080:8080"
        environment:
          - PINNER_CACHE_DIR=/data/cache
          - PIN_TTL=168h       # 1 week
        volumes:
          - ./data:/data
    

    Configuration file (optional, YAML):

    server:
      bind: "0.0.0.0:8080"
    cache:
      dir: "/data/cache"
      ttl: "168h"
    github:
      token: "ghp_..."   # optional, speeds up API queries and increases rate limits
    

    Start with config:

    ./pinner-mcp --config ./config.yaml
    

    Available Resources

    Common HTTP endpoints (examples):

    EndpointMethodDescription
    /resolveGETResolve a resource to an immutable pin. Query params: type (action
    /pinPOSTRequest a pin and store cache entry. Body: JSON { type, ref }
    /statusGETServer health and cache statistics
    /audit/{id}GETFetch audit record for a previous resolution

    Example resolve request:

    curl "http://localhost:8080/resolve?type=action&ref=actions/checkout@v2"
    

    Example response:

    {
      "type": "action",
      "source": "github",
      "original_ref": "actions/checkout@v2",
      "pinned_ref": "actions/checkout@c1a2b3d4e5f6...",
      "resolved_at": "2026-04-01T12:00:00Z"
    }
    

    Use Cases

    • Pinning GitHub Actions in workflows:
      • Before: uses: actions/checkout@v2
      • After resolving with Pinner MCP: uses: actions/checkout@c1a2b3d4e5f6…
      • Integration: Use a CI step that queries the MCP server and rewrites or validates the workflow file, failing builds if unpinned actions are detected.

    Example GitHub Actions step to fetch a pinned action:

    steps:
      - name: Resolve pinned action
        run: |
          PIN=$(curl -s "http://pinner:8080/resolve?type=action&ref=actions/checkout@v2" | jq -r .pinned_ref)
          echo "Resolved: $PIN"
    
    • Pinning container base images:
      • Dockerfile before: FROM ubuntu:22.04
      • After: FROM ubuntu@sha256:abcdef1234…
      • Use the server as a pre-step in your image build process to fetch digests and substitute FROM lines, ensuring reproducible builds.

    Example shell script to pin an image:

    ORIG="ubuntu:22.04"
    PIN=$(curl -s "http://localhost:8080/resolve?type=image&ref=${ORIG}" | jq -r .pinned_ref)
    sed -i "s|FROM ${ORIG}|FROM ${PIN}|" Dockerfile
    docker build -t myapp:stable .
    
    • Auditing and compliance: Store /audit records from the server to demonstrate which exact SHAs/digests were used for builds and when they were resolved.

    Getting help and contributing

    Repository and source code: https://github.com/safedep/pinner-mcp

    Open issues or pull requests on GitHub for bugs, feature requests, or integration questions. The project welcomes contributions to add more resolution backends, authentication options, and richer audit/logging formats.

    Common Issues & Solutions

    The project safedep/pinner-mcp is listed on Spark, but the maintainer hasn't claimed it yet.

    ✓ Solution

    I ran into this too! Claiming the listing on Spark is important for getting that 'Maintainer Verified' badge and being able to manage the project’s presence on the platform. It allows you to edit details, access analytics, and add badges to your README, which enhances visibility and credibility. Just follow the steps provided in the issue to claim it. You’ll be all set in no time!

    npm install @ChromeDevTools/chrome-devtools-mcp