Skip to content
Merged
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
7 changes: 7 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
target/
.git/
.github/
.claude/
.serena/
IMPROVEMENT_PLAN.md
README.md
52 changes: 52 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Docker

on:
push:
tags:
- '**[0-9]+.[0-9]+.[0-9]+*'

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
docker:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v6

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
58 changes: 58 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# ============================================================================
# Stage 1: Chef — prepare recipe for dependency caching
# ============================================================================
FROM rust:1.95-slim AS chef

RUN apt-get update && apt-get install -y --no-install-recommends \
pkg-config \
&& rm -rf /var/lib/apt/lists/*

RUN cargo install cargo-chef --locked
WORKDIR /app

# ============================================================================
# Stage 2: Planner — compute dependency recipe (cache-key)
# ============================================================================
FROM chef AS planner
COPY Cargo.toml Cargo.lock ./
COPY src/ src/
RUN cargo chef prepare --recipe-path recipe.json

# ============================================================================
# Stage 3: Builder — build dependencies (cached) then application
# ============================================================================
FROM chef AS builder

COPY --from=planner /app/recipe.json recipe.json

# Build dependencies only — this layer is cached until Cargo.toml/lock change
RUN cargo chef cook --release --recipe-path recipe.json

# Now build the actual application
COPY Cargo.toml Cargo.lock ./
COPY src/ src/
RUN cargo build --release \
&& strip target/release/code-mcp

# ============================================================================
# Stage 4: Runtime — minimal distroless-style image
# ============================================================================
FROM debian:bookworm-slim AS runtime

RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
git \
&& rm -rf /var/lib/apt/lists/* \
&& useradd --create-home --shell /bin/false mcp

COPY --from=builder /app/target/release/code-mcp /usr/local/bin/code-mcp

USER mcp
WORKDIR /project

EXPOSE 8080

# All CLI flags are passed directly via CMD/args — ENTRYPOINT is the binary
ENTRYPOINT ["code-mcp"]
# Sensible defaults: bind all interfaces, project root = /project
CMD ["--bind", "0.0.0.0:8080", "--project", "/project"]
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ Re-uses ripgrep crates just like ripgrep itself.
### Pre-built binaries
Download the latest release from the [Releases page](https://github.com/devfire/code-mcp/releases).

### Docker (GHCR)
```bash
docker pull ghcr.io/devfire/code-mcp:latest
docker run -p 8080:8080 -v /path/to/repo:/project:ro ghcr.io/devfire/code-mcp
```

Multi-arch images (`linux/amd64`, `linux/arm64`) are published automatically on every release.

### From source
```bash
cargo install --git https://github.com/devfire/code-mcp.git
Expand Down Expand Up @@ -115,6 +123,40 @@ cargo build --release
./target/release/code-mcp --bind 0.0.0.0:8080 --project ./my/repo
```

### Docker

The included `Dockerfile` uses a multi-stage build with `cargo-chef` for optimal layer caching. The final image is based on `debian:bookworm-slim` (~80 MB) and contains only the stripped binary and `git` (needed by the `ignore` crate for `.gitignore` traversal).

```sh
# Build
docker build -t code-mcp .

# Run with defaults (bind 0.0.0.0:8080, project /project)
docker run -p 8080:8080 -v /path/to/repo:/project:ro code-mcp

# Override any flag — all CLI args are supported natively via ENTRYPOINT
docker run -p 9090:9090 \
-v /path/to/repo:/src:ro \
-v /path/to/memories:/memories:ro \
code-mcp \
--bind 0.0.0.0:9090 \
--project /src \
--memory-dir /memories \
--max-sessions 128 \
--initialize-rate-per-min 20 \
--session-idle-timeout-secs 3600

# With debug logging
docker run -p 8080:8080 -e RUST_LOG=debug,rmcp=info \
-v /path/to/repo:/project:ro code-mcp
```

The `ENTRYPOINT` is the binary itself, so any arguments after the image name replace the default `CMD` and go directly to clap. Pass `--help` to see all options:

```sh
docker run --rm code-mcp --help
```

Flags:

- `--bind <addr:port>` — default `0.0.0.0:8080`.
Expand Down