From 61fc5d252b84475e4507f8fd9840c8112f6fe8d5 Mon Sep 17 00:00:00 2001 From: Jamie Treworgy Date: Fri, 5 Sep 2025 16:51:59 -0400 Subject: [PATCH 1/4] Make working Dockerfile --- .gitignore | 4 ++ Dockerfile | 74 ++++++++++++++++++++++------- README.md | 40 +++++++++++++++- scripts/build-docker.sh | 14 ++++++ scripts/docker-compose.example.yaml | 13 +++++ 5 files changed, 127 insertions(+), 18 deletions(-) create mode 100755 scripts/build-docker.sh create mode 100644 scripts/docker-compose.example.yaml diff --git a/.gitignore b/.gitignore index 5c8b5d4..78760f3 100644 --- a/.gitignore +++ b/.gitignore @@ -163,3 +163,7 @@ coverage/ repomix-output* mcp-servers.json mcp-config.json + +# AI Agents + +.claude/ diff --git a/Dockerfile b/Dockerfile index 4c9716b..4c302bc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,16 +2,31 @@ # Use a specific Node.js version known to work, Alpine for smaller size FROM node:23-alpine AS base WORKDIR /usr/src/app -ENV NODE_ENV=production +#ENV NODE_ENV=production -# ---- Dependencies ---- -# Install dependencies first to leverage Docker cache -FROM base AS deps -WORKDIR /usr/src/app -COPY package.json package-lock.json* ./ -# Use npm ci for deterministic installs based on lock file -# Install only production dependencies in this stage for the final image -RUN npm ci --only=production +ENV MCP_TRANSPORT_TYPE=http +ARG USER_ID +ARG GROUP_ID +ARG MCP_HTTP_HOST=0.0.0.0 +ENV MCP_HTTP_HOST=${MCP_HTTP_HOST} +ARG MCP_HTTP_PORT=3015 +ENV MCP_HTTP_PORT=${MCP_HTTP_PORT} +ARG MCP_LOG_LEVEL=info +ENV MCP_LOG_LEVEL=${MCP_LOG_LEVEL} +ARG GIT_USER_EMAIL +ARG GIT_USER_NAME + +# The user/group ID must match your local user/group ID +RUN test -n "$USER_ID" || (echo "ERROR: USER_ID build argument is required" && exit 1) +RUN test -n "$GROUP_ID" || (echo "ERROR: GROUP_ID build argument is required" && exit 1) +RUN test -n "$GIT_USER_EMAIL" || (echo "ERROR: GIT_USER_EMAIL build argument is required" && exit 1) +RUN test -n "$GIT_USER_NAME" || (echo "ERROR: GIT_USER_NAME build argument is required" && exit 1) + +ENV GIT_USER_EMAIL=${GIT_USER_EMAIL} +ENV GIT_USER_NAME=${GIT_USER_NAME} + +# Force to log to console +ENV MCP_LOG_LEVEL=info # ---- Builder ---- # Build the application @@ -19,29 +34,54 @@ FROM base AS builder WORKDIR /usr/src/app # Copy dependency manifests and install *all* dependencies (including dev) COPY package.json package-lock.json* ./ -RUN npm ci # Copy the rest of the source code COPY . . + +RUN npm ci # Build the TypeScript project RUN npm run build +RUN npm run postbuild + +# ---- Production Dependencies ---- +# Install only production dependencies for the final image +FROM base AS prod-deps +WORKDIR /usr/src/app +COPY package.json package-lock.json* ./ +RUN npm ci --only=production # ---- Runner ---- # Final stage with only production dependencies and built code FROM base AS runner WORKDIR /usr/src/app -# Copy production node_modules from the 'deps' stage -COPY --from=deps /usr/src/app/node_modules ./node_modules +# Copy production node_modules from the 'prod-deps' stage +COPY --from=prod-deps /usr/src/app/node_modules ./node_modules # Copy built application from the 'builder' stage COPY --from=builder /usr/src/app/dist ./dist # Copy package.json (needed for potential runtime info, like version) COPY package.json . -# Create a non-root user and switch to it -RUN addgroup -S appgroup && adduser -S appuser -G appgroup -USER appuser +# Add git to the container +RUN apk update +RUN apk add git + +# The container has a default user called node(1000), we need to delete it +# in case this conflicts with our local user/group ID +RUN deluser node + +# Create a non-root user and switch to ituse +RUN addgroup -g ${GROUP_ID} appgroup +RUN adduser -u ${USER_ID} -G appgroup -D appuser + +# This seems to need to exist, though we really just want to log to the console +RUN mkdir logs && chown appuser:appgroup logs + +USER appuser:appgroup + +# Configure git before switching to non-root user +RUN git config --global user.email "${GIT_USER_EMAIL}" +RUN git config --global user.name "${GIT_USER_NAME}" -# Expose port if the application runs a server (adjust if needed) -# EXPOSE 3000 +EXPOSE ${MCP_HTTP_PORT} # Command to run the application CMD ["node", "dist/index.js"] diff --git a/README.md b/README.md index 1e8651e..3258955 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ Add the following to your MCP client's configuration file (e.g., `cline_mcp_sett npm install @cyanheads/git-mcp-server ``` -### 3. Running the Server +##### Running the Server - **Production (Stdio):** ```bash @@ -139,6 +139,44 @@ npm install @cyanheads/git-mcp-server npm run dev:http ``` +### Installing witrh Docker + +You can use the Dockerfile to create an image that hosts the mcp server. To build: + +```sh +./scripts/build-docker.sh +``` + +Once complete, run the docker container. Refer to the example [docker-compose](./scripts/docker-compose.example.yaml) to create a docker compose file for your container. Once completed you can start the server with, for example, if you've put a file called `docker-compose.git-mcp.yaml` in your home directory; + + +```sh +docker compose -f ~/docker-compose.git-mcp.yaml up -d +``` + +#### Considerations when running with docker + +Your local file system is made available to the server in Docker using a volume mount. This doesn't change the permissions; it appears inside the container +with the same uid/gid as your host machine. + +##### Privileges to read/write files in the mount + +The docker image MUST be built with the same uid/gid as your active user using the server. Otherwise, it may not be able to access the files on the mount. The build script should configure this correctly. + +You need to add a volume that exposes the root of the git repos you plan to work with. The best strategy for this depends on the operating system + +##### Linux/Mac/WSL + +It's recommended to configure the volume at the same path inside the container as the actual path on your computer. This way agents are likely to find your repositories easily since the path will be the same as from the host where it's making the request. + +##### Windows + +If you're using Windows, this won't work, since the paths may include drive letters or UNC paths. Additionally, Docker on Windows + + - Repos in the WSL filesystem work the same way as linux/mac + - Repos in the windows filesystem will need to be accessed through the WSL mount, e.g. /mnt/c for the C drive. Since paths won't map the same way as the do in *nix type systems, I'd suggest making a volume called `/windows-code` or similar. Agents can be trained to map windows paths to this root when querying the server. + + ## ⚙️ Configuration Configure the server using these environment variables (or a `.env` file): diff --git a/scripts/build-docker.sh b/scripts/build-docker.sh new file mode 100755 index 0000000..c2fcddb --- /dev/null +++ b/scripts/build-docker.sh @@ -0,0 +1,14 @@ +#!/bin/bash +VERSION=$(grep -m 1 "^## v" CHANGELOG.md | sed 's/^## v\([0-9.]*\).*/\1/') +GIT_USER_NAME=$(git config user.name) +GIT_USER_EMAIL=$(git config user.email) +USER_ID=$(id -u) +GROUP_ID=$(id -g) +docker build --no-cache\ + --build-arg USER_ID="$USER_ID" \ + --build-arg GROUP_ID="$GROUP_ID" \ + --build-arg GIT_USER_NAME="$GIT_USER_NAME" \ + --build-arg GIT_USER_EMAIL="$GIT_USER_EMAIL" \ + -t git-mcp-server:$VERSION \ + -t git-mcp-server:latest \ + . \ No newline at end of file diff --git a/scripts/docker-compose.example.yaml b/scripts/docker-compose.example.yaml new file mode 100644 index 0000000..873a003 --- /dev/null +++ b/scripts/docker-compose.example.yaml @@ -0,0 +1,13 @@ +services: + git-mcp-server: + image: git-mcp-server + container_name: gitmcp + ports: + - "3015:3015" + volumes: + # Use to expose your code base to the service. + # Example mount for linux/wsl/mac, replace with your actual username. + - /home//code:/home//code + # Example mount for windows, replace with your actual username. + - /mnt/c/Users//code:/windows-code + restart: unless-stopped \ No newline at end of file From 730a4654de3d355203eed8a166a06161dbe27c3b Mon Sep 17 00:00:00 2001 From: Jamie Treworgy Date: Fri, 5 Sep 2025 16:58:35 -0400 Subject: [PATCH 2/4] Correct typos; minor organization in Dockerfile --- Dockerfile | 4 ++-- README.md | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4c302bc..7984d6e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,14 +5,14 @@ WORKDIR /usr/src/app #ENV NODE_ENV=production ENV MCP_TRANSPORT_TYPE=http -ARG USER_ID -ARG GROUP_ID ARG MCP_HTTP_HOST=0.0.0.0 ENV MCP_HTTP_HOST=${MCP_HTTP_HOST} ARG MCP_HTTP_PORT=3015 ENV MCP_HTTP_PORT=${MCP_HTTP_PORT} ARG MCP_LOG_LEVEL=info ENV MCP_LOG_LEVEL=${MCP_LOG_LEVEL} +ARG USER_ID +ARG GROUP_ID ARG GIT_USER_EMAIL ARG GIT_USER_NAME diff --git a/README.md b/README.md index 3258955..bf2bf5d 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ npm install @cyanheads/git-mcp-server npm run dev:http ``` -### Installing witrh Docker +### Installing with Docker You can use the Dockerfile to create an image that hosts the mcp server. To build: @@ -167,14 +167,14 @@ You need to add a volume that exposes the root of the git repos you plan to work ##### Linux/Mac/WSL -It's recommended to configure the volume at the same path inside the container as the actual path on your computer. This way agents are likely to find your repositories easily since the path will be the same as from the host where it's making the request. +It's recommended that you configure the volume at the same path inside the container as the actual path on your computer. This way agents are likely to find your repositories easily since the path will be the same as from the host where it's making the request. ##### Windows -If you're using Windows, this won't work, since the paths may include drive letters or UNC paths. Additionally, Docker on Windows +If you're using Windows, this won't work, since the paths may include drive letters or UNC paths. - Repos in the WSL filesystem work the same way as linux/mac - - Repos in the windows filesystem will need to be accessed through the WSL mount, e.g. /mnt/c for the C drive. Since paths won't map the same way as the do in *nix type systems, I'd suggest making a volume called `/windows-code` or similar. Agents can be trained to map windows paths to this root when querying the server. + - Repos in the windows filesystem will need to be accessed through the WSL mount, e.g. `/mnt/c` for the C drive. Since paths won't map the same way as the do in *nix type systems, I'd suggest making a volume called `/windows-code` or similar. Agents can be trained to map windows paths to this root when querying the server. ## ⚙️ Configuration From b33c1a7602cb5ba7ea3a9a1e4f169cc0bc2168f3 Mon Sep 17 00:00:00 2001 From: Jamie Treworgy Date: Mon, 8 Sep 2025 12:30:34 +0000 Subject: [PATCH 3/4] Configure environment for current user on container startup instead of build --- Dockerfile | 39 ++++++++--------------------- scripts/build-docker.sh | 8 ------ scripts/docker-compose.example.yaml | 7 ++++++ scripts/docker-entrypoint.sh | 13 ++++++++++ 4 files changed, 31 insertions(+), 36 deletions(-) create mode 100755 scripts/docker-entrypoint.sh diff --git a/Dockerfile b/Dockerfile index 7984d6e..139856f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,6 @@ # Use a specific Node.js version known to work, Alpine for smaller size FROM node:23-alpine AS base WORKDIR /usr/src/app -#ENV NODE_ENV=production ENV MCP_TRANSPORT_TYPE=http ARG MCP_HTTP_HOST=0.0.0.0 @@ -11,19 +10,6 @@ ARG MCP_HTTP_PORT=3015 ENV MCP_HTTP_PORT=${MCP_HTTP_PORT} ARG MCP_LOG_LEVEL=info ENV MCP_LOG_LEVEL=${MCP_LOG_LEVEL} -ARG USER_ID -ARG GROUP_ID -ARG GIT_USER_EMAIL -ARG GIT_USER_NAME - -# The user/group ID must match your local user/group ID -RUN test -n "$USER_ID" || (echo "ERROR: USER_ID build argument is required" && exit 1) -RUN test -n "$GROUP_ID" || (echo "ERROR: GROUP_ID build argument is required" && exit 1) -RUN test -n "$GIT_USER_EMAIL" || (echo "ERROR: GIT_USER_EMAIL build argument is required" && exit 1) -RUN test -n "$GIT_USER_NAME" || (echo "ERROR: GIT_USER_NAME build argument is required" && exit 1) - -ENV GIT_USER_EMAIL=${GIT_USER_EMAIL} -ENV GIT_USER_NAME=${GIT_USER_NAME} # Force to log to console ENV MCP_LOG_LEVEL=info @@ -64,24 +50,21 @@ COPY package.json . RUN apk update RUN apk add git -# The container has a default user called node(1000), we need to delete it -# in case this conflicts with our local user/group ID -RUN deluser node - -# Create a non-root user and switch to ituse -RUN addgroup -g ${GROUP_ID} appgroup -RUN adduser -u ${USER_ID} -G appgroup -D appuser - # This seems to need to exist, though we really just want to log to the console -RUN mkdir logs && chown appuser:appgroup logs +RUN mkdir logs && chmod 777 logs -USER appuser:appgroup +COPY ./scripts/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh +RUN chmod +x /usr/local/bin/docker-entrypoint.sh -# Configure git before switching to non-root user -RUN git config --global user.email "${GIT_USER_EMAIL}" -RUN git config --global user.name "${GIT_USER_NAME}" +# In this base image "node" is 1000:1000. It doesn't matter what user is used to run the server; +# rather the user ID should match the uid/gid of the host user so that the server can +# read and write files in the volume mount. +RUN chmod 777 /home/node +ENV HOME=/home/node +USER node:node +ENV NODE_ENV=production EXPOSE ${MCP_HTTP_PORT} -# Command to run the application +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] CMD ["node", "dist/index.js"] diff --git a/scripts/build-docker.sh b/scripts/build-docker.sh index c2fcddb..f51f8bc 100755 --- a/scripts/build-docker.sh +++ b/scripts/build-docker.sh @@ -1,14 +1,6 @@ #!/bin/bash VERSION=$(grep -m 1 "^## v" CHANGELOG.md | sed 's/^## v\([0-9.]*\).*/\1/') -GIT_USER_NAME=$(git config user.name) -GIT_USER_EMAIL=$(git config user.email) -USER_ID=$(id -u) -GROUP_ID=$(id -g) docker build --no-cache\ - --build-arg USER_ID="$USER_ID" \ - --build-arg GROUP_ID="$GROUP_ID" \ - --build-arg GIT_USER_NAME="$GIT_USER_NAME" \ - --build-arg GIT_USER_EMAIL="$GIT_USER_EMAIL" \ -t git-mcp-server:$VERSION \ -t git-mcp-server:latest \ . \ No newline at end of file diff --git a/scripts/docker-compose.example.yaml b/scripts/docker-compose.example.yaml index 873a003..c4b2b9a 100644 --- a/scripts/docker-compose.example.yaml +++ b/scripts/docker-compose.example.yaml @@ -4,6 +4,13 @@ services: container_name: gitmcp ports: - "3015:3015" + environment: + # Git user configuration - set these to your Git user details + - GIT_USER_NAME=Your Name + - GIT_USER_EMAIL=your.email@example.com + # Optional: Override other MCP settings + # - MCP_LOG_LEVEL=debug + # - GIT_SIGN_COMMITS=true volumes: # Use to expose your code base to the service. # Example mount for linux/wsl/mac, replace with your actual username. diff --git a/scripts/docker-entrypoint.sh b/scripts/docker-entrypoint.sh new file mode 100755 index 0000000..aa759d7 --- /dev/null +++ b/scripts/docker-entrypoint.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +# Configure Git user settings if environment variables are provided +if [ -n "$GIT_USER_EMAIL" ] && [ -n "$GIT_USER_NAME" ]; then + echo "Configuring Git user: $GIT_USER_NAME <$GIT_USER_EMAIL>" + git config --global user.email "$GIT_USER_EMAIL" + git config --global user.name "$GIT_USER_NAME" +else + echo "Warning: GIT_USER_EMAIL and/or GIT_USER_NAME not set. Git operations may fail without proper user configuration." +fi + +# Execute the main command +exec "$@" \ No newline at end of file From 8cf2df0136524a3ae80c826edd8b0e7a13a2abee Mon Sep 17 00:00:00 2001 From: Jamie Treworgy Date: Mon, 8 Sep 2025 08:34:07 -0400 Subject: [PATCH 4/4] Update docker-compose example --- scripts/docker-compose.example.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/docker-compose.example.yaml b/scripts/docker-compose.example.yaml index c4b2b9a..5b5df4e 100644 --- a/scripts/docker-compose.example.yaml +++ b/scripts/docker-compose.example.yaml @@ -4,6 +4,8 @@ services: container_name: gitmcp ports: - "3015:3015" + # Configure to run with the same user ID as the host user + user: "1000:1000" environment: # Git user configuration - set these to your Git user details - GIT_USER_NAME=Your Name