From fa3244f3449ea03fa687367dc18e597576a6fee6 Mon Sep 17 00:00:00 2001 From: indifferentketchup Date: Sun, 7 Jun 2026 17:57:24 +0000 Subject: [PATCH] feat(codecontext): upgrade sidecar to boocontext MCP aggregator - Multi-stage Dockerfile builds boocontext (Node) + HTTP shim (Go) - shim.go supports CODECONTEXT_CHILD env var for configurable MCP child - Adds routes for get_symbol_details, get_call_graph, get_blast_radius - docker-compose.yml adds env vars for child MCP paths --- codecontext/Dockerfile | 55 ++++++++++++++++++++---------------------- codecontext/shim.go | 19 +++++++++------ docker-compose.yml | 6 +++++ 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/codecontext/Dockerfile b/codecontext/Dockerfile index 529dd82..5f1d049 100644 --- a/codecontext/Dockerfile +++ b/codecontext/Dockerfile @@ -1,41 +1,38 @@ -# v1.12 Track B — codecontext sidecar container. +# v2.8 — boocontext sidecar container. +# Multi-stage build: Go shim from golang:1.24-alpine, boocontext MCP aggregator +# from node:20-alpine, then an alpine:3.20 runtime holding both. # -# Multi-stage build: golang:1.24-alpine builder produces two binaries -# (codecontext from source + our HTTP shim), then a minimal alpine:3.20 -# runtime holds both. +# The shim spawns boocontext as a child MCP process over stdio NDJSON, +# translating HTTP requests to MCP tools/call. # -# No upstream Docker image exists for codecontext. We clone the repo -# directly because the module path declared in go.mod -# (github.com/nuthan-ms/codecontext) differs from the GitHub repo URL -# (github.com/nmakod/codecontext) — `go install` against the GitHub path -# wouldn't resolve. The tagged v3.2.1 source tree is the same either way. +# To stage the fork source for a Docker build: +# tar -czf codecontext/fork.tar.gz -C /opt/forks/boocontext \ +# --exclude=.git --exclude=node_modules --exclude=dist -FROM golang:1.24-alpine AS builder -WORKDIR /build - -RUN apk add --no-cache git ca-certificates build-base - -# Build codecontext from the boocode-ts fork (has .codecontextignore support). -# Source is staged into the build context by the pre-build step: -# tar -czf codecontext/fork.tar.gz -C /opt/forks/codecontext . -# CGO is required: codecontext binds tree-sitter via cgo. -COPY fork.tar.gz /build/fork.tar.gz -RUN mkdir -p /build/codecontext && tar -xzf /build/fork.tar.gz -C /build/codecontext -WORKDIR /build/codecontext -RUN CGO_ENABLED=1 GOOS=linux go build -o /build/codecontext-bin ./cmd/codecontext - -# Build the shim. Stdlib-only — no go.sum needed. +# Stage 1: Go shim builder +FROM golang:1.24-alpine AS shim-builder WORKDIR /build/shim +RUN apk add --no-cache ca-certificates COPY go.mod ./ COPY shim.go ./ RUN CGO_ENABLED=0 GOOS=linux go build -o /build/shim-bin ./ -# Runtime: alpine matches the build target so codecontext's cgo bindings -# resolve against the same musl libc. +# Stage 2: boocontext MCP builder +FROM node:20-alpine AS boocontext-builder +WORKDIR /build/boocontext +RUN apk add --no-cache git python3 make g++ ca-certificates +COPY fork.tar.gz /build/fork.tar.gz +RUN mkdir -p /build/boocontext && tar -xzf /build/fork.tar.gz -C /build/boocontext +WORKDIR /build/boocontext +RUN npm ci && npm run build + +# Stage 3: Runtime FROM alpine:3.20 -RUN apk add --no-cache ca-certificates -COPY --from=builder /build/codecontext-bin /usr/local/bin/codecontext -COPY --from=builder /build/shim-bin /usr/local/bin/shim +RUN apk add --no-cache ca-certificates nodejs uv +COPY --from=shim-builder /build/shim-bin /usr/local/bin/shim +COPY --from=boocontext-builder /build/boocontext/dist /usr/local/lib/boocontext/dist +COPY --from=boocontext-builder /build/boocontext/node_modules /usr/local/lib/boocontext/node_modules +COPY --from=boocontext-builder /build/boocontext/package.json /usr/local/lib/boocontext/package.json EXPOSE 8080 HEALTHCHECK --interval=30s --timeout=5s --start-period=30s \ diff --git a/codecontext/shim.go b/codecontext/shim.go index 5e4b3c5..3a38f28 100644 --- a/codecontext/shim.go +++ b/codecontext/shim.go @@ -26,6 +26,7 @@ import ( "os" "os/exec" "os/signal" + "strings" "sync" "sync/atomic" "syscall" @@ -185,13 +186,14 @@ func notify(method string, params any) error { // ---- Child lifecycle ---- func startChild() error { - // `codecontext mcp` with --watch=true (the default) keeps fsnotify - // running on the indexed directory; the per-call target_dir swap - // invalidates and re-indexes on demand. `--target=/opt/projects` is the - // initial scan target — codecontext rebuilds the graph against whatever - // target_dir each call carries, so this is just a valid bootstrap path - // (the default "." is the alpine root and trips on transient /proc fds). - child = exec.Command("codecontext", "mcp", "--target=/opt/projects", "--watch=true", "--respect-gitignore") + // Support CODECONTEXT_CHILD env var for overriding the MCP child command. + // Default to boocontext (Node.js MCP aggregator). Set in docker-compose. + childCmd := os.Getenv("CODECONTEXT_CHILD") + if childCmd == "" { + childCmd = "node /usr/local/lib/boocontext/dist/index.js" + } + parts := strings.Split(childCmd, " ") + child = exec.Command(parts[0], parts[1:]...) var err error childStdin, err = child.StdinPipe() if err != nil { @@ -417,6 +419,9 @@ func main() { mux.HandleFunc("POST /v1/watch_changes", makeToolHandler("watch_changes")) mux.HandleFunc("POST /v1/get_semantic_neighborhoods", makeToolHandler("get_semantic_neighborhoods")) mux.HandleFunc("POST /v1/get_framework_analysis", makeToolHandler("get_framework_analysis")) + mux.HandleFunc("POST /v1/get_symbol_details", makeToolHandler("get_symbol_details")) + mux.HandleFunc("POST /v1/get_call_graph", makeToolHandler("get_call_graph")) + mux.HandleFunc("POST /v1/get_blast_radius", makeToolHandler("get_blast_radius")) server := &http.Server{ Addr: ":8080", diff --git a/docker-compose.yml b/docker-compose.yml index 8adbb3b..1333767 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -109,10 +109,16 @@ services: ports: - "127.0.0.1:8080:8080" restart: unless-stopped + environment: + CODECONTEXT_CHILD: node /usr/local/lib/boocontext/dist/index.js + TYPE_INJECT_MCP_PATH: /opt/type-inject/packages/mcp/dist/index.js + TREE_SITTER_MCP_CMD: uvx + TREE_SITTER_MCP_ARGS: --from tree-sitter-analyzer[mcp] tree-sitter-analyzer-mcp networks: - boocode_net volumes: - /opt:/opt:ro + - /opt/forks:/opt/forks:ro healthcheck: test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health || exit 1"] interval: 30s