Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ jobs:
- name: Install dependencies
run: yarn

- name: Type-check
run: npx tsc --noEmit

- name: Unit tests
run: yarn test

- name: Build the project
run: yarn build

Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,9 @@ jobs:
HARNESS_SKIP: cloudpods,ephemeral,replicator,chaos
run: |
npm ci --prefix data/sample-cdk
mkdir -p "$HOME/.localstack-mcp"
node tests/docker/validate-image.mjs -- \
docker run -i --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$HOME/.localstack-mcp:$HOME/.localstack-mcp" \
-e XDG_CACHE_HOME="$HOME/.localstack-mcp" \
--add-host host.docker.internal:host-gateway \
--add-host s3.host.docker.internal:host-gateway \
--add-host snowflake.localhost.localstack.cloud:host-gateway \
Expand Down
43 changes: 21 additions & 22 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,40 @@ RUN yarn install --frozen-lockfile
COPY . .
RUN yarn build

FROM python:3.12-slim-bookworm AS runtime
# The server drives LocalStack through the Docker Engine API (dockerode) and REST —
# no localstack CLI, docker CLI, or awscli needed in the image. Python remains only
# for the IaC/Snowflake CLIs (tflocal, samlocal, snow) used by the deployer and
# snowflake-client tools.
FROM node:22-bookworm-slim AS runtime
ENV DEBIAN_FRONTEND=noninteractive \
PYTHONDONTWRITEBYTECODE=1
PYTHONDONTWRITEBYTECODE=1 \
PATH="/opt/venv/bin:${PATH}"

RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends ca-certificates curl gnupg unzip git; \
apt-get install -y --no-install-recommends ca-certificates curl gnupg unzip git \
python3 python3-pip python3-venv; \
install -m 0755 -d /usr/share/keyrings; \
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -; \
curl -fsSL https://apt.releases.hashicorp.com/gpg \
| gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg; \
chmod a+r /usr/share/keyrings/hashicorp-archive-keyring.gpg; \
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com bookworm main" \
> /etc/apt/sources.list.d/hashicorp.list; \
curl -fsSL https://download.docker.com/linux/debian/gpg \
| gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg; \
chmod a+r /usr/share/keyrings/docker-archive-keyring.gpg; \
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian bookworm stable" \
> /etc/apt/sources.list.d/docker.list; \
apt-get update; \
apt-get install -y --no-install-recommends nodejs terraform docker-ce-cli; \
apt-get install -y --no-install-recommends terraform; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*

RUN pip install --no-cache-dir --upgrade pip \
# Debian's python carries the PEP 668 externally-managed marker — install the IaC
# CLIs into a dedicated venv (its bin dir is on PATH for runCommand spawns).
RUN python3 -m venv /opt/venv \
&& pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir --no-compile \
localstack \
awscli \
awscli-local \
terraform-local \
aws-sam-cli \
aws-sam-cli-local \
snowflake-cli \
&& find /usr/local/lib/python3.12/site-packages \
&& find /opt/venv/lib \
\( -type d \( -name __pycache__ -o -name tests -o -name test \) -o -type f \( -name '*.pyc' -o -name '*.pyo' \) \) \
-prune -exec rm -rf '{}' +

Expand All @@ -50,7 +50,10 @@ RUN npm install -g aws-cdk@2.1114.0 aws-cdk-local \

RUN node <<'NODE'
const fs = require("fs");
const file = "/usr/lib/node_modules/aws-cdk/lib/index.js";
const path = require("path");
const { execSync } = require("child_process");
const globalRoot = execSync("npm root -g").toString().trim();
const file = path.join(globalRoot, "aws-cdk/lib/index.js");
const source = fs.readFileSync(file, "utf8");
const target = ` s3() {\n const client = new import_client_s33.S3Client(this.config);`;
const replacement = ` s3() {\n if (/^(1|true|yes)$/i.test(process.env.AWS_S3_FORCE_PATH_STYLE || "")) {\n this.config.forcePathStyle = true;\n }\n const client = new import_client_s33.S3Client(this.config);`;
Expand All @@ -64,7 +67,7 @@ if (!source.includes(replacement)) {
NODE

WORKDIR /app
RUN mkdir -p /usr/lib/localstack /tmp/dockerode-deps \
RUN mkdir -p /tmp/dockerode-deps \
&& npm install --prefix /tmp/dockerode-deps --omit=dev --ignore-scripts --no-audit --no-fund dockerode@4.0.7 \
&& mkdir -p /app/node_modules \
&& cp -R /tmp/dockerode-deps/node_modules/. /app/node_modules/ \
Expand All @@ -74,20 +77,16 @@ COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./package.json

RUN set -eux; \
localstack --version; \
aws --version; \
awslocal --version; \
terraform version; \
tflocal --version; \
sam --version; \
command -v samlocal; \
cdklocal --version; \
snow --version; \
docker --version; \
node -e "require('dockerode'); console.log('dockerode ok')"

LABEL org.opencontainers.image.title="LocalStack MCP Server" \
org.opencontainers.image.description="Self-contained MCP server for managing LocalStack (CLI, CDK, Terraform, SAM, awslocal baked in)" \
org.opencontainers.image.description="Self-contained MCP server for managing LocalStack via the Docker API (CDK, Terraform, SAM, Snowflake CLIs baked in)" \
org.opencontainers.image.source="https://github.com/localstack/localstack-mcp-server" \
org.opencontainers.image.licenses="Apache-2.0"

Expand Down
Loading
Loading