feat: add Docker deployment for MCP server - #52
Conversation
Multi-stage Dockerfile (pnpm/turbo build -> node:22-alpine runtime) plus an entrypoint that lazily initializes docsets from config.yaml and starts the stdio MCP server. Docsets are mounted via volumes; symlinks are created inside the Linux container, avoiding the Windows symlink-permission problem on the host. Adds Dockerfile, docker-entrypoint.sh, .dockerignore and docs/docker.md; ignores .idea/ and npm lockfiles.
corepack enable creates symlinks in the root-owned /usr/local/bin. Under rootless Podman the build container defaults to a non-root uid, so this failed with EACCES. Docker builds as root by default and was unaffected. Add explicit USER root to both build and runtime stages before the corepack/pnpm install steps; the runtime stage still drops to USER node before the ENTRYPOINT. Mirrors the pattern already used in bruno-cli-mcp.
ReviewGreat PR — the multi-stage build, rootless Podman fix, and volume-mount approach are all well thought out. Here are some findings: High1. Fragile YAML parsing in entrypoint grep -E '^\s+- id:' "$CONFIG" | sed 's/.*id: *//' | tr -d '\r' | while read -r id; doThis grep/sed approach is fragile:
Suggestion: Use a proper YAML parser. Since Node.js is available, call it from the entrypoint or add a small CLI subcommand like Medium2. RUN pnpm install --frozen-lockfile --prod --ignore-scriptsThe 3. Confusing stdout/stderr redirection node /app/packages/cli/dist/index.js init "$id" 1>&2 2>&1 || trueThe node /app/packages/cli/dist/index.js init "$id" &>/dev/stderr || true4. Silent init failures The FAILED=0
while read -r id; do
if ! node /app/packages/cli/dist/index.js init "$id" &>/dev/stderr; then
echo "FAILED to init docset: $id" >&2
FAILED=1
fi
done
if [ "$FAILED" -eq 1 ]; then
echo "WARNING: Some docsets failed to initialize" >&2
fi5. Redundancy: config.yaml vs. Docker volume mounts Every Follow-up suggestion: wrapper script A wrapper script (e.g., ./run.sh # runs with all docsets
./run.sh my-docs # runs only a specific docsetThe script could:
This would be a nice follow-up to this PR. Low6. Verbose COPY statements — Each 7. Missing HEALTHCHECK — For production deployment, a simple healthcheck would be valuable. 8. |
The entrypoint grep/sed approach broke on tabs, inline comments, and other valid YAML formatting. Moving docset discovery to Node.js via a dedicated `init-all` CLI command uses js-yaml (already a dependency) for correct config parsing. Failures now cause a hard abort (fast-fail) instead of a silent || true — MCP server logs are rarely read so fail- fast makes broken starts visible. Also fixes deprecated --prod flag (pnpm v9+) and .gitignore missing trailing newline. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ADR-002: Windows runtime is container-only (Docker/Podman), no native execution - ADR-003: container image serves only pre-materialized local_folder docsets; git_repo/archive are rejected by the entrypoint and materialized on the host (loaders stay available in the npm package/CLI) - docker-deployment-design.md: design rationale, scope narrowed per ADR-003 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PR: feat(docker): ship MCP server as a Docker/Podman image (+ ADR-002/ADR-003)SummaryShips What's included
|
The docker-deployment-design.md draft still carried pre-ADR-003 git/network assumptions and duplicated operative content from docker.md. Both surfaced as drift risk: a stale "needs egress to clone git repos" network section and an acceptance criterion testing in-image git cloning, neither of which the local_folder-only image does. - Section 5 (Netzwerk): image needs no network at startup; git-clone egress is marked as host/CLI behaviour, not the image. - Acceptance criterion 4: replaced the in-image git-clone scenario with a local_folder/init-all criterion; git/archive materialization is host-side. - Removed the mcp.json block and the Dockerfile stage diagram (both duplicate docs/docker.md), replacing them with pointers; the design doc keeps only the rationale (the "why"). docker.md remains the operative reference; the design doc holds the rationale. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
as discussed: copy (instead of linking) is not a viable option. closing this for now until we find a real DRY approach |
Multi-stage Dockerfile (pnpm/turbo build -> node:22-alpine runtime) plus an entrypoint that lazily initializes docsets from config.yaml and starts the stdio MCP server. Docsets are mounted via volumes; symlinks are created inside the Linux container, avoiding the Windows symlink-permission problem on the host.
Adds Dockerfile, docker-entrypoint.sh, .dockerignore and docs/docker.md; ignores .idea/ and npm lockfiles.