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
This commit is contained in:
2026-06-07 17:57:24 +00:00
parent 6b7c2bab1e
commit 214cc32ac2
3 changed files with 44 additions and 36 deletions

View File

@@ -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 # The shim spawns boocontext as a child MCP process over stdio NDJSON,
# (codecontext from source + our HTTP shim), then a minimal alpine:3.20 # translating HTTP requests to MCP tools/call.
# runtime holds both.
# #
# No upstream Docker image exists for codecontext. We clone the repo # To stage the fork source for a Docker build:
# directly because the module path declared in go.mod # tar -czf codecontext/fork.tar.gz -C /opt/forks/boocontext \
# (github.com/nuthan-ms/codecontext) differs from the GitHub repo URL # --exclude=.git --exclude=node_modules --exclude=dist
# (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.
FROM golang:1.24-alpine AS builder # Stage 1: Go shim builder
WORKDIR /build FROM golang:1.24-alpine AS shim-builder
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.
WORKDIR /build/shim WORKDIR /build/shim
RUN apk add --no-cache ca-certificates
COPY go.mod ./ COPY go.mod ./
COPY shim.go ./ COPY shim.go ./
RUN CGO_ENABLED=0 GOOS=linux go build -o /build/shim-bin ./ RUN CGO_ENABLED=0 GOOS=linux go build -o /build/shim-bin ./
# Runtime: alpine matches the build target so codecontext's cgo bindings # Stage 2: boocontext MCP builder
# resolve against the same musl libc. 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 FROM alpine:3.20
RUN apk add --no-cache ca-certificates RUN apk add --no-cache ca-certificates nodejs uv
COPY --from=builder /build/codecontext-bin /usr/local/bin/codecontext COPY --from=shim-builder /build/shim-bin /usr/local/bin/shim
COPY --from=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 EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s \ HEALTHCHECK --interval=30s --timeout=5s --start-period=30s \

View File

@@ -26,6 +26,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"os/signal" "os/signal"
"strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"syscall" "syscall"
@@ -185,13 +186,14 @@ func notify(method string, params any) error {
// ---- Child lifecycle ---- // ---- Child lifecycle ----
func startChild() error { func startChild() error {
// `codecontext mcp` with --watch=true (the default) keeps fsnotify // Support CODECONTEXT_CHILD env var for overriding the MCP child command.
// running on the indexed directory; the per-call target_dir swap // Default to boocontext (Node.js MCP aggregator). Set in docker-compose.
// invalidates and re-indexes on demand. `--target=/opt/projects` is the childCmd := os.Getenv("CODECONTEXT_CHILD")
// initial scan target — codecontext rebuilds the graph against whatever if childCmd == "" {
// target_dir each call carries, so this is just a valid bootstrap path childCmd = "node /usr/local/lib/boocontext/dist/index.js"
// (the default "." is the alpine root and trips on transient /proc fds). }
child = exec.Command("codecontext", "mcp", "--target=/opt/projects", "--watch=true", "--respect-gitignore") parts := strings.Split(childCmd, " ")
child = exec.Command(parts[0], parts[1:]...)
var err error var err error
childStdin, err = child.StdinPipe() childStdin, err = child.StdinPipe()
if err != nil { if err != nil {
@@ -417,6 +419,9 @@ func main() {
mux.HandleFunc("POST /v1/watch_changes", makeToolHandler("watch_changes")) 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_semantic_neighborhoods", makeToolHandler("get_semantic_neighborhoods"))
mux.HandleFunc("POST /v1/get_framework_analysis", makeToolHandler("get_framework_analysis")) 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{ server := &http.Server{
Addr: ":8080", Addr: ":8080",

View File

@@ -109,10 +109,16 @@ services:
ports: ports:
- "127.0.0.1:8080:8080" - "127.0.0.1:8080:8080"
restart: unless-stopped 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: networks:
- boocode_net - boocode_net
volumes: volumes:
- /opt:/opt:ro - /opt:/opt:ro
- /opt/forks:/opt/forks:ro
healthcheck: healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health || exit 1"] test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health || exit 1"]
interval: 30s interval: 30s