# syntax=docker/dockerfile:1 # ---- Build stage ------------------------------------------------------------- # Python 3.14 / Alpine 3.24 base. uv is copied from its official image because # there is no combined python3.14-alpine3.24 uv image tag. FROM python:3.14-alpine3.24 AS builder COPY --from=ghcr.io/astral-sh/uv:0.13.1 /uv /uvx /bin/ # setuptools-scm derives the package version from git history and tags. RUN apk add --no-cache git ENV UV_COMPILE_BYTECODE=1 \ UV_LINK_MODE=copy \ UV_PYTHON_DOWNLOADS=never WORKDIR /app # Dependencies first: this layer only rebuilds when pyproject.toml/uv.lock change. COPY pyproject.toml uv.lock .python-version ./ RUN --mount=type=cache,target=/root/.cache/uv \ uv sync --frozen --no-dev --no-install-project # Then install the project itself. The full checkout (including .git) is # copied so setuptools-scm sees a clean worktree and derives the version # from the closest release tag. COPY . . RUN --mount=type=cache,target=/root/.cache/uv \ uv sync --frozen --no-dev --no-editable # ---- Runtime stage ----------------------------------------------------------- FROM python:3.14-alpine3.24 AS runtime ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PATH="/app/.venv/bin:$PATH" RUN addgroup -S -g 1001 xmcp \ && adduser -S -D -H -u 1001 -G xmcp xmcp \ && mkdir -p /etc/xmcp WORKDIR /app COPY --from=builder --chown=xmcp:xmcp /app/.venv /app/.venv USER xmcp EXPOSE 8000 # Probes the MCP HTTP port when the mounted configuration uses transport http. HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD ["python", "-m", "xmcp.healthcheck"] CMD ["python", "-m", "xmcp"]