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:
@@ -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 \
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user