image-delta-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@image-delta-mcpcompare cgr.dev/chainguard/nginx to docker.io/library/nginx:latest"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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.devfree-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_gapsfields — never papered over as empty-but-successful results.
Related MCP server: MCP Image Compare Server
The four tools
Tool | Question it answers |
| What tags exist, with digests and created dates — plus how many historical digests are publicly recoverable from signature tags. |
| Package-level delta (added / removed / version-changed) between two refs, from the SPDX SBOM attestations attached to each digest. |
| CVEs shed and introduced between two refs, from identical registry-direct grype scans. |
| 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-changedThe 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)
"What CVEs did we shed moving chainguard/node from
sha256:0029ab60fc5a…to latest?" → clean shed/introduced delta with the package changes behind it."Compare chainguard/nginx to docker.io/library/nginx." → the zero-CVE claim reproduced live from public data by a tool an agent can call.
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_packagesworks 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 attestationfetches; it does not verify signatures against Fulcio/Rekor. A production version wouldcosign verify-attestationagainst 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 grypeBuild 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.jsor 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 unauthenticatedRefuses 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.
This server cannot be installed
Maintenance
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/joepangallo/image-delta-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server