Repository navigation
Description
codebase-memory-mcp Codebase Analysis & Improvements Report
This report outlines 5 high-impact bugs, build failures, and architectural issues identified in the DeusData/codebase-memory-mcp repository, along with their root causes and suggested code fixes.
1. Windows stdio MCP Handshake Hangs & Early Exits
Important
Impact: Prevents the MCP server from running in stdio mode on Windows when spawned as a subprocess by clients like Cursor or Claude Code.
Root Cause
In mcp.c, the event loop polls stdin for new data to manage idle timeouts. On Windows, it tries to wait directly on the stdin file descriptor handle:
#ifdef _WIN32
/* Windows: WaitForSingleObject on stdin handle */
HANDLE hStdin = (HANDLE)_get_osfhandle(fd);
DWORD wr = WaitForSingleObject(hStdin, STORE_IDLE_TIMEOUT_S * MCP_TIMEOUT_MS);
if (wr == WAIT_FAILED) {
break;
}Windows anonymous pipes (which redirect standard inputs for subprocesses) do not support synchronization and cannot be used with wait functions. Thus, WaitForSingleObject fails immediately and returns WAIT_FAILED, causing the server loop to break and terminate the process during handshake.
Additionally, stdout is block-buffered on Windows when redirected to a pipe. Although fflush is called in the loop, if the process crashes or hangs on a blocked read, standard outputs remain stuck in the OS-level stream buffer.
Suggested Fix
- Disable
WaitForSingleObjecton pipe handles or usePeekNamedPipeto non-blockingly check if input is available:#ifdef _WIN32 HANDLE hStdin = (HANDLE)_get_osfhandle(fd); DWORD type = GetFileType(hStdin); if (type == FILE_TYPE_PIPE) { // Bypass WaitForSingleObject for anonymous pipes to prevent WAIT_FAILED DWORD bytes_avail = 0; if (PeekNamedPipe(hStdin, NULL, 0, NULL, &bytes_avail, NULL)) { if (bytes_avail == 0) { cbm_usleep(10000); // Back off 10ms to prevent CPU pinning continue; } } } else { DWORD wr = WaitForSingleObject(hStdin, STORE_IDLE_TIMEOUT_S * MCP_TIMEOUT_MS); if (wr == WAIT_FAILED) break; if (wr == WAIT_TIMEOUT) { cbm_mcp_server_evict_idle(srv, STORE_IDLE_TIMEOUT_S); continue; } } #endif
- Disable buffering on stdout at server startup in main.c:
setvbuf(stdout, NULL, _IONBF, 0);
2. Cascading .gitignore Files Ignored in Subdirectories
Warning
Impact: Subdirectory indexing ignores .gitignore files nested inside subfolders, causing build/dist folders or other excluded patterns to be scanned.
Root Cause
In discover.c, try_load_nested_gitignore checks:
static cbm_gitignore_t *try_load_nested_gitignore(const walk_frame_t *frame) {
if (frame->local_gi || frame->prefix[0] == '\0') {
return NULL;
}
...
}If a parent directory has a .gitignore loaded, frame->local_gi is set. When entering subdirectories, try_load_nested_gitignore exits early, meaning nested .gitignore files deeper in the tree are never loaded.
Suggested Fix
Allow loading nested .gitignore files even if a parent .gitignore was loaded. If a nested .gitignore is found in the current directory, merge its rules with the parent or stack them in a chain of active ignore filters.
3. get_code_snippet Hangs on Non-UTF-8 Encoded Files
Important
Impact: MCP clients (e.g. Node-based clients) crash or hang when requesting snippets from files using non-UTF-8 encodings (such as EUC-KR / CP949 or Shift-JIS).
Root Cause
The read_file_lines function reads raw bytes from files. When a non-UTF-8 file is read, it inserts invalid UTF-8 bytes directly into the JSON response structure.
yy_doc_to_str serializes the document using YYJSON_WRITE_ALLOW_INVALID_UNICODE which allows invalid bytes to be written straight to standard output. When the receiving MCP client (which expects strictly valid UTF-8 JSON) parses the stdout stream, it chokes on the invalid bytes and hangs.
Suggested Fix
Sanitize the file source content in build_snippet_response before adding it to the JSON response. Validate the UTF-8 sequences and replace any invalid bytes with the Unicode replacement character (\uFFFD or ?) to guarantee the JSON string output is always valid UTF-8.
4. libgit2 1.8+ Compilation Failure
Tip
Impact: Compilation fails when building with newer libgit2 versions (v1.8.0+).
Root Cause
The git_allocator struct was moved from <git2.h> to <git2/sys/alloc.h> in libgit2 1.8+. Because cbm.c only includes <git2.h>, compiling with libgit2 1.8+ throws an incomplete type error for git_allocator.
Suggested Fix
Include <git2/sys/alloc.h> conditionally in cbm.c:
#if defined(HAVE_LIBGIT2)
#include <git2.h>
+#include <git2/sys/alloc.h> // For git_allocator on libgit2 1.8+
#endif5. Directory Discovery Ignores .git/info/exclude
Note
Impact: Local-only ignore rules configured in .git/info/exclude are not respected, leading to unwanted local files being indexed.
Root Cause
The discovery process in src/discover/discover.c searches for .gitignore and .cbmignore files but never attempts to load .git/info/exclude.
Suggested Fix
During setup, check for the presence of .git/info/exclude in the repository root and load it as a base gitignore filter alongside .gitignore.
Activity
tarun1790 commentedon Jun 20, 2026
codebase-memory-mcp Codebase Analysis & Improvements Report (Batch 2)
This comment outlines two additional bugs/improvements identified in the codebase, along with their root causes and suggested code-level fixes (complete with code diffs).
1. ADR Data Loss on Index Rebuilds / Incremental runs
Important
Impact: Users silently lose all their stored Architecture Decision Records (ADRs) whenever a full re-indexing or an incremental indexing pass runs.
Root Cause
Both full re-indexing (in
src/pipeline/pipeline.c) and incremental indexing (insrc/pipeline/pipeline_incremental.c) delete the existing SQLite database usingcbm_unlink(db_path)and then write the new database from scratch.Because the custom page-based
sqlite_writeronly serializes the graph buffer nodes/edges/hashes, theproject_summariestable (which stores ADRs) is recreated empty. Consequently, the user's stored ADRs are wiped out.Suggested Fix
Temporarily back up the ADR metadata (the content,
created_at, andupdated_attimestamps) in memory before the SQLite file is unlinked, and restore it using a newcbm_store_adr_restorefunction right after the new SQLite database is generated. This preserves both the ADR content and the original creation/update timestamps.Code Diff
2. Cross-Service Edge Traversal Ignored in
trace_pathWarning
Impact: The
trace_pathtool withmode=cross_servicefails to navigate across repositories because it completely ignores allCROSS_*edges.Root Cause
In
src/mcp/mcp.c, themode_cross_svcarray is hardcoded to:This ignores the
CROSS_prefix and does not cover other cross-service protocols (likeCROSS_GRPC_CALLS,CROSS_GRAPHQL_CALLS,CROSS_TRPC_CALLS,CROSS_CHANNEL).Suggested Fix
Update the
mode_cross_svcarray to include all actualCROSS_*edge type names generated by the pipeline, and adjust the defaults count from 4 to 10.Code Diff
DeusData commentedon Jun 20, 2026
Hey thanks for highlighting. Will check everything beginning of next week.
tarun1790 commentedon Jun 20, 2026
codebase-memory-mcp Codebase Analysis & Improvements Report (Batch 3)
This comment outlines the root cause and complete code-level fix (including a clean code diff) for the cascading gitignore bug.
1. Cascading
.gitignoreFiles Ignored in SubdirectoriesWarning
Impact: Nested
.gitignorefiles inside subfolders (e.g.sub/nested/.gitignore) are completely ignored if any parent directory already has a.gitignoreloaded. This causes build/dist folders or other excluded patterns nested inside subfolders to be scanned during indexing.Root Cause
In
src/discover/discover.c, when walking the directory structure, the recursive framework uses a stack of frames (walk_frame_t).Each frame has a
local_gipointer and alocal_gi_prefixstring.When entering a subdirectory, the parent's
local_giandlocal_gi_prefixare inherited by the child frame.However, the
try_load_nested_gitignorehelper checks:If
frame->local_giis already non-NULL (because a parent.gitignorewas loaded and inherited), the function returns early and does not look for nested.gitignorefiles in the current folder.Suggested Fix
cbm_local_gi_node_tto chain active gitignores and their relative prefixes.walk_frame_tto point to the head of this chain (local_gi_chain).stack[*top].local_gi_chain = parent->local_gi_chain.try_load_nested_gitignore, check for a.gitignorein the current frame directory regardless of whether parent gitignores are already active.walk_dirto prevent leaks.should_skip_directoryandshould_skip_file.Code Diff
DeusData commentedon Jun 20, 2026
Hey, thanks for the great analysis. Will check everything ASAP 😊
DeusData commentedon Jun 23, 2026
For tracking: this report bundles three separate items that each also have focused issues / PRs —
WaitForSingleObject-on-anonymous-pipe root cause you describe)..gitignoreoverlaps .gitignore (non repo root) gaps and overrides #510 (PR fix(discover): honor root .gitignore when indexing subfolders #517 honors the root.gitignorewhen indexing subfolders).\u00XX).Labeling
bug/windows. Going forward, atomic one-bug-per-issue reports are easier to review, bisect, and revert — but thank you for the thorough analysis here; it's genuinely useful.test(windows): add red regressions for hook augmenter and UI drive pi…
test(windows): add red regressions for hook augmenter and UI drive pi…
github-actions commentedon Jul 8, 2026
Thanks for the report! To move this forward we need a bit more so we can reproduce it ourselves:
codebase-memory-mcp --versionyou're onOnce that's here we'll pick it straight back up. Heads-up: issues left
awaiting-reporterare automatically closed after a few weeks of silence, but a comment reopens the door anytime.DeusData commentedon Jul 8, 2026
v0.9.0 directly includes Windows support, ignore handling, and valid UTF-8 snippet fixes that overlap this report. Could you retest on v0.9.0 and confirm which parts, if any, still reproduce? Marking awaiting-reporter.
fix(discover): stack every nested .gitignore on a path, not just the …
fix(discover): stack every nested .gitignore on a path, not just the …
DeusData commentedon Oct 25, 2026
Thank you for documenting the distinct Windows startup, ignore, encoding, ADR, and cross-service symptoms. Following up on the earlier retest request: if any still occurs on your current version, please identify that specific symptom and version. We should track the remaining cases separately rather than treat the whole bundle as fixed.