How the Louvain Community Detection Algorithm Discovers Functional Modules in codebase-memory-mcp

The Louvain community detection algorithm in codebase-memory-mcp models your software as a call-graph where functions, methods, and classes are nodes, then applies a multi-level Leiden optimization to group tightly-coupled call-sites into cohesive functional modules.

codebase-memory-mcp treats software architecture as a network analysis problem. By representing the codebase as a directed graph of CALLS relationships and running the Leiden algorithm (a modern, refined variant of Louvain), it automatically identifies logical functional modules without requiring manual annotation or predefined boundaries. The implementation is found in src/store/store.c and exposed through the cbm_louvain and cbm_leiden APIs in src/store/store.h.

Modeling Code as a Call Graph

Before detection begins, the system abstracts the codebase into a mathematical graph structure. In src/store/store.c, the arch_clusters routine constructs this graph by querying the underlying SQLite store.

Nodes and Edge Relationships

The graph contains three types of vertices:

  • Function nodes
  • Method nodes
  • Class nodes

Edges represent directed CALLS relationships extracted from the edges table. According to the SQL queries in src/store/store.c (lines 5290–5293 and 5439–5450), the algorithm selects only edges where both endpoints are code entities (functions, methods, or classes) and the relationship type is exactly CALLS. This produces a clean CSR-style edge list (cbm_louvain_edge_t) that encodes the directed dependencies between software components.

The Leiden Algorithm Implementation

The core engine is cbm_leiden, a complete implementation of the Leiden algorithm following Traag et al. (2019). Unlike the classic Louvain method, Leiden guarantees that all communities are internally connected, producing more coherent modules. The algorithm proceeds through three repeating phases until modularity convergence.

Phase 1: Local Moving

Each node is iteratively moved to a neighboring community if the move increases modularity—a measure of the density of edges inside communities compared to edges outside them. The algorithm evaluates the resolution-adjusted modularity gain for every possible move and selects the highest positive gain. This greedy optimization runs across all nodes repeatedly until no further improvements can be made.

Phase 2: Refinement

After the local moving phase, the refinement step splits communities into smaller, well-connected sub-communities. This is the critical difference from classic Louvain: it ensures that the resulting functional modules are not just dense in connections, but internally coherent. A module cannot be fragmented into disconnected sub-components, which prevents the creation of illogical groupings in the software architecture.

Phase 3: Aggregation

The algorithm contracts each discovered community into a single super-node, aggregating the edge weights between communities. The entire process—local moving, refinement, and aggregation—repeats recursively on this coarser graph until the modularity score converges and no further improvements are possible.

From Graph to Functional Modules

The arch_clusters function orchestrates the end-to-end discovery pipeline in src/store/store.c (starting around line 5152).

  1. Node selection: Queries the database for all entities labeled Function, Method, or Class.
  2. Edge collection: Retrieves CALLS edges where both source and target exist in the selected node set.
  3. Community detection: Invokes cbm_leiden via the convenience wrapper cbm_louvain to partition the graph.
  4. Result processing: Constructs cbm_cluster_info_t structures for each detected community.

The algorithm automatically adapts to graph size and density, requiring no prior knowledge of the codebase structure.

Post-Processing and Cohesion Scoring

Raw community assignments are transformed into actionable architecture insights through quantitative analysis.

Calculating Module Cohesion

For each detected community, arch_clusters computes a cohesion score defined as:


cohesion = internal_edges / (internal_edges + boundary_edges)

This metric, implemented in src/store/store.c (lines 5422–5424), indicates how tightly-knit a functional module is. A cohesion score approaching 1.0 indicates a highly cohesive module with minimal external coupling, while lower scores suggest boundary-spanning concerns.

Labeling and Filtering

The system generates human-readable labels by analyzing the most frequent package names within each community. It then filters the results to return the top 12 communities (by default) that contain at least two members, discarding singletons and noise.

Developers can influence the granularity of the results through the resolution parameter exposed in the cbm_leiden API. Higher resolution values produce more, smaller modules; lower values yield fewer, larger architectural components.

Practical Usage Examples

You can invoke the functional module discovery through the C API or the command-line interface.

C API Integration

#include "store.h"

/* Open a store handle for your analyzed project */
cbm_store_t *store = cbm_store_open("my_project.db");
cbm_architecture_info_t arch;

/* Discover functional modules across the entire project */
int rc = cbm_store_get_architecture(
    store,                 // Store handle
    "my_project",          // Project name
    NULL,                  // Path filter (NULL = whole project)
    NULL, 0,               // Aspects (NULL = all)
    &arch);                // Output structure

if (rc == CBM_STORE_OK) {
    for (int i = 0; i < arch.cluster_count; ++i) {
        const cbm_cluster_info_t *c = &arch.clusters[i];
        printf("Module %d: %d members, cohesion %.2f, label: %s\n",
               c->id, c->members, c->cohesion, c->label);
    }
}

cbm_architecture_free(&arch);
cbm_store_close(store);

Command-Line Interface


# Discover architecture via the PyPI package CLI

codebase-mcp-get-architecture \
    --project my_project \
    --store my_project.db \
    --aspects all

The CLI tool ultimately calls cbm_store_get_architecture, executing the same Leiden-based pipeline described above.

Summary

  • codebase-memory-mcp represents software as a call-graph where functions, methods, and classes are nodes connected by CALLS edges.
  • The Leiden algorithm (accessible via cbm_leiden and cbm_louvain in src/store/store.h) optimizes modularity through iterative local moving, refinement, and aggregation phases.
  • The arch_clusters function in src/store/store.c orchestrates graph construction, runs the detection algorithm, and filters results to return the top functional modules.
  • Each module receives a cohesion score calculated from internal versus boundary edge counts, quantifying architectural coupling.
  • The optional resolution parameter allows tuning of module granularity without re-analyzing the source code.

Frequently Asked Questions

What is the difference between Louvain and Leiden in this implementation?

Leiden is the specific algorithm implemented in codebase-memory-mcp, serving as an improved successor to the classic Louvain method. While both optimize modularity, Leiden adds a refinement phase that guarantees all communities are internally connected. This prevents the formation of fragmented modules where two disconnected subgraphs are incorrectly grouped together, resulting in more logically coherent functional modules in software architecture analysis.

How does the algorithm handle large codebases?

The multi-level aggregation strategy makes the implementation scalable to large graphs. By contracting communities into super-nodes after each iteration, the algorithm reduces the problem size exponentially. The C implementation in src/store/store.c uses efficient CSR-style edge lists and operates directly on the SQLite store, allowing it to process codebases with tens of thousands of functions without loading the entire graph into memory.

What does the cohesion score represent?

Cohesion measures the internal coupling strength of a discovered module relative to its external dependencies. Calculated as internal_edges / (internal_edges + boundary_edges) in src/store/store.c (lines 5422–5424), this score ranges from 0 to 1. A score near 1.0 indicates a highly cohesive module where most calls stay within the boundary, while lower scores suggest the module acts as a cross-cutting concern or integration layer.

Can I adjust the granularity of the detected modules?

Yes, through the resolution parameter exposed in the cbm_leiden API. The resolution parameter acts as a scaling factor on the modularity calculation: values greater than 1.0 produce more, smaller communities (finer granularity), while values between 0 and 1.0 yield fewer, larger modules (coarser granularity). This allows you to tune the analysis to reveal microservices-level modules or high-level architectural layers without changing the underlying graph structure.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →