-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathDockerfile
More file actions
169 lines (140 loc) · 7.64 KB
/
Dockerfile
File metadata and controls
169 lines (140 loc) · 7.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# Stage 1: Build and bundle the TypeScript application
ARG DISABLE_LANGUAGES=rust
FROM node:20-slim@sha256:f93745c153377ee2fbbdd6e24efcd03cd2e86d6ab1d8aa9916a3790c40313a55 AS builder
ARG DISABLE_LANGUAGES
ENV DEBUG_MCP_DISABLE_LANGUAGES=${DISABLE_LANGUAGES}
# Install pnpm (using version 10 to match local development)
RUN npm install -g pnpm@10.33.0
# Set application directory
WORKDIR /app
# Add container marker
ENV MCP_CONTAINER=true
# Cache busting argument - changes this will invalidate all subsequent layers
ARG CACHEBUST=1
# 1) Copy ONLY manifests for dependency install (preserves cache)
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY packages/shared/package.json ./packages/shared/package.json
COPY packages/adapter-mock/package.json ./packages/adapter-mock/package.json
COPY packages/adapter-python/package.json ./packages/adapter-python/package.json
COPY packages/adapter-javascript/package.json ./packages/adapter-javascript/package.json
COPY packages/adapter-rust/package.json ./packages/adapter-rust/package.json
COPY packages/adapter-go/package.json ./packages/adapter-go/package.json
COPY packages/adapter-java/package.json ./packages/adapter-java/package.json
# 2) Install dependencies with workspace support using the lockfile
# If lockfile is stale, this will fail (good signal to refresh it locally).
# Copy all package sources to allow pnpm to resolve workspace:* links
COPY packages ./packages
# Remove any existing dist folders and tsbuildinfo artifacts from packages to prevent stale
# build outputs (and their cached path maps) from polluting the Docker build.
RUN set -eux; \
for pkg in ./packages/*; do \
[ -d "$pkg" ] || continue; \
rm -rf "$pkg/dist" "$pkg/tsconfig.tsbuildinfo"; \
done
RUN pnpm --version && pnpm install --frozen-lockfile --ignore-scripts
# 3) Copy the rest of the sources and build configs
COPY tsconfig*.json ./
COPY packages/shared/tsconfig*.json ./packages/shared/
COPY packages/adapter-mock/tsconfig*.json ./packages/adapter-mock/
COPY packages/adapter-python/tsconfig*.json ./packages/adapter-python/
COPY packages/adapter-javascript/tsconfig*.json ./packages/adapter-javascript/
COPY packages/adapter-rust/tsconfig*.json ./packages/adapter-rust/
COPY packages/adapter-go/tsconfig*.json ./packages/adapter-go/
COPY packages/adapter-java/tsconfig*.json ./packages/adapter-java/
COPY src ./src
COPY scripts ./scripts/
# 4) Build workspace packages and main project (root build runs build:packages); then bundle
# Download Linux CodeLLDB artifacts during the container build if they are not already vendored.
RUN CODELLDB_VENDOR_ALL=false CODELLDB_PLATFORMS=linux-x64 pnpm run build --silent
RUN node scripts/bundle.js
# Optional: quick diagnostics for bundle
RUN echo "=== Listing dist directory after bundling ===" && \
ls -la dist/ && \
echo "=== Checking for bundle.cjs ===" && \
ls -la dist/bundle.cjs || true && \
echo "=== Bundle size ===" && \
(command -v du >/dev/null 2>&1 && du -h dist/bundle.cjs) || true
# 5) Ensure adapter packages are available in node_modules
# pnpm uses symlinks that don't survive Docker COPY, so we need to replace them with actual files
RUN rm -rf /app/node_modules/@debugmcp && \
mkdir -p /app/node_modules/@debugmcp/shared && \
mkdir -p /app/node_modules/@debugmcp/adapter-mock && \
mkdir -p /app/node_modules/@debugmcp/adapter-python && \
mkdir -p /app/node_modules/@debugmcp/adapter-javascript && \
cp -r /app/packages/shared/dist /app/node_modules/@debugmcp/shared/ && \
cp /app/packages/shared/package.json /app/node_modules/@debugmcp/shared/ && \
cp -r /app/packages/adapter-mock/dist /app/node_modules/@debugmcp/adapter-mock/ && \
cp /app/packages/adapter-mock/package.json /app/node_modules/@debugmcp/adapter-mock/ && \
cp -r /app/packages/adapter-python/dist /app/node_modules/@debugmcp/adapter-python/ && \
cp /app/packages/adapter-python/package.json /app/node_modules/@debugmcp/adapter-python/ && \
cp -r /app/packages/adapter-javascript/dist /app/node_modules/@debugmcp/adapter-javascript/ && \
cp -r /app/packages/adapter-javascript/vendor /app/node_modules/@debugmcp/adapter-javascript/ && \
cp /app/packages/adapter-javascript/package.json /app/node_modules/@debugmcp/adapter-javascript/ && \
mkdir -p /app/node_modules/@debugmcp/adapter-java && \
cp -r /app/packages/adapter-java/dist /app/node_modules/@debugmcp/adapter-java/ && \
cp -r /app/packages/adapter-java/java /app/node_modules/@debugmcp/adapter-java/ && \
cp /app/packages/adapter-java/package.json /app/node_modules/@debugmcp/adapter-java/
# Stage 2: Create runtime image with full LLDB dependencies
FROM ubuntu:24.04@sha256:84e77dee7d1bc93fb029a45e3c6cb9d8aa4831ccfcc7103d36e876938d28895b
# Disable Go at runtime too — Delve isn't installed in the container
ENV DEBUG_MCP_DISABLE_LANGUAGES=rust,go,dotnet
# Set application directory
WORKDIR /app
# Set container marker for runtime
ENV MCP_CONTAINER=true
# Set default workspace mount location (can be overridden at runtime)
ENV MCP_WORKSPACE_ROOT=/workspace
# Install Python, LLDB, and supporting tools (Node copied from builder)
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
curl \
ca-certificates \
strace \
procps \
lsof \
tini \
python3 \
python3-pip \
python3-venv \
libstdc++6 \
lldb \
python3-lldb \
openjdk-21-jdk-headless && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
pip3 install --break-system-packages --no-cache-dir "debugpy==1.8.14"
# Copy Node runtime from builder to avoid installing system-wide Node.js
COPY --from=builder /usr/local/bin/node /usr/local/bin/node
COPY --from=builder /usr/local/lib/node_modules /usr/local/lib/node_modules
RUN ln -sf /usr/local/bin/node /usr/bin/node
# Copy ONLY the bundled server and proxy files (everything else is bundled)
COPY --from=builder /app/dist/bundle.cjs /app/dist/bundle.cjs
COPY --from=builder /app/dist/proxy/proxy-bootstrap.js /app/dist/proxy/proxy-bootstrap.js
COPY --from=builder /app/dist/proxy/proxy-bundle.cjs /app/dist/proxy/proxy-bundle.cjs
COPY --from=builder /app/dist/proxy/utils /app/dist/proxy/utils
# Copy ONLY the runtime adapter packages (not entire node_modules)
# These are loaded dynamically at runtime via import()
COPY --from=builder /app/node_modules/@debugmcp /app/node_modules/@debugmcp
# Pre-compile JDI bridge for instant Java debugging (no on-demand compilation at runtime)
RUN mkdir -p /app/node_modules/@debugmcp/adapter-java/java/out && \
javac --release 21 \
/app/node_modules/@debugmcp/adapter-java/java/JdiDapServer.java \
-d /app/node_modules/@debugmcp/adapter-java/java/out
# Copy ONLY the production runtime dependencies needed by adapters
# Use a minimal set - the bundle already includes most dependencies
COPY --from=builder /app/node_modules/@vscode /app/node_modules/@vscode
COPY --from=builder /app/node_modules/which /app/node_modules/which
COPY --from=builder /app/node_modules/.pnpm/isexe@4.0.0/node_modules/isexe /app/node_modules/isexe
# Expose ports
EXPOSE 3000 5679
# Copy stdio silencer preloader into runtime image
COPY --from=builder /app/scripts/stdio-silencer.cjs /app/scripts/stdio-silencer.cjs
# Create logs directory with proper permissions for any user
RUN mkdir -p /app/logs && chmod 777 /app/logs
# Copy entrypoint wrapper (version-controlled script avoids shell quoting pitfalls)
COPY scripts/docker-entry.sh /app/entry.sh
RUN sed -i 's/\r$//' /app/entry.sh && chmod +x /app/entry.sh
# Use tini as PID1 to properly handle signals, then run our wrapper
ENTRYPOINT ["/usr/bin/tini", "--", "/app/entry.sh"]
# Default command arguments
CMD ["stdio"]