# Dead Code Detection in Codebase Memory MCP: Knowledge Graph Implementation

> Discover how DeusData codebase-memory-mcp detects dead code using knowledge graph queries. Learn to identify unused functions efficiently by checking inbound CALLS and USAGE degrees.

- Repository: [Martin Vogel/codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp)
- Tags: deep-dive
- Published: 2026-07-15

---

**The system detects dead code by querying the SQLite knowledge graph for true inbound degrees of CALLS and USAGE edges, classifying functions as "dead" only when both counts equal zero across the entire graph.**

Dead code detection in DeusData's codebase-memory-mcp relies on a complete **knowledge graph** stored in SQLite to accurately identify unused functions. Unlike visual sampling methods that might miss cross-file references, the system performs full-graph degree queries to ensure a symbol truly has zero inbound dependencies before marking it as dead.

## How the Knowledge Graph Tracks Symbol Usage

The codebase-memory-mcp stores the entire codebase structure as a graph in a SQLite database. When the 3D layout engine generates visualizations in [`src/ui/layout3d.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/ui/layout3d.c), it does not rely on the sampled edges visible in the current viewport to determine code health. Instead, it queries the underlying store for the **true incoming degree** of every node.

The system uses two primary edge types to calculate dependency counts:

- **CALLS**: Represents function or method invocations
- **USAGE**: Represents symbol references and imports

These counts come directly from the store via the `cbm_store_batch_count_degrees` function implemented in [`src/store/cbm_store.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/store/cbm_store.c), ensuring the classification reflects the complete graph rather than a subset.

## The Classification Algorithm for Dead Code Detection

The dead code detection logic executes during layout generation and assigns a status string to each node based on its structural role and connectivity.

### Querying True Inbound Degrees

Before classification, the renderer fetches the complete inbound degree counts for batches of nodes. This operation must query the store directly rather than using sampled edge data:

```c
/* True full‑graph incoming degree for dead‑code classification. This MUST
 * come from the store, not the sampled `mapped` edges built above … */
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);

```

This batch operation populates arrays with the exact number of incoming CALLS and USAGE edges for each node in the batch, guaranteeing that no cross-file or off-screen references are missed.

### Status Assignment Logic

After retrieving the degree counts, the system applies a priority-based classification in [`src/ui/layout3d.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/ui/layout3d.c) (lines 84-90). The logic checks structural properties first, then dependency counts:

| Condition | Assigned Status |
|-----------|-----------------|
| Not a function/method | `"structural"` |
| Test file or test-marked | `"test"` |
| Entry point or route | `"entry"` |
| Exported symbol | `"exported"` |
| **Both** `in_calls == 0` **and** `in_usage == 0` | **`"dead"`** |
| `in_calls == 1` | `"single"` |
| Otherwise | `"normal"` |

The critical check for dead code requires both incoming call and usage counts to equal zero:

```c
if (!is_fn)               status = "structural";
else if (testish)         status = "test";
else if (nf.is_entry)    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";

```

## Implementation in layout3d.c

The dead code detection implementation centers on [`src/ui/layout3d.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/ui/layout3d.c), specifically between lines 45-55 and 62-75 where the degree queries execute, and lines 84-90 where the status determination occurs.

By querying `cbm_store_batch_count_degrees` directly against the SQLite store rather than using the visual window's sampled `mapped` edges, the system eliminates false positives that could arise from incomplete subgraph views. This ensures that a function is marked `"dead"` only if it truly has no callers or references anywhere in the codebase.

## Exposing Dead Code Data to Consumers

Once classified, the status information flows through the system architecture for downstream consumption. The `cbm_layout_node_t` structure defined in [`src/ui/cbm_layout.h`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/ui/cbm_layout.h) holds the `status` field, which is then serialized to JSON by [`src/ui/cbm_layout_to_json.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/ui/cbm_layout_to_json.c). This allows external tools and IDE integrations to consume dead code information programmatically.

```c
/* Example: fetch full‑graph degrees for a batch of nodes */
int64_t ids[] = { 101, 102, 103 };
int calls[3], usage[3], dummy[3];
cbm_store_batch_count_degrees(store, ids, 3, "CALLS", calls, dummy);
cbm_store_batch_count_degrees(store, ids, 3, "USAGE", usage, dummy);

/* Determine dead‑code status */
for (int i = 0; i < 3; ++i) {
    const char *status = (calls[i] == 0 && usage[i] == 0) ? "dead" : "alive";
    printf("Node %lld is %s\n", (long long)ids[i], status);
}

```

## Summary

- **Full-graph queries** via `cbm_store_batch_count_degrees` in [`src/store/cbm_store.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/store/cbm_store.c) provide accurate inbound degree counts for CALLS and USAGE edges.
- **Zero-inbound rule**: A function is classified as `"dead"` in [`src/ui/layout3d.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/ui/layout3d.c) only when both `in_calls` and `in_usage` equal zero.
- **Priority classification**: The system checks structural roles (tests, exports, entry points) before evaluating dependency counts to avoid misclassifying special symbols.
- **JSON serialization** through [`src/ui/cbm_layout_to_json.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/ui/cbm_layout_to_json.c) makes dead code detection results available to external tools.

## Frequently Asked Questions

### How does codebase-memory-mcp avoid false positives in dead code detection?

The system queries the **complete knowledge graph** stored in SQLite using `cbm_store_batch_count_degrees` rather than relying on sampled edges from the visual viewport. This ensures that cross-file references outside the current view are counted, preventing functions from being incorrectly marked as dead when they have legitimate callers elsewhere in the codebase.

### What types of symbols are excluded from dead code classification?

The algorithm in [`src/ui/layout3d.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/ui/layout3d.c) excludes non-functions (`"structural"`), test files (`"test"`), entry points (`"entry"`), and exported symbols (`"exported"`) from dead code evaluation. These categories receive special status assignments regardless of their inbound degree counts, as they serve architectural or integration purposes even with zero internal callers.

### How can downstream tools access dead code information?

Downstream tools consume dead code data through the JSON serialization layer in [`src/ui/cbm_layout_to_json.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/ui/cbm_layout_to_json.c), which exports the `status` field defined in [`src/ui/cbm_layout.h`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/ui/cbm_layout.h). This field contains string values like `"dead"`, `"single"`, or `"normal"` that tools can parse to identify unused functions and prioritize refactoring efforts.

### What distinguishes "dead" status from "single" status?

A node receives `"single"` status when it has exactly one incoming call (`in_calls == 1`), indicating it is used but potentially fragile or tightly coupled. In contrast, `"dead"` status requires **both** `in_calls` and `in_usage` to equal zero, indicating the function has no inbound dependencies whatsoever and can be safely removed without affecting system functionality.