How Dead Code Detection Works in Codebase-Memory-MCP: Analyzing the Dependency Graph
Codebase-Memory-MCP identifies dead code by querying inbound CALLS and USAGE edges in its SQLite-backed dependency graph, marking functions as dead only when they have zero incoming calls and zero usage references while explicitly excluding entry points, exported symbols, and test files.
Codebase-Memory-MCP (MCP) constructs a complete dependency graph of your codebase, persisting nodes for functions, methods, and classes alongside their relationships in an SQLite store. The dead code detection system operates during the 3D layout phase in src/ui/layout3d.c, batch-querying the graph to identify functions that are never invoked or referenced by any other part of the system. This analysis reveals how MCP distinguishes between truly dead code and intentionally exposed entry points through a strict priority-based classification matrix.
The Dead Code Classification Matrix
MCP applies a hierarchical decision tree when labeling nodes, checking conditions in strict priority order to prevent false positives:
| Condition | Status Assigned |
|---|---|
| Not a function or method | structural |
Test file (is_test flag or path heuristic) |
test |
Entry point (is_entry_point) or route (route_path) |
entry |
Exported symbol (is_exported) |
exported |
Zero inbound CALLS and zero inbound USAGE |
dead |
Exactly one inbound CALL |
single |
| All other functions/methods | normal |
This matrix ensures that public APIs and application entry points are never mistakenly flagged as dead code, even if they have low internal usage within the analyzed subgraph.
Implementation in src/ui/layout3d.c
The classification logic resides in the layout phase where the UI prepares the graph for rendering. The process executes in five distinct steps:
1. Collect Node Identifiers
First, the system aggregates node IDs from the current search result set:
int64_t *node_ids = malloc((size_t)n * sizeof(int64_t));
for (int i = 0; i < n; i++) {
node_ids[i] = search_out.results[i].node.id;
}
2. Batch Query Inbound Degrees
To avoid SQLite parameter limits, MCP queries degrees in configurable chunks using DEAD_DEGREE_CHUNK:
int *in_calls = calloc((size_t)n, sizeof(int));
int *in_usage = calloc((size_t)n, sizeof(int));
for (int off = 0; off < n; off += DEAD_DEGREE_CHUNK) {
int cnt = (n - off < DEAD_DEGREE_CHUNK) ? (n - off) : DEAD_DEGREE_CHUNK;
cbm_store_batch_count_degrees(store,
node_ids + off, cnt, "CALLS", in_calls + off, deg_dummy + off);
cbm_store_batch_count_degrees(store,
node_ids + off, cnt, "USAGE", in_usage + off, deg_dummy + off);
}
3. Parse Node Metadata Flags
The system extracts semantic metadata from the JSON properties_json field to identify special categories:
node_flags_t nf = parse_node_flags(sn->properties_json);
4. Apply Decision Logic
The core classification compares inbound call counts (ic) against usage counts (iu):
bool is_fn = sn->label && (strcmp(sn->label, "Function") == 0 ||
strcmp(sn->label, "Method") == 0);
int ic = in_calls ? in_calls[i] : 1; // inbound CALLS count
int iu = in_usage ? in_usage[i] : 1; // inbound USAGE count
const char *status;
if (!is_fn) status = "structural";
else if (testish) status = "test";
else if (nf.is_entry || nf.is_route) status = "entry";
else if (nf.is_exported) status = "exported";
else if (ic == 0 && iu == 0) status = "dead";
else if (ic == 1) status = "single";
else status = "normal";
result->nodes[i].status = status;
5. Store Results for Frontend
The final classification is attached to the layout result for visualization:
result->nodes[i].status = status;
The Store Layer and Batch Queries
The heavy lifting occurs in src/store/store.c within cbm_store_batch_count_degrees. This function constructs bounded IN clauses to query edge counts efficiently without hitting SQLite's parameter limits:
int cbm_store_batch_count_degrees(cbm_store_t *s,
const int64_t *node_ids, int id_count,
const char *edge_type, int *out_in, int *out_out)
{
/* ... builds IN clause like "(?,?,?)" ... */
bool has_type = edge_type && edge_type[0] != '\0';
/* inbound degree */
int rc = count_degrees_direction(s, node_ids, id_count,
in_clause, has_type, edge_type, true, out_in);
if (rc != CBM_STORE_OK) return rc;
/* outbound degree */
return count_degrees_direction(s, node_ids, id_count,
in_clause, has_type, edge_type, false, out_out);
}
The underlying count_degrees_direction executes the actual SQLite counting operations, filtering by edge type (CALLS or USAGE) and direction to populate the inbound and outbound degree buffers.
Edge Cases and Error Handling
MCP handles several critical edge cases to prevent false positives in dead code detection:
- Entry Points and Routes: Functions marked with
is_entry_pointor containingroute_pathare classified asentry, protecting main functions and HTTP handlers from deletion regardless of their inbound degree. - Exported Symbols: Public API surfaces flagged with
is_exportedreceiveexportedstatus, preserving library interfaces that external consumers may invoke. - Test Files: Files identified via
is_testflags or path heuristics are marked astest, acknowledging that test functions and helpers often lack internal callers by design. - Query Failure Fallback: If
cbm_store_batch_count_degreesreturns an error code (anything other thanCBM_STORE_OK), the code defaults to"normal"status rather than"dead". This conservative approach ensures transient SQLite errors never cause developers to delete active code.
Summary
- Dead code detection in codebase-memory-mcp occurs during the UI layout phase in
src/ui/layout3d.c, analyzing the complete dependency graph stored in SQLite. - A function is marked
deadonly when it has zero inboundCALLSand zero inboundUSAGEedges, indicating it is neither invoked nor referenced by any other node. - The system explicitly excludes entry points, exported symbols, routes, and test files through a priority-ordered decision matrix that checks metadata flags before degree counts.
- Batch queries using
cbm_store_batch_count_degreesinsrc/store/store.cefficiently compute degrees while respecting SQL parameter limits through chunkedINclauses. - Error handling defaults to
"normal"status to prevent false dead-code positives during database failures or connectivity issues.
Frequently Asked Questions
What defines "dead code" in codebase-memory-mcp?
In codebase-memory-mcp, dead code refers to functions or methods that have zero incoming CALLS edges and zero incoming USAGE edges in the dependency graph. This means no other function invokes them directly, and no other code references them as callbacks, event handlers, or configuration values. The analysis specifically targets the CALLS relationships established by src/pipeline/pass_calls.c and usage patterns tracked throughout the graph topology.
Does MCP detect dead classes or only functions?
The current implementation in src/ui/layout3d.c specifically checks for nodes labeled "Function" or "Method". Classes, structs, and other structural elements receive a structural status by default and are excluded from dead-code evaluation. The is_fn boolean check explicitly filters the classification logic to only consider callable code entities, preventing structural declarations from being flagged as dead.
How does MCP avoid flagging public APIs as dead code?
MCP parses node metadata from properties_json to identify exported symbols (is_exported), entry points (is_entry_point), and routes (route_path). These boolean flags take precedence in the decision matrix, assigning exported or entry status before the dead-code check executes. According to the source code in src/ui/layout3d.c, this ensures public interfaces and application entry points remain classified as active regardless of their actual inbound call count in the analyzed subgraph.
What happens if the SQLite query fails during dead code analysis?
If cbm_store_batch_count_degrees returns an error code (anything other than CBM_STORE_OK), the layout engine defaults the node status to "normal" rather than "dead". This conservative fallback, implemented in the error handling logic of the layout phase, prevents transient database errors or schema mismatches from causing developers to delete actually-used code. The system prioritizes safety over aggressive dead-code identification.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →