image-delta-mcp

image-delta-mcp

Enables comparing container image versions to see package and CVE changes, including shedding/introductions, and comparing Chainguard images to upstream.

Category
访问服务器

README

image-delta-mcp — UNOFFICIAL: built as interview prep, not affiliated with or endorsed by Chainguard.

An MCP server over Chainguard's public, free-tier image data that answers, inside Claude Code:

"What changed between these two versions of this image, and what CVEs did we shed?"

It shells out to three standard supply-chain tools — crane (tags/digests/manifests), cosign (SPDX SBOM attestations), and grype (registry-direct CVE scanning) — wraps them in strict validation, rate limiting, and a digest-keyed cache, and exposes four small tools to any MCP client.

Guardrails (why this exists and what it will not do)

  • Unofficial. Interview-prep project. Not affiliated with, endorsed by, or representing Chainguard in any way.
  • Free-tier public data only. cgr.dev free-tier images, their public Sigstore attestations, and public upstream registries. No gated catalog access, no scraping.
  • Polite to registries. Every registry-touching call goes through a token bucket (4 burst, 1/s refill) and a digest-keyed on-disk cache under .cache/, so repeated queries do not hammer cgr.dev or Docker Hub.
  • Honest about gaps. Missing SBOMs, unresolvable platforms, free-tier tag limitations, and scanner-database freshness are reported in data_gaps fields — never papered over as empty-but-successful results.

The four tools

Tool Question it answers
list_versions(image) What tags exist, with digests and created dates — plus how many historical digests are publicly recoverable from signature tags.
diff_packages(image, ref_a, ref_b) Package-level delta (added / removed / version-changed) between two refs, from the SPDX SBOM attestations attached to each digest.
diff_cves(image, ref_a, ref_b) CVEs shed and introduced between two refs, from identical registry-direct grype scans.
compare_to_upstream(image, upstream_ref) The money tool: a Chainguard image vs. its upstream equivalent, CVE counts side by side.

Refs are tags (latest) or digests (sha256:<64 hex>); platform defaults to linux/amd64 (linux/arm64 supported).

Real demo results (run 2026-07-21, grype DB of 2026-07-21)

These are actual outputs from npm run smoke on this machine — no numbers below are invented, and they will drift as images and the vulnerability database update.

1. "What CVEs did we shed?" — cgr.dev/chainguard/node, an older digest vs latest

Older ref sha256:0029ab60fc5a… (created 2023-01-14) → latest sha256:c002402b3552… (created 2026-07-17):

454 CVEs → 2 CVEs
447 CVE instances shed (16 Critical, 127 High, 100 Medium, 15 Low, 189 Unknown)
1 introduced (1 Medium), 1 retained
packages: 189 added, 2 removed, 17 version-changed

The older digest was recovered from public signature tags (sha256-*.sig) — the free tier exposes only rolling tags, but 7,697 historical index digests of node are publicly enumerable and diffable this way.

2. Chainguard vs upstream — cgr.dev/chainguard/nginx vs docker.io/library/nginx:latest

cgr.dev/chainguard/nginx:latest      0 CVEs
docker.io/library/nginx:latest     340 CVEs (21 Critical, 58 High, 86 Medium, 7 Low, 103 Negligible, 65 Unknown)

Same scanner, same database, same platform (linux/amd64), both registry-direct — an apples-to-apples scanner comparison, not an official vendor count.

The 3-minute demo script (inside Claude Code)

  1. "What CVEs did we shed moving chainguard/node from sha256:0029ab60fc5a… to latest?" → clean shed/introduced delta with the package changes behind it.
  2. "Compare chainguard/nginx to docker.io/library/nginx." → the zero-CVE claim reproduced live from public data by a tool an agent can call.
  3. Close: built in a day on hardened template conventions, 144 tests, externally reviewed — and an agent can call this before choosing a base image.

Architecture

Claude Code (stdio, primary)          optional: Streamable HTTP (127.0.0.1, Bearer auth)
        │                                        │
        └────────────► McpServer (4 tools, zod-validated inputs)
                           │
             validate.ts   │  tight regexes for image/tag/digest/platform,
             (before any   │  checked BEFORE anything is spawned
              spawn)       ▼
                       exec.ts ── execFile ONLY, binary allowlist {crane, cosign, grype},
                           │      per-arg character checks, hard timeouts + SIGKILL,
                           │      bounded output buffers
                           ▼
            rate-limit.ts (token bucket) ──► crane / cosign / grype ──► registries
                           │
                       cache.ts — digest-keyed on-disk cache (.cache/):
                       immutable entries for digest-addressed content
                       (manifests, configs, SBOMs), TTL entries for tag
                       lists (30m), tag→digest (15m), grype scans (24h)

Data flow for a diff: tag/digest → index digest (crane digest) → platform image digest (crane manifest) → SBOM (cosign download attestation, predicate https://spdx.dev/Document, with legacy .sbom-attachment fallback) and CVEs (grype registry:image@digest -o json) → pure diff logic (src/diff.ts).

Honest limitations

  • Free tier only sees rolling tags. latest, latest-dev, etc. Versioned tags are a paid feature; "older refs" here are historical digests recovered from public signature tags, which are unordered and must be dated individually.
  • CVE counts are scanner-relative. grype against its daily DB — not Chainguard's advisory feed, not an official count from either vendor. Numbers move as the DB updates; scans are cached up to 24h.
  • Upstream images have no SBOMs. diff_packages works where SBOM attestations exist (Chainguard images); for most upstream images it reports a data gap instead.
  • SBOM parity. Chainguard SBOMs are per-platform in-toto attestations; very old digests may predate them (legacy attachment fallback included, but some gaps remain and are reported as such).
  • Attestations are downloaded, not verified. cosign download attestation fetches; it does not verify signatures against Fulcio/Rekor. A production version would cosign verify-attestation against Chainguard's identity.
  • grype's own registry traffic is not rate-limited by this server's token bucket (it pulls layers internally). The bucket gates how often scans start; the 24h scan cache keeps repeats near zero.

Install & run

Prereqs: Node >= 20 and the three binaries:

brew install crane cosign grype

Build and test:

npm install
npm test          # 144 vitest tests — fully offline, binaries mocked
npm run build
npm run smoke     # the real pipeline against public registries (network!)

Claude Code (stdio — primary mode)

claude mcp add image-delta -- node /absolute/path/to/image-delta-mcp/dist/index.js

or in .mcp.json:

{
  "mcpServers": {
    "image-delta": {
      "command": "node",
      "args": ["/absolute/path/to/image-delta-mcp/dist/index.js"]
    }
  }
}

Optional HTTP mode (hardened, off by default)

IMAGE_DELTA_API_KEY="$(openssl rand -hex 24)" PORT=3900 node dist/index.js --http
# POST /mcp with Authorization: Bearer <key>; binds 127.0.0.1; /healthz unauthenticated

Refuses to start without a key (>= 16 chars). Timing-safe Bearer comparison, per-IP token bucket, 2 MB body cap, stateless transport.

Env: IMAGE_DELTA_CACHE_DIR overrides the cache location (default .cache/ in the package root).

Quality gate

  • 144 vitest tests (validation, exec safety, rate limiting, cache, SBOM and grype parsing, diff logic, all four tools against a mocked registry, MCP end-to-end over an in-memory transport, HTTP auth/limits integration). Tests never touch the network.
  • Integration smoke script (scripts/smoke.mjs) exercises the real pipeline; the demo numbers above are its output.
  • Adversarial security self-review + external Codex review — findings and fixes recorded in SECURITY-REVIEW.md.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选