Files
mcp/Dockerfile
zeekayandHanzo Dev b33bcad727 build: move mcp to pnpm and delete the second lockfile
This repo committed BOTH package-lock.json and pnpm-lock.yaml — two descriptions of
one dependency tree, neither reconciled against the other. CI then ran
`npm ci || npm install`, which is the shape that lets that persist: when the lockfile
stops matching package.json, the fallback silently resolves a different tree and the
run still goes green. A lockfile nobody fails on is not a lockfile.

Now there is one. pnpm-lock.yaml is tracked, package-lock.json is deleted and
ignored along with every other manager's, corepack takes the pnpm version from
`packageManager`, and Docker and CI both install with --frozen-lockfile so a drifted
tree stops the build instead of being papered over.

The `pnpm.onlyBuiltDependencies` field in package.json was dead — pnpm 11 reads
settings from pnpm-workspace.yaml and prints a warning that it ignored it. Its five
entries moved there verbatim, documented, so the build-script denial is actually in
effect rather than merely written down.

esbuild goes ^0.27.4 -> ^0.28.1, the version console already builds with. 0.27.4's
install script overwrites its own bin/esbuild with a raw ELF, and pnpm's shim reads
a missing shebang as "run this with node", so `pnpm build` died on
`SyntaxError: Invalid or unexpected token` at the first byte. Same minor series, and
it is what makes the build work under pnpm at all.

Verified: frozen-lockfile install clean, `pnpm build` emits a 617KB bundle, 354
tests passing.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 08:24:23 -07:00

94 lines
2.2 KiB
Docker

# Production-ready Dockerfile for Hanzo MCP Server
# Multi-stage build optimized for TypeScript CLI applications
# ===== Base Stage =====
FROM ghcr.io/hanzoai/nodejs:v24.18.0 AS base
# Install system dependencies and security updates
RUN apk update && apk upgrade && \
apk add --no-cache \
dumb-init \
tini \
ca-certificates && \
rm -rf /var/cache/apk/*
# Create app directory with proper permissions
WORKDIR /app
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 mcp && \
chown -R mcp:nodejs /app
# ===== Dependencies Stage =====
FROM base AS deps
# Copy package files
COPY --chown=mcp:nodejs package.json package-lock.json* ./
# Switch to mcp user for security
USER mcp
# Install dependencies
RUN corepack enable && pnpm install --prod --frozen-lockfile && pnpm store prune
# Install dev dependencies for building
RUN corepack enable && pnpm install --frozen-lockfile
# ===== Builder Stage =====
FROM deps AS builder
# Copy source code
COPY --chown=mcp:nodejs . .
# Build the application
RUN pnpm build
# ===== Development Stage =====
FROM deps AS development
# Copy source code
COPY --chown=mcp:nodejs . .
# Set environment
ENV NODE_ENV=development
# Development command with hot reload
CMD ["pnpm", "dev"]
# ===== Production Stage =====
FROM base AS production
# Install only runtime dependencies
COPY --chown=mcp:nodejs package.json package-lock.json* ./
# Switch to mcp user for security
USER mcp
# Install only production dependencies
RUN corepack enable && pnpm install --prod --frozen-lockfile && pnpm store prune
# Copy built application from builder
COPY --from=builder --chown=mcp:nodejs /app/dist ./dist
# Make CLI executable
RUN chmod +x ./dist/cli.js
# Set environment variables
ENV NODE_ENV=production
# Create symlink for global access (optional)
USER root
RUN ln -sf /app/dist/cli.js /usr/local/bin/hanzo-mcp
USER mcp
# Health check for MCP server
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "process.exit(0);" || exit 1
# Use tini for proper signal handling
ENTRYPOINT ["/sbin/tini", "--"]
# Default command runs the MCP server
CMD ["node", "dist/index.js"]
# ===== Default to production =====
FROM production