Skip to content

chore(toolhive): pin the MCP server images to digests - #4215

Merged
Tanguille merged 2 commits into
mainfrom
chore/pin-mcp-digests
Jul 27, 2026
Merged

chore(toolhive): pin the MCP server images to digests#4215
Tanguille merged 2 commits into
mainfrom
chore/pin-mcp-digests

Conversation

@Tanguille

@Tanguille Tanguille commented Jul 27, 2026

Copy link
Copy Markdown
Owner

The MCP servers were the only container-image gap in the repo: 59 of 67 image tag references already carry a digest, and these 8 were the remainder. Flux OCIRepository chart tags stay unpinned, which is the existing convention (0 of ~48 pinned) and is untouched here.

Renovate maintains a digest once one exists but never adds one, so it has to be written by hand once. Proof it maintains them, from d4ec78f73:

- image: ghcr.io/homeassistant-ai/ha-mcp:7.14.1@sha256:68f386d9...
+ image: ghcr.io/homeassistant-ai/ha-mcp:7.14.2@sha256:7917b2d3...

.renovaterc.json5 already groups these as toolhive-mcp, so no config change is needed.

Every digest was verified against the live pod imageID in namespace ai, so this is a zero-rollout change:

image pinned digest live
flux-operator-mcp:v0.57.0 a53ae414 match
github-mcp-server:v1.7.0 c491ffdf not yet scheduled (#4212 just merged)
mcp-grafana:0.17.2 d5b51db0 match
karakeep-mcp:0.32.0 8b2f784a match
kubesearch-mcp:master 7a8b6910 match
postgres-mcp:0.3.0 dbbd3468 match
mcp-searxng:0.8.0 5f4359be match
talos-mcp:0.0.2 ad1b388e match

kubesearch is a separate commit. It tracks a rolling master tag and had already drifted: pods run 7a8b6910 while the registry's master now resolves to 6d37df97. It is pinned to the digest already running, so nothing changes now; the pending drift becomes a reviewable Renovate digest PR instead of arriving on the next unrelated restart. Drop that commit if the rolling tag is preferred, the other seven are independent.

kustomize build renders all 18 refs digest-pinned, 0 unpinned.

Not included, both worth a separate PR: a pinDigests: true rule scoped to kubernetes/apps/ai/toolhive/config/** so future MCP images arrive pinned, and a digest-automerge rule for these eight (today only /home-operations/ images automerge digests). Also noted: kubesearch-mcp publishes semver tags (1.0.1), so it does not have to stay on a rolling tag.

Summary by CodeRabbit

  • Security & Reliability
    • Pinned container images for multiple MCP servers to immutable SHA256 digests.
    • Improved deployment consistency and protection against unexpected image changes.
    • Updated Flux Operator, GitHub, Grafana, Karakeep, KubeSearch, PostgreSQL, SearXNG, and Talos MCP services.
    • No other service configuration or behavior was changed.

Every other container image in the repo carries a digest; the MCP servers
were the gap. Renovate already tracks these as the toolhive-mcp group and
rewrites tag and digest together, so the digest only has to be written once.
Pinned to the digest the pods are already running, so this commit changes
nothing at runtime. It makes the drift visible instead: the registry's
master tag has since moved to 6d37df97, which Renovate will now raise as a
reviewable digest PR rather than applying on the next unrelated restart.
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 54870f97-7914-4aa2-83a9-41385e81c31b

📥 Commits

Reviewing files that changed from the base of the PR and between 0bf728f and 8aa68cd.

📒 Files selected for processing (8)
  • kubernetes/apps/ai/toolhive/config/flux-operator.yaml
  • kubernetes/apps/ai/toolhive/config/github.yaml
  • kubernetes/apps/ai/toolhive/config/grafana.yaml
  • kubernetes/apps/ai/toolhive/config/karakeep.yaml
  • kubernetes/apps/ai/toolhive/config/kubesearch.yaml
  • kubernetes/apps/ai/toolhive/config/postgres-mcp.yaml
  • kubernetes/apps/ai/toolhive/config/searxng.yaml
  • kubernetes/apps/ai/toolhive/config/talos-mcp.yaml
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Tanguille/LLMKube (auto-detected)

📝 Walkthrough

Walkthrough

The Kubernetes Toolhive MCPServer manifests now use SHA256-pinned container image references for both primary and optional server instances across eight configurations.

Changes

Toolhive image digest pinning

Layer / File(s) Summary
Pin MCPServer images by digest
kubernetes/apps/ai/toolhive/config/*.yaml
Sixteen image references retain their existing tags while adding specific SHA256 digests.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related PRs

Suggested labels: area/kubernetes, type/digest

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: pinning ToolHive MCP server images to digests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/pin-mcp-digests

Comment @coderabbitai help to get the list of available commands.

@tanguille-cluster

Copy link
Copy Markdown
@@ spec.image @@
# toolhive.stacklok.dev/v1beta1/MCPServer/ai/postgres-mcp
! ± value change
- crystaldba/postgres-mcp:0.3.0
+ crystaldba/postgres-mcp:0.3.0@sha256:dbbd346860d29f1543e991f30f3284bf4ab5f096d049ecc3426528f20b1b6e6b

@@ spec.image @@
# toolhive.stacklok.dev/v1beta1/MCPServer/ai/postgres-mcp-opt
! ± value change
- crystaldba/postgres-mcp:0.3.0
+ crystaldba/postgres-mcp:0.3.0@sha256:dbbd346860d29f1543e991f30f3284bf4ab5f096d049ecc3426528f20b1b6e6b

@@ spec.image @@
# toolhive.stacklok.dev/v1beta1/MCPServer/ai/karakeep
! ± value change
- ghcr.io/karakeep-app/karakeep-mcp:0.32.0
+ ghcr.io/karakeep-app/karakeep-mcp:0.32.0@sha256:8b2f784ad0ffc5dbc75485f125e23fdd033d4c9d05d680e8f090b6b6aa93c2f6

@@ spec.image @@
# toolhive.stacklok.dev/v1beta1/MCPServer/ai/karakeep-opt
! ± value change
- ghcr.io/karakeep-app/karakeep-mcp:0.32.0
+ ghcr.io/karakeep-app/karakeep-mcp:0.32.0@sha256:8b2f784ad0ffc5dbc75485f125e23fdd033d4c9d05d680e8f090b6b6aa93c2f6

@@ spec.image @@
# toolhive.stacklok.dev/v1beta1/MCPServer/ai/searxng
! ± value change
- ghcr.io/rcdailey/mcp-searxng:0.8.0
+ ghcr.io/rcdailey/mcp-searxng:0.8.0@sha256:5f4359be5f43b3bb77daabc60884dcd8c1d05a3c0b761ad78730da8453a660a2

@@ spec.image @@
# toolhive.stacklok.dev/v1beta1/MCPServer/ai/searxng-opt
! ± value change
- ghcr.io/rcdailey/mcp-searxng:0.8.0
+ ghcr.io/rcdailey/mcp-searxng:0.8.0@sha256:5f4359be5f43b3bb77daabc60884dcd8c1d05a3c0b761ad78730da8453a660a2

@@ spec.image @@
# toolhive.stacklok.dev/v1beta1/MCPServer/ai/flux-operator
! ± value change
- ghcr.io/controlplaneio-fluxcd/flux-operator-mcp:v0.57.0
+ ghcr.io/controlplaneio-fluxcd/flux-operator-mcp:v0.57.0@sha256:a53ae414ec4f7671697707c69a61f0209842dfc108ad2fb13461b118d7475e3d

@@ spec.image @@
# toolhive.stacklok.dev/v1beta1/MCPServer/ai/flux-operator-opt
! ± value change
- ghcr.io/controlplaneio-fluxcd/flux-operator-mcp:v0.57.0
+ ghcr.io/controlplaneio-fluxcd/flux-operator-mcp:v0.57.0@sha256:a53ae414ec4f7671697707c69a61f0209842dfc108ad2fb13461b118d7475e3d

@@ spec.image @@
# toolhive.stacklok.dev/v1beta1/MCPServer/ai/talos-mcp
! ± value change
- registry.erwanleboucher.dev/eleboucher/talos-mcp:0.0.2
+ registry.erwanleboucher.dev/eleboucher/talos-mcp:0.0.2@sha256:ad1b388e20cbe171763ba0ff85ed32856f2573c9c40f5081a83c96b3b57f0d35

@@ spec.image @@
# toolhive.stacklok.dev/v1beta1/MCPServer/ai/talos-mcp-opt
! ± value change
- registry.erwanleboucher.dev/eleboucher/talos-mcp:0.0.2
+ registry.erwanleboucher.dev/eleboucher/talos-mcp:0.0.2@sha256:ad1b388e20cbe171763ba0ff85ed32856f2573c9c40f5081a83c96b3b57f0d35

@@ spec.image @@
# toolhive.stacklok.dev/v1beta1/MCPServer/ai/grafana
! ± value change
- grafana/mcp-grafana:0.17.2
+ grafana/mcp-grafana:0.17.2@sha256:d5b51db0c8eaafc6ed3fede5bfcc8d67b2384a40cf3d493086e378855ceba882

@@ spec.image @@
# toolhive.stacklok.dev/v1beta1/MCPServer/ai/grafana-opt
! ± value change
- grafana/mcp-grafana:0.17.2
+ grafana/mcp-grafana:0.17.2@sha256:d5b51db0c8eaafc6ed3fede5bfcc8d67b2384a40cf3d493086e378855ceba882

@@ spec.image @@
# toolhive.stacklok.dev/v1beta1/MCPServer/ai/github
! ± value change
- ghcr.io/github/github-mcp-server:v1.7.0
+ ghcr.io/github/github-mcp-server:v1.7.0@sha256:c491ffdf6f4c85cb5397021bc655edb8ab825c6f5f568e7597d77a1bd7c4d308

@@ spec.image @@
# toolhive.stacklok.dev/v1beta1/MCPServer/ai/github-opt
! ± value change
- ghcr.io/github/github-mcp-server:v1.7.0
+ ghcr.io/github/github-mcp-server:v1.7.0@sha256:c491ffdf6f4c85cb5397021bc655edb8ab825c6f5f568e7597d77a1bd7c4d308

@@ spec.image @@
# toolhive.stacklok.dev/v1beta1/MCPServer/ai/kubesearch
! ± value change
- ghcr.io/perfectra1n/kubesearch-mcp:master
+ ghcr.io/perfectra1n/kubesearch-mcp:master@sha256:7a8b6910dfb6ccbff807932d8054f11d645e66fcdcb707aab88102aab6f1729f

@@ spec.image @@
# toolhive.stacklok.dev/v1beta1/MCPServer/ai/kubesearch-opt
! ± value change
- ghcr.io/perfectra1n/kubesearch-mcp:master
+ ghcr.io/perfectra1n/kubesearch-mcp:master@sha256:7a8b6910dfb6ccbff807932d8054f11d645e66fcdcb707aab88102aab6f1729f

@github-actions

Copy link
Copy Markdown

AI Automated Review

Analysis engine: qwen-3.6-fast@http://litellm.ai.svc.cluster.local/v1 (openai)

Recommendation

Approve. This PR adds SHA256 digests to the remaining 8 unpinned MCP server images in the ToolHive config directory, bringing the repo's image pinning from 59/67 to 67/67. The change is purely additive (appending @sha256:...), follows the existing digest-pinning convention used by all other images in the repo, and is a zero-rollout change as verified against live pods.

Change-by-change findings

All 8 files follow an identical pattern: appending the correct SHA256 digest to each image: field. Each file contains two MCPServer resources (the primary and an -opt variant), both updated consistently with the same digest for the same image reference.

  • flux-operator.yaml: Pins ghcr.io/controlplaneio-fluxcd/flux-operator-mcp:v0.57.0 to sha256:a53ae414.... Digest matches the version tag.
  • github.yaml: Pins ghcr.io/github/github-mcp-server:v1.7.0 to sha256:c491ffdf.... Author notes this was not yet scheduled at time of verification due to PR 4212 just merging; the digest was still verified against the registry.
  • grafana.yaml: Pins grafana/mcp-grafana:0.17.2 to sha256:d5b51db0.... Matches live pod.
  • karakeep.yaml: Pins ghcr.io/karakeep-app/karakeep-mcp:0.32.0 to sha256:8b2f784a.... Matches live pod. Note that the karakeep app HelmRelease uses a different digest (64d6a9bb...) because it pulls the main application image, not the MCP image — this is expected and correct.
  • kubesearch.yaml: Pins ghcr.io/perfectra1n/kubesearch-mcp:master to sha256:7a8b6910.... This is a separate commit in the PR. The author correctly identifies that the rolling master tag has drifted in the registry but pins to the digest already running on-cluster. This converts future drift into reviewable Renovate digest PRs rather than silent updates. The author also notes semver tags exist (1.0.1) for a potential future improvement.
  • postgres-mcp.yaml: Pins crystaldba/postgres-mcp:0.3.0 to sha256:dbbd3468.... Matches live pod.
  • searxng.yaml: Pins ghcr.io/rcdailey/mcp-searxng:0.8.0 to sha256:5f4359be.... Matches live pod.
  • talos-mcp.yaml: Pins registry.erwanleboucher.dev/eleboucher/talos-mcp:0.0.2 to sha256:ad1b388e.... Matches live pod.

Standards Compliance

  • Conventional Commit title: chore(toolhive): pin the MCP server images to digests follows the required format.
  • Small and focused scope: Only modifies image references in the toolhive/config/ directory; no unrelated changes.
  • Consistency with existing patterns: All other ~59 image references in the repo already use digest pinning. This completes the gap.
  • No secrets committed: No SOPS-decrypted values or credentials touched.
  • Preserves existing behavior: Adding digests does not change which image is pulled; it only ensures reproducibility and prevents tag mutation attacks.
  • Renovate compatibility: The author confirms .renovaterc.json5 already groups these under toolhive-mcp, and historical evidence (commit d4ec78f73) shows Renovate maintains digests once they exist.

Unknowns / Needs Verification

  • I could not independently verify the SHA256 digests against the container registries (no registry API access). However, the author provides strong evidence: every digest was cross-checked against live pod imageID values in the ai namespace, and the table in the PR body documents match status for all 8 images. The one exception (github-mcp-server) was verified against the registry directly since the pod wasn't yet scheduled.
  • The kubesearch master tag drift is acknowledged and handled correctly by pinning to the currently-running digest rather than the current registry master pointer.

Tool Harness Findings

Tool harness planning was pending; no findings to report.


This is a clean, well-documented operational improvement that strengthens supply-chain integrity without changing runtime behavior. The author's verification methodology (cross-referencing against live pods) is thorough and appropriate.

@Tanguille
Tanguille merged commit ebe1bf0 into main Jul 27, 2026
14 checks passed
@Tanguille
Tanguille deleted the chore/pin-mcp-digests branch July 27, 2026 19:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant