chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
pkgbase = codebase-memory-mcp
|
||||
pkgdesc = Fast code intelligence engine for AI coding agents — single static binary MCP server
|
||||
pkgver = 0.8.1
|
||||
pkgrel = 1
|
||||
url = https://github.com/DeusData/codebase-memory-mcp
|
||||
arch = x86_64
|
||||
arch = aarch64
|
||||
license = MIT
|
||||
provides = codebase-memory-mcp
|
||||
conflicts = codebase-memory-mcp
|
||||
source_x86_64 = codebase-memory-mcp-0.8.1-linux-amd64.tar.gz::https://github.com/DeusData/codebase-memory-mcp/releases/download/v0.8.1/codebase-memory-mcp-linux-amd64.tar.gz
|
||||
sha256sums_x86_64 = dbd3b92ea870ef240b63059f26bda15015f76ef9978931bebc3a0f9d09470973
|
||||
source_aarch64 = codebase-memory-mcp-0.8.1-linux-arm64.tar.gz::https://github.com/DeusData/codebase-memory-mcp/releases/download/v0.8.1/codebase-memory-mcp-linux-arm64.tar.gz
|
||||
sha256sums_aarch64 = d2f842d1365da5c35d9c5796f57a821c9745267350994346735e1e6e04d46091
|
||||
|
||||
pkgname = codebase-memory-mcp
|
||||
@@ -0,0 +1,28 @@
|
||||
# Maintainer: DeusData <https://github.com/DeusData>
|
||||
pkgname=codebase-memory-mcp
|
||||
pkgver=0.8.1
|
||||
pkgrel=1
|
||||
pkgdesc="Fast code intelligence engine for AI coding agents — single static binary MCP server"
|
||||
arch=('x86_64' 'aarch64')
|
||||
url="https://github.com/DeusData/codebase-memory-mcp"
|
||||
license=('MIT')
|
||||
provides=("$pkgname")
|
||||
conflicts=("$pkgname")
|
||||
|
||||
source_x86_64=("${pkgname}-${pkgver}-linux-amd64.tar.gz::https://github.com/DeusData/codebase-memory-mcp/releases/download/v${pkgver}/codebase-memory-mcp-linux-amd64.tar.gz")
|
||||
source_aarch64=("${pkgname}-${pkgver}-linux-arm64.tar.gz::https://github.com/DeusData/codebase-memory-mcp/releases/download/v${pkgver}/codebase-memory-mcp-linux-arm64.tar.gz")
|
||||
|
||||
sha256sums_x86_64=('dbd3b92ea870ef240b63059f26bda15015f76ef9978931bebc3a0f9d09470973')
|
||||
sha256sums_aarch64=('d2f842d1365da5c35d9c5796f57a821c9745267350994346735e1e6e04d46091')
|
||||
|
||||
package() {
|
||||
install -Dm755 "${srcdir}/codebase-memory-mcp" \
|
||||
"${pkgdir}/usr/bin/codebase-memory-mcp"
|
||||
install -Dm644 "${srcdir}/LICENSE" \
|
||||
"${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
|
||||
# Third-party attribution bundle (present in archives since v0.8.1)
|
||||
if [ -f "${srcdir}/THIRD_PARTY_NOTICES.md" ]; then
|
||||
install -Dm644 "${srcdir}/THIRD_PARTY_NOTICES.md" \
|
||||
"${pkgdir}/usr/share/licenses/${pkgname}/THIRD_PARTY_NOTICES.md"
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>codebase-memory-mcp</id>
|
||||
<version>0.8.1</version>
|
||||
<title>codebase-memory-mcp</title>
|
||||
<authors>DeusData</authors>
|
||||
<projectUrl>https://github.com/DeusData/codebase-memory-mcp</projectUrl>
|
||||
<licenseUrl>https://github.com/DeusData/codebase-memory-mcp/blob/main/LICENSE</licenseUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<projectSourceUrl>https://github.com/DeusData/codebase-memory-mcp</projectSourceUrl>
|
||||
<bugTrackerUrl>https://github.com/DeusData/codebase-memory-mcp/issues</bugTrackerUrl>
|
||||
<tags>mcp ai claude code-intelligence codebase developer-tools tree-sitter llm</tags>
|
||||
<summary>Fast code intelligence engine for AI coding agents</summary>
|
||||
<description>
|
||||
codebase-memory-mcp is a single static binary MCP server that indexes codebases at
|
||||
extreme speed and exposes 14 MCP tools for AI coding agents.
|
||||
|
||||
Full indexes an average repository in milliseconds, the Linux kernel (28M LOC) in
|
||||
3 minutes. Answers structural queries in under 1ms. Supports 159 languages via
|
||||
vendored tree-sitter grammars. Zero dependencies. Plug and play across 11 coding
|
||||
agents including Claude Code, Codex CLI, Gemini CLI, and Zed.
|
||||
|
||||
After installation, run:
|
||||
|
||||
codebase-memory-mcp install
|
||||
|
||||
to configure your coding agents automatically.
|
||||
</description>
|
||||
<releaseNotes>https://github.com/DeusData/codebase-memory-mcp/releases/tag/v0.8.1</releaseNotes>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="tools\**" target="tools" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -0,0 +1,25 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$packageName = 'codebase-memory-mcp'
|
||||
$version = '0.8.1'
|
||||
$url64 = "https://github.com/DeusData/codebase-memory-mcp/releases/download/v${version}/codebase-memory-mcp-windows-amd64.zip"
|
||||
$checksum64 = 'a602ad090ed3f49d86c55472f73f27ad7055222806a82358f2e08513e027f00f'
|
||||
$installDir = Join-Path $env:ChocolateyBinRoot $packageName
|
||||
|
||||
Install-ChocolateyZipPackage `
|
||||
-PackageName $packageName `
|
||||
-Url64bit $url64 `
|
||||
-Checksum64 $checksum64 `
|
||||
-ChecksumType64 'sha256' `
|
||||
-UnzipLocation $installDir
|
||||
|
||||
# Shim the binary so it is on PATH
|
||||
$binPath = Join-Path $installDir 'codebase-memory-mcp.exe'
|
||||
Install-BinFile -Name 'codebase-memory-mcp' -Path $binPath
|
||||
|
||||
# Configure coding agents (non-fatal)
|
||||
try {
|
||||
& $binPath install -y 2>&1 | Out-Null
|
||||
} catch {
|
||||
Write-Warning "Agent configuration failed (non-fatal). Run manually: codebase-memory-mcp install"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$packageName = 'codebase-memory-mcp'
|
||||
$installDir = Join-Path $env:ChocolateyBinRoot $packageName
|
||||
|
||||
Uninstall-BinFile -Name 'codebase-memory-mcp'
|
||||
|
||||
if (Test-Path $installDir) {
|
||||
Remove-Item $installDir -Recurse -Force
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
# Glama MCP directory (glama.ai) check image — NOT required to run the tool.
|
||||
#
|
||||
# codebase-memory-mcp needs NO Docker to run. This image exists only so Glama
|
||||
# can build a sandbox, launch the stdio MCP server, and run its introspection
|
||||
# checks, which power the directory's score badge. The same image is exercised
|
||||
# locally and in CI by pkg/glama/verify.sh to guard against drift.
|
||||
#
|
||||
# We pull the "-portable" release asset, which is fully statically linked
|
||||
# (gcc -static) — unlike the standard asset, which dynamically links glibc/
|
||||
# libstdc++ and would fail on an older base. Because it's static, the runtime
|
||||
# base image is arbitrary. TARGETARCH is amd64 / arm64, matching the asset names.
|
||||
|
||||
FROM debian:bookworm-slim AS fetch
|
||||
ARG TARGETARCH
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends curl ca-certificates \
|
||||
&& curl -fsSL "https://github.com/DeusData/codebase-memory-mcp/releases/latest/download/codebase-memory-mcp-linux-${TARGETARCH}-portable.tar.gz" \
|
||||
| tar -xz -C /tmp codebase-memory-mcp LICENSE THIRD_PARTY_NOTICES.md \
|
||||
&& chmod +x /tmp/codebase-memory-mcp \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
FROM debian:bookworm-slim
|
||||
COPY --from=fetch /tmp/codebase-memory-mcp /usr/local/bin/codebase-memory-mcp
|
||||
COPY --from=fetch /tmp/LICENSE /tmp/THIRD_PARTY_NOTICES.md /usr/share/doc/codebase-memory-mcp/
|
||||
ENV CBM_CACHE_DIR=/tmp/cbm
|
||||
ENTRYPOINT ["codebase-memory-mcp"]
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build the Glama check image and confirm the MCP stdio server starts and
|
||||
# answers initialize + tools/list with no project indexed — exactly what
|
||||
# Glama's directory check does. Used locally and by the CI smoke suite.
|
||||
#
|
||||
# Env:
|
||||
# IMAGE image tag to build (default: cbm-glama-check)
|
||||
# DOCKER_BUILD_ARGS extra args for `docker build` (e.g. --platform linux/amd64)
|
||||
set -euo pipefail
|
||||
|
||||
IMAGE="${IMAGE:-cbm-glama-check}"
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
run_with_timeout() {
|
||||
local t="$1"; shift
|
||||
if command -v timeout >/dev/null 2>&1; then timeout "$t" "$@"
|
||||
elif command -v gtimeout >/dev/null 2>&1; then gtimeout "$t" "$@"
|
||||
else "$@"; fi
|
||||
}
|
||||
|
||||
echo "==> building ${IMAGE} (${DIR}/Dockerfile)"
|
||||
# shellcheck disable=SC2086
|
||||
docker build ${DOCKER_BUILD_ARGS:-} -f "${DIR}/Dockerfile" -t "${IMAGE}" "${DIR}"
|
||||
|
||||
echo "==> MCP introspection handshake (initialize -> initialized -> tools/list)"
|
||||
REQ="$(printf '%s\n%s\n%s\n' \
|
||||
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"glama-verify","version":"1"}}}' \
|
||||
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
|
||||
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}')"
|
||||
OUT="$(printf '%s' "${REQ}" | run_with_timeout 60 docker run -i --rm "${IMAGE}" || true)"
|
||||
|
||||
printf '%s\n' "${OUT}"
|
||||
|
||||
echo "==> assertions"
|
||||
printf '%s' "${OUT}" | grep -q '"result"' || { echo "FAIL: no JSON-RPC result (server did not respond)"; exit 1; }
|
||||
printf '%s' "${OUT}" | grep -q 'search_graph' || { echo "FAIL: tools/list missing expected tool 'search_graph'"; exit 1; }
|
||||
COUNT="$(printf '%s' "${OUT}" | grep -o '"name"' | wc -l | tr -d ' ')"
|
||||
echo "PASS: server started and introspected; ~${COUNT} name entries (>=14 tools expected)"
|
||||
@@ -0,0 +1,343 @@
|
||||
// codebase-memory-mcp — Go installer wrapper.
|
||||
//
|
||||
// On first run, downloads the pre-built binary for the current platform from
|
||||
// GitHub Releases, caches it, and replaces the current process with it.
|
||||
// Subsequent runs skip directly to exec.
|
||||
//
|
||||
// Install:
|
||||
//
|
||||
// go install github.com/DeusData/codebase-memory-mcp/pkg/go/cmd/codebase-memory-mcp@latest
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"archive/zip"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
repo = "DeusData/codebase-memory-mcp"
|
||||
version = "0.8.1"
|
||||
)
|
||||
|
||||
func main() {
|
||||
bin, err := ensureBinary()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "codebase-memory-mcp: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := execBinary(bin, os.Args[1:]); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "codebase-memory-mcp: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func ensureBinary() (string, error) {
|
||||
bin := binPath()
|
||||
if _, err := os.Stat(bin); err == nil {
|
||||
return bin, nil
|
||||
}
|
||||
if err := download(bin); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return bin, nil
|
||||
}
|
||||
|
||||
func binPath() string {
|
||||
name := "codebase-memory-mcp"
|
||||
if runtime.GOOS == "windows" {
|
||||
name += ".exe"
|
||||
}
|
||||
return filepath.Join(cacheDir(), version, name)
|
||||
}
|
||||
|
||||
func cacheDir() string {
|
||||
if d := os.Getenv("CBM_CACHE_DIR"); d != "" {
|
||||
return d
|
||||
}
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
if d := os.Getenv("LOCALAPPDATA"); d != "" {
|
||||
return filepath.Join(d, "codebase-memory-mcp")
|
||||
}
|
||||
case "darwin":
|
||||
if home, err := os.UserHomeDir(); err == nil {
|
||||
return filepath.Join(home, "Library", "Caches", "codebase-memory-mcp")
|
||||
}
|
||||
}
|
||||
if d := os.Getenv("XDG_CACHE_HOME"); d != "" {
|
||||
return filepath.Join(d, "codebase-memory-mcp")
|
||||
}
|
||||
if home, err := os.UserHomeDir(); err == nil {
|
||||
return filepath.Join(home, ".cache", "codebase-memory-mcp")
|
||||
}
|
||||
return filepath.Join(os.TempDir(), "codebase-memory-mcp")
|
||||
}
|
||||
|
||||
func goos() string {
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
return "darwin"
|
||||
case "linux":
|
||||
return "linux"
|
||||
case "windows":
|
||||
return "windows"
|
||||
default:
|
||||
return runtime.GOOS
|
||||
}
|
||||
}
|
||||
|
||||
func goarch() string {
|
||||
switch runtime.GOARCH {
|
||||
case "amd64":
|
||||
return "amd64"
|
||||
case "arm64":
|
||||
return "arm64"
|
||||
default:
|
||||
return runtime.GOARCH
|
||||
}
|
||||
}
|
||||
|
||||
func download(dest string) error {
|
||||
platform := goos()
|
||||
arch := goarch()
|
||||
ext := "tar.gz"
|
||||
if platform == "windows" {
|
||||
ext = "zip"
|
||||
}
|
||||
|
||||
archive := fmt.Sprintf("codebase-memory-mcp-%s-%s.%s", platform, arch, ext)
|
||||
url := fmt.Sprintf("https://github.com/%s/releases/download/v%s/%s", repo, version, archive)
|
||||
checksumURL := fmt.Sprintf("https://github.com/%s/releases/download/v%s/checksums.txt", repo, version)
|
||||
|
||||
fmt.Fprintf(os.Stderr, "codebase-memory-mcp: downloading v%s for %s/%s...\n", version, platform, arch)
|
||||
|
||||
tmp, err := os.MkdirTemp("", "cbm-install-*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
archivePath := filepath.Join(tmp, "cbm."+ext)
|
||||
if err := httpGet(url, archivePath); err != nil {
|
||||
return fmt.Errorf("download failed: %w", err)
|
||||
}
|
||||
|
||||
// Verify checksum if available (non-fatal if checksums.txt unreachable)
|
||||
if checksums, err := fetchChecksums(checksumURL); err == nil {
|
||||
if expected, ok := checksums[archive]; ok {
|
||||
if err := verifyChecksum(archivePath, expected); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binName := "codebase-memory-mcp"
|
||||
if platform == "windows" {
|
||||
binName += ".exe"
|
||||
}
|
||||
|
||||
if ext == "tar.gz" {
|
||||
if err := extractTarGz(archivePath, tmp, binName); err != nil {
|
||||
return fmt.Errorf("extraction failed: %w", err)
|
||||
}
|
||||
} else {
|
||||
if err := extractZip(archivePath, tmp, binName); err != nil {
|
||||
return fmt.Errorf("extraction failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil {
|
||||
return fmt.Errorf("could not create cache dir: %w", err)
|
||||
}
|
||||
|
||||
if err := copyFile(filepath.Join(tmp, binName), dest); err != nil {
|
||||
return fmt.Errorf("could not install binary: %w", err)
|
||||
}
|
||||
|
||||
if err := os.Chmod(dest, 0755); err != nil {
|
||||
return fmt.Errorf("could not set permissions: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateURLScheme rejects non-https URLs before any fetch (defense-in-depth).
|
||||
func validateURLScheme(rawURL string) error {
|
||||
if !strings.HasPrefix(rawURL, "https://") {
|
||||
return fmt.Errorf("refusing non-https URL: %s", rawURL)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// httpsOnlyClient returns an HTTP client that rejects non-HTTPS redirects.
|
||||
var httpsOnlyClient = &http.Client{
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
if req.URL.Scheme != "https" {
|
||||
return fmt.Errorf("refusing non-https redirect to %s", req.URL)
|
||||
}
|
||||
if len(via) >= 10 {
|
||||
return fmt.Errorf("too many redirects")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func httpGet(rawURL, dest string) error {
|
||||
if err := validateURLScheme(rawURL); err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := httpsOnlyClient.Get(rawURL) //nolint:gosec
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("HTTP %d for %s", resp.StatusCode, rawURL)
|
||||
}
|
||||
f, err := os.Create(dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = io.Copy(f, resp.Body)
|
||||
return err
|
||||
}
|
||||
|
||||
func fetchChecksums(url string) (map[string]string, error) {
|
||||
if err := validateURLScheme(url); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := httpsOnlyClient.Get(url) //nolint:gosec
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
|
||||
}
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make(map[string]string)
|
||||
for _, line := range strings.Split(string(body), "\n") {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) == 2 {
|
||||
result[parts[1]] = parts[0]
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func verifyChecksum(path, expected string) error {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return err
|
||||
}
|
||||
actual := hex.EncodeToString(h.Sum(nil))
|
||||
if actual != expected {
|
||||
return fmt.Errorf("checksum mismatch: expected %s, got %s", expected, actual)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractTarGz(archivePath, destDir, targetFile string) error {
|
||||
f, err := os.Open(archivePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
gz, err := gzip.NewReader(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer gz.Close()
|
||||
tr := tar.NewReader(gz)
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if filepath.Base(hdr.Name) == targetFile {
|
||||
out, err := os.Create(filepath.Join(destDir, targetFile))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
_, err = io.Copy(out, tr) //nolint:gosec
|
||||
return err
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("%s not found in archive", targetFile)
|
||||
}
|
||||
|
||||
func extractZip(archivePath, destDir, targetFile string) error {
|
||||
r, err := zip.OpenReader(archivePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
for _, f := range r.File {
|
||||
if filepath.Base(f.Name) == targetFile {
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rc.Close()
|
||||
out, err := os.Create(filepath.Join(destDir, targetFile))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
_, err = io.Copy(out, rc) //nolint:gosec
|
||||
return err
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("%s not found in archive", targetFile)
|
||||
}
|
||||
|
||||
func copyFile(src, dst string) error {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
_, err = io.Copy(out, in)
|
||||
return err
|
||||
}
|
||||
|
||||
func execBinary(bin string, args []string) error {
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd := exec.Command(bin, args...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
return syscall.Exec(bin, append([]string{bin}, args...), os.Environ())
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module github.com/DeusData/codebase-memory-mcp/pkg/go
|
||||
|
||||
go 1.26.1
|
||||
@@ -0,0 +1,49 @@
|
||||
class CodebaseMemoryMcp < Formula
|
||||
desc "Fast code intelligence engine for AI coding agents"
|
||||
homepage "https://github.com/DeusData/codebase-memory-mcp"
|
||||
version "0.8.1"
|
||||
license "MIT"
|
||||
|
||||
on_macos do
|
||||
on_arm do
|
||||
url "https://github.com/DeusData/codebase-memory-mcp/releases/download/v#{version}/codebase-memory-mcp-darwin-arm64.tar.gz"
|
||||
sha256 "fbd047509852021b5446a11141bcb0a3d1dcaebf6e5112460960f29f052c1c58"
|
||||
end
|
||||
on_intel do
|
||||
url "https://github.com/DeusData/codebase-memory-mcp/releases/download/v#{version}/codebase-memory-mcp-darwin-amd64.tar.gz"
|
||||
sha256 "fb62da3016ea12b948351208759b5c083fb1446cf6e78d6db8b7cd28fe86fd54"
|
||||
end
|
||||
end
|
||||
|
||||
on_linux do
|
||||
on_arm do
|
||||
url "https://github.com/DeusData/codebase-memory-mcp/releases/download/v#{version}/codebase-memory-mcp-linux-arm64.tar.gz"
|
||||
sha256 "d2f842d1365da5c35d9c5796f57a821c9745267350994346735e1e6e04d46091"
|
||||
end
|
||||
on_intel do
|
||||
url "https://github.com/DeusData/codebase-memory-mcp/releases/download/v#{version}/codebase-memory-mcp-linux-amd64.tar.gz"
|
||||
sha256 "dbd3b92ea870ef240b63059f26bda15015f76ef9978931bebc3a0f9d09470973"
|
||||
end
|
||||
end
|
||||
|
||||
def install
|
||||
bin.install "codebase-memory-mcp"
|
||||
# Third-party attribution bundle (present in archives since v0.8.1)
|
||||
doc.install "THIRD_PARTY_NOTICES.md" if File.exist?("THIRD_PARTY_NOTICES.md")
|
||||
end
|
||||
|
||||
def caveats
|
||||
<<~EOS
|
||||
Run the following to configure your coding agents:
|
||||
codebase-memory-mcp install
|
||||
|
||||
To tap this formula directly:
|
||||
brew tap deusdata/codebase-memory-mcp https://github.com/DeusData/codebase-memory-mcp
|
||||
brew install codebase-memory-mcp
|
||||
EOS
|
||||
end
|
||||
|
||||
test do
|
||||
assert_match "codebase-memory-mcp", shell_output("#{bin}/codebase-memory-mcp --version")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,90 @@
|
||||
# codebase-memory-mcp
|
||||
|
||||
[](https://www.npmjs.com/package/codebase-memory-mcp)
|
||||
[](https://github.com/DeusData/codebase-memory-mcp/releases/latest)
|
||||
[](https://github.com/DeusData/codebase-memory-mcp/blob/main/LICENSE)
|
||||
[](https://github.com/DeusData/codebase-memory-mcp/releases/latest)
|
||||
|
||||
**The fastest and most efficient code intelligence engine for AI coding agents.** Full-indexes an average repository in milliseconds, the Linux kernel (28M LOC, 75K files) in 3 minutes. Answers structural queries in under 1ms. Ships as a single static binary — this package downloads and runs it automatically.
|
||||
|
||||
High-quality parsing through [tree-sitter](https://tree-sitter.github.io/tree-sitter/) AST analysis across 159 languages — producing a persistent knowledge graph of functions, classes, call chains, HTTP routes, and cross-service links. 14 MCP tools. Zero dependencies. Plug and play across 11 coding agents.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install -g codebase-memory-mcp
|
||||
```
|
||||
|
||||
The binary for your platform is downloaded automatically at install time. Then configure your coding agents:
|
||||
|
||||
```bash
|
||||
codebase-memory-mcp install
|
||||
```
|
||||
|
||||
Restart your agent. Say **"Index this project"** — done.
|
||||
|
||||
## Why codebase-memory-mcp
|
||||
|
||||
- **Extreme indexing speed** — Linux kernel (28M LOC, 75K files) in 3 minutes. RAM-first pipeline with LZ4 compression and in-memory SQLite.
|
||||
- **Plug and play** — single static binary for macOS (arm64/amd64), Linux (arm64/amd64), and Windows (amd64). No Docker, no runtime dependencies, no API keys.
|
||||
- **159 languages** — vendored tree-sitter grammars compiled into the binary. Nothing to install, nothing that breaks.
|
||||
- **120x fewer tokens** — 5 structural queries: ~3,400 tokens vs ~412,000 via file-by-file search.
|
||||
- **11 agents, one command** — `install` auto-detects Claude Code, Codex CLI, Gemini CLI, Zed, OpenCode, Antigravity, Aider, KiloCode, VS Code, OpenClaw, and Kiro.
|
||||
- **14 MCP tools** — search, trace, architecture, impact analysis, Cypher queries, dead code detection, cross-service HTTP linking, ADR management, and more.
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
| OS | Architecture |
|
||||
|---------|-------------|
|
||||
| macOS | arm64, amd64 |
|
||||
| Linux | arm64, amd64 |
|
||||
| Windows | amd64 |
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
codebase-memory-mcp install # configure all detected coding agents
|
||||
codebase-memory-mcp --version
|
||||
codebase-memory-mcp --help
|
||||
codebase-memory-mcp update # update to latest release
|
||||
codebase-memory-mcp uninstall # remove agent configs
|
||||
```
|
||||
|
||||
### CLI Mode
|
||||
|
||||
Every MCP tool is also available directly from the command line:
|
||||
|
||||
```bash
|
||||
codebase-memory-mcp cli index_repository '{"repo_path": "/path/to/repo"}'
|
||||
codebase-memory-mcp cli search_graph '{"name_pattern": ".*Handler.*", "label": "Function"}'
|
||||
codebase-memory-mcp cli trace_call_path '{"function_name": "main", "direction": "both"}'
|
||||
codebase-memory-mcp cli get_architecture '{}'
|
||||
```
|
||||
|
||||
## MCP Tools
|
||||
|
||||
| Category | Tools |
|
||||
|----------|-------|
|
||||
| **Indexing** | `index_repository`, `list_projects`, `delete_project`, `index_status` |
|
||||
| **Querying** | `search_graph`, `trace_call_path`, `detect_changes`, `query_graph` |
|
||||
| **Analysis** | `get_architecture`, `get_graph_schema`, `get_code_snippet`, `search_code` |
|
||||
| **Advanced** | `manage_adr`, `ingest_traces` |
|
||||
|
||||
## Performance
|
||||
|
||||
Benchmarked on Apple M3 Pro:
|
||||
|
||||
| Operation | Time |
|
||||
|-----------|------|
|
||||
| Linux kernel full index (28M LOC, 75K files) | 3 min |
|
||||
| Django full index | ~6s |
|
||||
| Cypher query | <1ms |
|
||||
| Trace call path (depth=5) | <10ms |
|
||||
|
||||
## Full Documentation
|
||||
|
||||
See [github.com/DeusData/codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp) for the full README including all MCP tools, configuration options, graph data model, and language support details.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
// CLI shim: resolves the downloaded binary and replaces the current process with it.
|
||||
// If the binary is missing (e.g. --ignore-scripts), attempts a one-time download.
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { spawnSync } = require('child_process');
|
||||
|
||||
const isWindows = process.platform === 'win32';
|
||||
const binName = isWindows ? 'codebase-memory-mcp.exe' : 'codebase-memory-mcp';
|
||||
const binPath = path.join(__dirname, 'bin', binName);
|
||||
|
||||
if (!fs.existsSync(binPath)) {
|
||||
// Binary missing — try running the install script (handles --ignore-scripts case)
|
||||
process.stderr.write('codebase-memory-mcp: binary not found, downloading...\n');
|
||||
const installResult = spawnSync(process.execPath, [path.join(__dirname, 'install.js')], {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
if (installResult.status !== 0 || !fs.existsSync(binPath)) {
|
||||
process.stderr.write(
|
||||
'codebase-memory-mcp: download failed.\n' +
|
||||
'Try reinstalling: npm install -g codebase-memory-mcp\n'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const result = spawnSync(binPath, process.argv.slice(2), {
|
||||
stdio: 'inherit',
|
||||
windowsHide: false,
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
process.stderr.write(`codebase-memory-mcp: ${result.error.message}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
process.exit(result.status ?? 0);
|
||||
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
// Postinstall script: downloads the platform-appropriate binary from GitHub Releases.
|
||||
// Runs automatically via `postinstall` in package.json.
|
||||
|
||||
const https = require('https');
|
||||
const crypto = require('crypto');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
const REPO = 'DeusData/codebase-memory-mcp';
|
||||
const VERSION = require('./package.json').version;
|
||||
const BIN_DIR = path.join(__dirname, 'bin');
|
||||
|
||||
function getPlatform() {
|
||||
switch (process.platform) {
|
||||
case 'linux': return 'linux';
|
||||
case 'darwin': return 'darwin';
|
||||
case 'win32': return 'windows';
|
||||
default: throw new Error(`Unsupported platform: ${process.platform}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getArch() {
|
||||
switch (process.arch) {
|
||||
case 'arm64': return 'arm64';
|
||||
case 'x64': return 'amd64';
|
||||
default: throw new Error(`Unsupported architecture: ${process.arch}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Security: only follow HTTPS URLs (defense-in-depth).
|
||||
function validateUrl(url) {
|
||||
if (!url.startsWith('https://')) {
|
||||
throw new Error(`Refusing non-HTTPS URL: ${url}`);
|
||||
}
|
||||
}
|
||||
|
||||
function download(url, dest) {
|
||||
validateUrl(url);
|
||||
return new Promise((resolve, reject) => {
|
||||
function follow(u, depth) {
|
||||
if (depth > 5) return reject(new Error('Too many redirects'));
|
||||
validateUrl(u);
|
||||
https.get(u, (res) => {
|
||||
if (res.statusCode === 301 || res.statusCode === 302) {
|
||||
const loc = res.headers.location;
|
||||
if (!loc) return reject(new Error('Redirect with no location'));
|
||||
const next = loc.startsWith('/') ? new URL(loc, u).href : loc;
|
||||
return follow(next, depth + 1);
|
||||
}
|
||||
if (res.statusCode !== 200) {
|
||||
return reject(new Error(`HTTP ${res.statusCode} for ${u}`));
|
||||
}
|
||||
const file = fs.createWriteStream(dest);
|
||||
res.pipe(file);
|
||||
file.on('finish', () => file.close(resolve));
|
||||
file.on('error', reject);
|
||||
}).on('error', reject);
|
||||
}
|
||||
follow(url, 0);
|
||||
});
|
||||
}
|
||||
|
||||
// Fetch checksums.txt and verify the archive hash.
|
||||
async function verifyChecksum(archivePath, archiveName) {
|
||||
const url = `https://github.com/${REPO}/releases/download/v${VERSION}/checksums.txt`;
|
||||
const tmpChecksums = archivePath + '.checksums';
|
||||
try {
|
||||
await download(url, tmpChecksums);
|
||||
const lines = fs.readFileSync(tmpChecksums, 'utf-8').split('\n');
|
||||
const match = lines.find((l) => l.includes(archiveName));
|
||||
if (!match) return; // checksum line not found — non-fatal
|
||||
const expected = match.split(/\s+/)[0];
|
||||
const actual = crypto
|
||||
.createHash('sha256')
|
||||
.update(fs.readFileSync(archivePath))
|
||||
.digest('hex');
|
||||
if (expected !== actual) {
|
||||
throw new Error(
|
||||
`Checksum mismatch for ${archiveName}:\n expected: ${expected}\n actual: ${actual}`,
|
||||
);
|
||||
}
|
||||
process.stdout.write('codebase-memory-mcp: checksum verified.\n');
|
||||
} catch (err) {
|
||||
if (err.message.startsWith('Checksum mismatch')) throw err;
|
||||
// Non-fatal: checksum unavailable (network issue, pre-release, etc.)
|
||||
} finally {
|
||||
try { fs.unlinkSync(tmpChecksums); } catch (_) { /* ignore */ }
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const platform = getPlatform();
|
||||
const arch = getArch();
|
||||
const ext = platform === 'windows' ? 'zip' : 'tar.gz';
|
||||
const binName = platform === 'windows' ? 'codebase-memory-mcp.exe' : 'codebase-memory-mcp';
|
||||
const binPath = path.join(BIN_DIR, binName);
|
||||
|
||||
if (fs.existsSync(binPath)) {
|
||||
return; // already installed, nothing to do
|
||||
}
|
||||
|
||||
fs.mkdirSync(BIN_DIR, { recursive: true });
|
||||
|
||||
// Linux ships a fully-static "-portable" build; the standard linux binary
|
||||
// dynamically links glibc 2.38+ and fails on older distros. macOS/Windows
|
||||
// have no such variant. Keep in sync with install.sh / pypi _cli.py / cli.c.
|
||||
const variant = platform === 'linux' ? '-portable' : '';
|
||||
// Opt into the UI build (embedded graph visualization) with CBM_VARIANT=ui.
|
||||
// Default is the standard (headless) build. Mirrors install.sh --ui.
|
||||
const ui = (process.env.CBM_VARIANT || '').toLowerCase() === 'ui' ? 'ui-' : '';
|
||||
const archive = `codebase-memory-mcp-${ui}${platform}-${arch}${variant}.${ext}`;
|
||||
const url = `https://github.com/${REPO}/releases/download/v${VERSION}/${archive}`;
|
||||
|
||||
const uiLabel = ui ? '(ui) ' : '';
|
||||
process.stdout.write(`codebase-memory-mcp: downloading v${VERSION} ${uiLabel}for ${platform}/${arch}...\n`);
|
||||
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cbm-install-'));
|
||||
const tmpArchive = path.join(tmpDir, `cbm.${ext}`);
|
||||
|
||||
try {
|
||||
await download(url, tmpArchive);
|
||||
await verifyChecksum(tmpArchive, archive);
|
||||
|
||||
// Extract using execFileSync (array args — no shell injection).
|
||||
if (ext === 'tar.gz') {
|
||||
execFileSync('tar', ['-xzf', tmpArchive, '-C', tmpDir, '--no-same-owner']);
|
||||
} else {
|
||||
execFileSync('powershell', [
|
||||
'-NoProfile', '-Command',
|
||||
`Expand-Archive -Path '${tmpArchive}' -DestinationPath '${tmpDir}' -Force`,
|
||||
]);
|
||||
}
|
||||
|
||||
// Validate extracted path doesn't escape tmpDir (tar-slip defense).
|
||||
const extracted = path.join(tmpDir, binName);
|
||||
const resolvedExtracted = path.resolve(extracted);
|
||||
const resolvedTmpDir = path.resolve(tmpDir);
|
||||
if (!resolvedExtracted.startsWith(resolvedTmpDir + path.sep)) {
|
||||
throw new Error(`Path traversal detected in archive: ${binName}`);
|
||||
}
|
||||
if (!fs.existsSync(extracted)) {
|
||||
throw new Error(`Binary not found after extraction at ${extracted}`);
|
||||
}
|
||||
|
||||
fs.copyFileSync(extracted, binPath);
|
||||
fs.chmodSync(binPath, 0o755);
|
||||
|
||||
process.stdout.write('codebase-memory-mcp: ready.\n');
|
||||
} finally {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
process.stderr.write(`\ncodebase-memory-mcp: install failed — ${err.message}\n`);
|
||||
process.stderr.write(`You can install manually: https://github.com/${REPO}#installation\n`);
|
||||
// Non-fatal: don't block the rest of npm install
|
||||
process.exit(0);
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "codebase-memory-mcp",
|
||||
"version": "0.8.1",
|
||||
"description": "Fast code intelligence engine for AI coding agents — single static binary MCP server",
|
||||
"mcpName": "io.github.DeusData/codebase-memory-mcp",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/DeusData/codebase-memory-mcp.git"
|
||||
},
|
||||
"homepage": "https://github.com/DeusData/codebase-memory-mcp",
|
||||
"bugs": {
|
||||
"url": "https://github.com/DeusData/codebase-memory-mcp/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"mcp",
|
||||
"claude",
|
||||
"code-intelligence",
|
||||
"codebase",
|
||||
"memory",
|
||||
"ai",
|
||||
"llm",
|
||||
"tree-sitter"
|
||||
],
|
||||
"bin": {
|
||||
"codebase-memory-mcp": "./bin.js"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "node install.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"os": ["linux", "darwin", "win32"],
|
||||
"cpu": ["x64", "arm64"],
|
||||
"files": [
|
||||
"bin.js",
|
||||
"install.js",
|
||||
"README.md"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
# codebase-memory-mcp
|
||||
|
||||
mcp-name: io.github.DeusData/codebase-memory-mcp
|
||||
|
||||
**Fast code intelligence engine for AI coding agents.** Indexes an average repository in milliseconds, the Linux kernel (28M LOC) in 3 minutes. Answers structural queries in under 1ms.
|
||||
|
||||
This package installs the `codebase-memory-mcp` binary from [GitHub Releases](https://github.com/DeusData/codebase-memory-mcp/releases). The binary is downloaded on first run and cached in your OS cache directory.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install codebase-memory-mcp
|
||||
# or
|
||||
pipx install codebase-memory-mcp
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
codebase-memory-mcp install # configure your coding agents
|
||||
codebase-memory-mcp --help
|
||||
```
|
||||
|
||||
## Supported platforms
|
||||
|
||||
| OS | Architecture |
|
||||
|---------|-------------|
|
||||
| macOS | arm64, amd64 |
|
||||
| Linux | arm64, amd64 |
|
||||
| Windows | amd64 |
|
||||
|
||||
## Full documentation
|
||||
|
||||
See [github.com/DeusData/codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp)
|
||||
@@ -0,0 +1,35 @@
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "codebase-memory-mcp"
|
||||
version = "0.8.1"
|
||||
description = "Fast code intelligence engine for AI coding agents — single static binary MCP server"
|
||||
readme = "README.md"
|
||||
license = { text = "MIT" }
|
||||
requires-python = ">=3.8"
|
||||
keywords = ["mcp", "claude", "code-intelligence", "codebase", "memory", "ai", "llm"]
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Console",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: MacOS",
|
||||
"Operating System :: POSIX :: Linux",
|
||||
"Operating System :: Microsoft :: Windows",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: Software Development",
|
||||
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/DeusData/codebase-memory-mcp"
|
||||
Repository = "https://github.com/DeusData/codebase-memory-mcp"
|
||||
Issues = "https://github.com/DeusData/codebase-memory-mcp/issues"
|
||||
|
||||
[project.scripts]
|
||||
codebase-memory-mcp = "codebase_memory_mcp:main"
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/codebase_memory_mcp"]
|
||||
@@ -0,0 +1,17 @@
|
||||
"""
|
||||
codebase-memory-mcp — Fast code intelligence engine for AI coding agents.
|
||||
Downloads and runs the codebase-memory-mcp binary from GitHub Releases.
|
||||
"""
|
||||
|
||||
try:
|
||||
from importlib.metadata import version, PackageNotFoundError
|
||||
try:
|
||||
__version__ = version("codebase-memory-mcp")
|
||||
except PackageNotFoundError:
|
||||
__version__ = "unknown"
|
||||
except ImportError:
|
||||
__version__ = "unknown"
|
||||
|
||||
from ._cli import main
|
||||
|
||||
__all__ = ["main", "__version__"]
|
||||
@@ -0,0 +1,4 @@
|
||||
from ._cli import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,227 @@
|
||||
"""Downloads the codebase-memory-mcp binary on first run, then exec's it."""
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
import stat
|
||||
import shutil
|
||||
import tempfile
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
from pathlib import Path
|
||||
|
||||
REPO = "DeusData/codebase-memory-mcp"
|
||||
|
||||
# Security: only permit https fetches. urllib's default handlers accept
|
||||
# file://, ftp://, and custom schemes — a redirect or tainted URL source
|
||||
# could otherwise turn a download into an arbitrary-local-file read.
|
||||
_ALLOWED_SCHEMES = frozenset({"https"})
|
||||
|
||||
|
||||
def _validate_url_scheme(url: str) -> None:
|
||||
"""Reject non-https URLs before any network fetch."""
|
||||
scheme = urllib.parse.urlparse(url).scheme
|
||||
if scheme not in _ALLOWED_SCHEMES:
|
||||
sys.exit(
|
||||
f"codebase-memory-mcp: refusing to fetch non-https URL "
|
||||
f"(scheme={scheme!r}): {url}"
|
||||
)
|
||||
|
||||
|
||||
def _safe_extract_tar(tf, dest: str) -> None:
|
||||
"""Extract a tarfile to dest, rejecting path-traversal entries.
|
||||
|
||||
Uses the tarfile data filter on Python >=3.12 (PEP 706), falls back to
|
||||
manual per-member path validation on older Pythons. Mitigates the
|
||||
classic tar-slip / Zip Slip vulnerability (CWE-22).
|
||||
"""
|
||||
if hasattr(tf, "extraction_filter") or sys.version_info >= (3, 12):
|
||||
tf.extractall(dest, filter="data")
|
||||
return
|
||||
|
||||
dest_abs = os.path.abspath(dest)
|
||||
for member in tf.getmembers():
|
||||
if member.issym() or member.islnk():
|
||||
sys.exit(
|
||||
f"codebase-memory-mcp: refusing unsafe tar entry "
|
||||
f"(link: {member.name!r})"
|
||||
)
|
||||
member_abs = os.path.abspath(os.path.join(dest_abs, member.name))
|
||||
if not (member_abs == dest_abs or member_abs.startswith(dest_abs + os.sep)):
|
||||
sys.exit(
|
||||
f"codebase-memory-mcp: refusing unsafe tar entry "
|
||||
f"(escapes dest: {member.name!r})"
|
||||
)
|
||||
tf.extractall(dest)
|
||||
|
||||
|
||||
def _safe_extract_zip(zf, dest: str) -> None:
|
||||
"""Extract a zipfile to dest, rejecting path-traversal entries."""
|
||||
dest_abs = os.path.abspath(dest)
|
||||
for name in zf.namelist():
|
||||
member_abs = os.path.abspath(os.path.join(dest_abs, name))
|
||||
if not (member_abs == dest_abs or member_abs.startswith(dest_abs + os.sep)):
|
||||
sys.exit(
|
||||
f"codebase-memory-mcp: refusing unsafe zip entry "
|
||||
f"(escapes dest: {name!r})"
|
||||
)
|
||||
zf.extractall(dest)
|
||||
|
||||
|
||||
def _verify_checksum(archive_path: str, archive_name: str, version: str) -> None:
|
||||
"""Verify SHA256 checksum against checksums.txt from the release."""
|
||||
url = f"https://github.com/{REPO}/releases/download/v{version}/checksums.txt"
|
||||
try:
|
||||
_validate_url_scheme(url)
|
||||
with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as tmp:
|
||||
tmp_path = tmp.name
|
||||
urllib.request.urlretrieve(url, tmp_path) # noqa: S310 — scheme validated above
|
||||
with open(tmp_path) as f:
|
||||
for line in f:
|
||||
if archive_name in line:
|
||||
expected = line.split()[0]
|
||||
h = hashlib.sha256()
|
||||
with open(archive_path, "rb") as af:
|
||||
for chunk in iter(lambda: af.read(65536), b""):
|
||||
h.update(chunk)
|
||||
actual = h.hexdigest()
|
||||
if expected != actual:
|
||||
sys.exit(
|
||||
f"codebase-memory-mcp: CHECKSUM MISMATCH for {archive_name}\n"
|
||||
f" expected: {expected}\n"
|
||||
f" actual: {actual}"
|
||||
)
|
||||
print("codebase-memory-mcp: checksum verified.", file=sys.stderr)
|
||||
break
|
||||
except SystemExit:
|
||||
raise
|
||||
except Exception:
|
||||
pass # Non-fatal: checksum unavailable
|
||||
finally:
|
||||
try:
|
||||
os.unlink(tmp_path)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _version() -> str:
|
||||
try:
|
||||
from importlib.metadata import version
|
||||
return version("codebase-memory-mcp")
|
||||
except Exception:
|
||||
return "0.8.1"
|
||||
|
||||
|
||||
def _os_name() -> str:
|
||||
p = sys.platform
|
||||
if p == "linux":
|
||||
return "linux"
|
||||
if p == "darwin":
|
||||
return "darwin"
|
||||
if p == "win32":
|
||||
return "windows"
|
||||
sys.exit(f"codebase-memory-mcp: unsupported platform: {p}")
|
||||
|
||||
|
||||
def _arch() -> str:
|
||||
m = platform.machine().lower()
|
||||
if m in ("arm64", "aarch64"):
|
||||
return "arm64"
|
||||
if m in ("x86_64", "amd64"):
|
||||
return "amd64"
|
||||
sys.exit(f"codebase-memory-mcp: unsupported architecture: {m}")
|
||||
|
||||
|
||||
def _cache_dir() -> Path:
|
||||
if sys.platform == "win32":
|
||||
base = Path(os.environ.get("LOCALAPPDATA", Path.home() / "AppData" / "Local"))
|
||||
elif sys.platform == "darwin":
|
||||
base = Path.home() / "Library" / "Caches"
|
||||
else:
|
||||
base = Path(os.environ.get("XDG_CACHE_HOME", Path.home() / ".cache"))
|
||||
return base / "codebase-memory-mcp"
|
||||
|
||||
|
||||
def _bin_path(version: str) -> Path:
|
||||
name = "codebase-memory-mcp.exe" if sys.platform == "win32" else "codebase-memory-mcp"
|
||||
return _cache_dir() / version / name
|
||||
|
||||
|
||||
def _download(version: str) -> Path:
|
||||
os_name = _os_name()
|
||||
arch = _arch()
|
||||
ext = "zip" if os_name == "windows" else "tar.gz"
|
||||
# Linux ships a fully-static "-portable" build; the standard linux binary
|
||||
# dynamically links glibc 2.38+ and fails on older distros. macOS/Windows
|
||||
# have no such variant. Keep in sync with install.sh / install.js / cli.c.
|
||||
variant = "-portable" if os_name == "linux" else ""
|
||||
# Opt into the UI build (embedded graph visualization) with CBM_VARIANT=ui.
|
||||
# Default is the standard (headless) build. Mirrors install.sh --ui.
|
||||
ui = "ui-" if os.environ.get("CBM_VARIANT", "").lower() == "ui" else ""
|
||||
archive = f"codebase-memory-mcp-{ui}{os_name}-{arch}{variant}.{ext}"
|
||||
url = f"https://github.com/{REPO}/releases/download/v{version}/{archive}"
|
||||
_validate_url_scheme(url)
|
||||
|
||||
dest = _bin_path(version)
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
print(
|
||||
f"codebase-memory-mcp: downloading v{version} for {os_name}/{arch}...",
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
tmp_archive = os.path.join(tmp, f"cbm.{ext}")
|
||||
try:
|
||||
urllib.request.urlretrieve(url, tmp_archive) # noqa: S310 — scheme validated above
|
||||
except urllib.error.HTTPError as e:
|
||||
sys.exit(
|
||||
f"codebase-memory-mcp: download failed ({e})\n"
|
||||
f"URL: {url}\n"
|
||||
f"See https://github.com/{REPO}/releases for available versions."
|
||||
)
|
||||
|
||||
_verify_checksum(tmp_archive, archive, version)
|
||||
|
||||
if ext == "tar.gz":
|
||||
import tarfile
|
||||
with tarfile.open(tmp_archive) as tf:
|
||||
_safe_extract_tar(tf, tmp)
|
||||
else:
|
||||
import zipfile
|
||||
with zipfile.ZipFile(tmp_archive) as zf:
|
||||
_safe_extract_zip(zf, tmp)
|
||||
|
||||
bin_name = "codebase-memory-mcp.exe" if os_name == "windows" else "codebase-memory-mcp"
|
||||
extracted = os.path.join(tmp, bin_name)
|
||||
if not os.path.exists(extracted):
|
||||
sys.exit("codebase-memory-mcp: binary not found after extraction")
|
||||
|
||||
shutil.copy2(extracted, dest)
|
||||
current = dest.stat().st_mode
|
||||
dest.chmod(current | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
||||
|
||||
return dest
|
||||
|
||||
|
||||
def main() -> None:
|
||||
version = _version()
|
||||
bin_path = _bin_path(version)
|
||||
|
||||
if not bin_path.exists():
|
||||
bin_path = _download(version)
|
||||
|
||||
# args is a list (not a shell string), so exec/subprocess treat each
|
||||
# element as a discrete argv entry — no shell interpretation, no
|
||||
# injection vector. sys.argv forwarding is the whole point of this
|
||||
# shim, so tainted-input suppression is intentional.
|
||||
args = [str(bin_path)] + sys.argv[1:]
|
||||
|
||||
if sys.platform != "win32":
|
||||
os.execv(str(bin_path), args) # noqa: S606 — list form, no shell
|
||||
else:
|
||||
import subprocess
|
||||
result = subprocess.run(args) # noqa: S603 — list form, no shell=True
|
||||
sys.exit(result.returncode)
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"version": "0.8.1",
|
||||
"description": "Fast code intelligence engine for AI coding agents — single static binary MCP server",
|
||||
"homepage": "https://github.com/DeusData/codebase-memory-mcp",
|
||||
"license": "MIT",
|
||||
"architecture": {
|
||||
"64bit": {
|
||||
"url": "https://github.com/DeusData/codebase-memory-mcp/releases/download/v0.8.1/codebase-memory-mcp-windows-amd64.zip",
|
||||
"hash": "a602ad090ed3f49d86c55472f73f27ad7055222806a82358f2e08513e027f00f",
|
||||
"bin": "codebase-memory-mcp.exe"
|
||||
}
|
||||
},
|
||||
"post_install": [
|
||||
"& \"$dir\\codebase-memory-mcp.exe\" install -y 2>&1 | Out-Null"
|
||||
],
|
||||
"checkver": {
|
||||
"github": "https://github.com/DeusData/codebase-memory-mcp"
|
||||
},
|
||||
"autoupdate": {
|
||||
"architecture": {
|
||||
"64bit": {
|
||||
"url": "https://github.com/DeusData/codebase-memory-mcp/releases/download/v$version/codebase-memory-mcp-windows-amd64.zip",
|
||||
"hash": {
|
||||
"url": "https://github.com/DeusData/codebase-memory-mcp/releases/download/v$version/checksums.txt",
|
||||
"find": "([a-f0-9]{64})\\s+codebase-memory-mcp-windows-amd64\\.zip"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
PackageIdentifier: DeusData.CodebaseMemoryMcp
|
||||
PackageVersion: 0.8.1
|
||||
InstallerLocale: en-US
|
||||
InstallerType: zip
|
||||
NestedInstallerType: portable
|
||||
NestedInstallerFiles:
|
||||
- RelativeFilePath: codebase-memory-mcp.exe
|
||||
PortableCommandAlias: codebase-memory-mcp
|
||||
Platform:
|
||||
- Windows.Desktop
|
||||
- Windows.Universal
|
||||
Installers:
|
||||
- Architecture: x64
|
||||
InstallerUrl: https://github.com/DeusData/codebase-memory-mcp/releases/download/v0.8.1/codebase-memory-mcp-windows-amd64.zip
|
||||
InstallerSha256: a602ad090ed3f49d86c55472f73f27ad7055222806a82358f2e08513e027f00f
|
||||
ManifestType: installer
|
||||
ManifestVersion: 1.6.0
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
PackageIdentifier: DeusData.CodebaseMemoryMcp
|
||||
PackageVersion: 0.8.1
|
||||
PackageLocale: en-US
|
||||
Publisher: DeusData
|
||||
PublisherUrl: https://github.com/DeusData
|
||||
PublisherSupportUrl: https://github.com/DeusData/codebase-memory-mcp/issues
|
||||
PackageName: codebase-memory-mcp
|
||||
PackageUrl: https://github.com/DeusData/codebase-memory-mcp
|
||||
License: MIT
|
||||
LicenseUrl: https://github.com/DeusData/codebase-memory-mcp/blob/main/LICENSE
|
||||
ShortDescription: Fast code intelligence engine for AI coding agents
|
||||
Description: |-
|
||||
codebase-memory-mcp is a single static binary MCP server that indexes codebases
|
||||
at extreme speed and exposes 14 MCP tools for AI coding agents. Full indexes an
|
||||
average repository in milliseconds, the Linux kernel (28M LOC) in 3 minutes.
|
||||
Supports 155 languages via vendored tree-sitter grammars. Zero dependencies.
|
||||
Plug and play across 11 coding agents including Claude Code, Codex CLI, and Zed.
|
||||
Moniker: codebase-memory-mcp
|
||||
Tags:
|
||||
- mcp
|
||||
- ai
|
||||
- claude
|
||||
- code-intelligence
|
||||
- codebase
|
||||
- developer-tools
|
||||
- tree-sitter
|
||||
ReleaseNotesUrl: https://github.com/DeusData/codebase-memory-mcp/releases/tag/v0.8.1
|
||||
ManifestType: defaultLocale
|
||||
ManifestVersion: 1.6.0
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
PackageIdentifier: DeusData.CodebaseMemoryMcp
|
||||
PackageVersion: 0.8.1
|
||||
DefaultLocale: en-US
|
||||
ManifestType: version
|
||||
ManifestVersion: 1.6.0
|
||||
Reference in New Issue
Block a user