FROM ghcr.io/github/github-mcp-server@sha256:1817b57d43916532dc002bdc5f344d639bd9fb54a9148d42168458f7c3280567 AS github FROM ghcr.io/blakeem/navidrome-mcp:2.2.0@sha256:047f911a5a8f7cc8f185bb4d6e7ca6c435542edefff4694a00c2f718ab0ee7f5 AS navidrome FROM samanhappy/mcphub:1.0.32@sha256:df34df85e639743d0bf4b64182fc2f47586bf544beb08c85a5e5d693891daad3 ARG MCP_VERSION=1.29.0 ARG ARR_MCP_VERSION=1.0.1 ARG YT_DLP_VERSION=2026.7.4 ARG FRITZ_MCP_VERSION=0.8.0 ARG FRITZ_MCP_SHA256=47f4e2b5595a522aeda4aed9f5c65a57e500c3589c9f4262b8fbf72bd8c72bd4 USER root # One image, multiple isolated MCP subprocesses. MCPHub already contains # Python, Node.js, uv/uvx and build tools; only the pinned runtime packages # used by our local servers are added here. RUN python3 -m pip install --no-cache-dir \ "mcp==${MCP_VERSION}" \ "arr-mcp[mcp]==${ARR_MCP_VERSION}" \ "yt-dlp==${YT_DLP_VERSION}" # Released, checksum-pinned Go binary. Keeping the download in the reproducible # image build prevents MCPHub upgrades from silently dropping the Fritz tools. RUN mkdir -p /opt/casaderoll \ && python3 -c 'import sys,urllib.request; v=sys.argv[1]; urllib.request.urlretrieve(f"https://github.com/kambriso/fritzbox-mcp-server/releases/download/v{v}/fritz-mcp-linux-amd64", "/opt/casaderoll/fritz-mcp")' "${FRITZ_MCP_VERSION}" \ && echo "${FRITZ_MCP_SHA256} /opt/casaderoll/fritz-mcp" | sha256sum -c - \ && chmod 0755 /opt/casaderoll/fritz-mcp COPY --from=github /server/github-mcp-server /usr/local/bin/github-mcp-server COPY --from=navidrome /app /opt/casaderoll/navidrome COPY platform/mcp/deemix_mcp.py /opt/casaderoll/mcps/deemix_mcp.py COPY platform/mcphub/mcphub_admin_mcp.py /opt/casaderoll/mcps/mcphub_admin_mcp.py COPY platform/mcp/patches/mcp_sonarr.py /usr/local/lib/python3.13/site-packages/arr_mcp/mcp/mcp_sonarr.py COPY platform/mcp/patches/mcp_radarr.py /usr/local/lib/python3.13/site-packages/arr_mcp/mcp/mcp_radarr.py COPY config/mcp-registry.json /opt/casaderoll/config/mcp-registry.json COPY platform/mcphub/configure-settings.py /opt/casaderoll/configure-settings.py COPY platform/mcphub/deploy-extension.py /opt/casaderoll/deploy-extension.py COPY platform/mcphub/run-with-env.py /usr/local/bin/run-with-env COPY platform/mcphub/casaderoll-entrypoint.sh /usr/local/bin/casaderoll-mcphub-entrypoint # Keep the llama.cpp-compatible schema correction from the former dedicated # Navidrome image. Abort the image build if upstream changes unexpectedly. RUN node -e 'const fs=require("node:fs"); const p="/opt/casaderoll/navidrome/dist/tools/handlers/radio-handlers.js"; let s=fs.readFileSync(p,"utf8"); const a="pattern: '\''^https?://.+'\''"; const b="pattern: '\''^https?://.+$'\''"; const n=s.split(a).length-1; if(n!==2) throw new Error(`expected 2 schema patterns, found ${n}`); fs.writeFileSync(p,s.split(a).join(b));' \ && chmod 0755 /usr/local/bin/run-with-env /usr/local/bin/casaderoll-mcphub-entrypoint /usr/local/bin/github-mcp-server \ && mkdir -p /app/data /run/secrets/mcphub ENV MCPHUB_SETTING_PATH=/app/data/ \ REQUEST_TIMEOUT=120000 \ NODE_ENV=production \ FRITZ_MCP_VERSION=${FRITZ_MCP_VERSION} ENTRYPOINT ["/usr/local/bin/casaderoll-mcphub-entrypoint"] CMD ["/usr/local/bin/entrypoint.sh", "pnpm", "start"]