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..139856f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,16 +2,17 @@ # 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 -# ---- 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 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} + +# Force to log to console +ENV MCP_LOG_LEVEL=info # ---- Builder ---- # Build the application @@ -19,29 +20,51 @@ 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 -# Expose port if the application runs a server (adjust if needed) -# EXPOSE 3000 +# This seems to need to exist, though we really just want to log to the console +RUN mkdir logs && chmod 777 logs + +COPY ./scripts/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +# 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/README.md b/README.md index 1e8651e..bf2bf5d 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 with 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 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. + + - 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..f51f8bc --- /dev/null +++ b/scripts/build-docker.sh @@ -0,0 +1,6 @@ +#!/bin/bash +VERSION=$(grep -m 1 "^## v" CHANGELOG.md | sed 's/^## v\([0-9.]*\).*/\1/') +docker build --no-cache\ + -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..5b5df4e --- /dev/null +++ b/scripts/docker-compose.example.yaml @@ -0,0 +1,22 @@ +services: + git-mcp-server: + image: git-mcp-server + 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 + - 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. + - /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 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