# How HTTP Routes Are Matched to Call‑Sites Across Services in Codebase‑Memory‑MCP

> Learn how Codebase-Memory-MCP matches HTTP routes to call-sites across microservices. Discover static analysis for code without execution in this technical guide.

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

---

**The Codebase‑Memory‑MCP (CBM) project constructs a persistent graph database that links HTTP route patterns directly to their implementing handler functions, enabling static analysis of which routes trigger which call‑sites across distributed microservices without executing code.**

Tracing HTTP routes matched to call‑sites across services is critical for dependency analysis and impact assessment in microservice architectures. CBM solves this by building a compressed, queryable graph that persists route‑to‑handler mappings across your entire codebase. The system extracts route definitions from source files, canonicalizes the patterns, and creates traversable edges that link HTTP endpoints to their implementation call‑sites.

## The Three‑Stage Route Matching Pipeline

CBM matches HTTP routes to call‑sites through a pipeline that combines static analysis with graph database storage. Each stage transforms raw source code into queryable relationships.

### Stage 1: Static Extraction of Route Definitions

Language‑specific parsers scan source files to identify HTTP route declarations. The extraction engine recognizes framework‑specific patterns across Go, Node.js, and Python.

*   **Go:** `http.HandleFunc`, `mux.Handle`, and `router.Path` calls
*   **Node.js/Express:** `app.get('/path', handler)` and similar HTTP method chains
*   **Python/Flask:** Decorators such as `@app.route('/path')`

When the scanner encounters a route definition, it records the literal pattern (e.g., `"/users/:id"`) and the identifier of the handler function. This data is then passed to canonicalization routines before graph insertion.

### Stage 2: Graph Insertion and CALLS Edge Creation

Each extracted route becomes a node in the graph, linked to its handler via a typed edge. The process creates three distinct artifacts:

1.  A **route node** containing the canonicalized pattern (e.g., `[Route "/orders/:orderId"]`)
2.  A **function node** representing the handler implementation (e.g., `[Function getOrder]`)
3.  A **`CALLS` edge** connecting the route node to the function node, which stores the file location for precise call‑site resolution

According to the CBM source code, insertion is performed through the generic `store_put` API in [`internal/cbm/zstd_store.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/zstd_store.c). This implementation writes node and edge data to a ZSTD‑compressed persistent store, ensuring the graph survives service restarts.

### Stage 3: Cross‑Service Traversal

Because all services write into the same shared graph, CBM can answer cross‑service queries such as "which routes resolve to a particular handler" or "which handlers might a given route invoke." The query engine walks the `CALLS` edges, filtering by the `service` attribute stored in each node. The [`server.json`](https://github.com/DeusData/codebase-memory-mcp/blob/main/server.json) configuration registers active microservices with the CBM server, enabling the query engine to scope traversals to specific service boundaries.

## Route Canonicalisation and Pattern Normalization

Before insertion, route patterns undergo canonicalisation to ensure consistency across different coding styles. The logic validates that patterns such as `"/users/:id"` are normalized correctly—removing trailing slashes and unifying parameter placeholders—before they enter the graph.

The unit tests in [`tests/test_route_canon.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/tests/test_route_canon.c) verify this canonicalization, ensuring that semantically equivalent routes (e.g., `/users/:id` and `/users/:id/`) are stored as identical nodes, preventing duplicate entries in the graph.

## Persistent Storage Implementation

The underlying storage layer in [`internal/cbm/zstd_store.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/zstd_store.c) implements a versioned, compressed format for the graph database. This C‑based module handles the low‑level persistence of nodes and edges, including the `CALLS` relationships that link HTTP routes to their handler functions. By using ZSTD compression, CBM maintains the entire routing map for large microservice ecosystems in a compact, replayable format.

## Configuration and Cross‑Service Discovery

The [`server.json`](https://github.com/DeusData/codebase-memory-mcp/blob/main/server.json) file configures which services participate in the graph, while the **graph‑ui** front‑end (configured via [`graph-ui/vite.config.ts`](https://github.com/DeusData/codebase-memory-mcp/blob/main/graph-ui/vite.config.ts)) visualizes the route‑to‑call‑site mappings. Because the graph is shared, any service can traverse relationships created by other services, revealing dependencies that span repository boundaries.

## Code Examples

The following patterns demonstrate how CBM extracts routes from various frameworks:

```go
// Go: Chi router example
router.HandleFunc("/api/v1/users/{id}", getUserHandler).Methods("GET")

```

```python

# Python: Flask decorator pattern

@app.route("/api/v1/users/<int:id>", methods=["GET"])
def get_user(id):
    pass

```

```javascript
// JavaScript: Express route registration
app.get('/api/v1/users/:id', (req, res) => { 
    // Handler implementation
});

```

In each case, CBM records the pattern (`/api/v1/users/:id`) and the handler identifier (`getUserHandler`, `get_user`, or the anonymous function), creating the graph edges described above.

## Summary

*   CBM uses **static extraction** to parse framework‑specific route definitions from Go, Python, and JavaScript/TypeScript source files.
*   Route patterns are **canonicalized** in [`tests/test_route_canon.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/tests/test_route_canon.c) to ensure consistent graph representation.
*   The **`CALLS` edge** links route nodes to function nodes, storing file locations for precise call‑site resolution.
*   **Persistent storage** in [`internal/cbm/zstd_store.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/zstd_store.c) uses ZSTD compression to maintain the graph across service restarts.
*   **Cross‑service traversal** leverages the shared graph and [`server.json`](https://github.com/DeusData/codebase-memory-mcp/blob/main/server.json) configuration to map dependencies across microservice boundaries.

## Frequently Asked Questions

### How does CBM handle different web frameworks?

CBM employs language‑specific parsers that recognize framework idioms. For Go, it detects `http.HandleFunc`, `mux.Handle`, and `router.Path` calls. For Node.js/Express, it identifies `app.get()` and similar method chains. For Python, it recognizes Flask decorators like `@app.route()`. Each pattern extracts the route string and handler reference for graph insertion.

### What storage format does CBM use for the route graph?

CBM persists the graph in a **ZSTD‑compressed binary format** implemented in [`internal/cbm/zstd_store.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/zstd_store.c). The `store_put` API writes nodes and edges to this compressed store, enabling efficient storage of large codebases and fast replay of the routing map after service restarts.

### How does CBM distinguish between routes from different microservices?

Each node in the graph includes a `service` attribute that identifies the originating microservice. When registering services via [`server.json`](https://github.com/DeusData/codebase-memory-mcp/blob/main/server.json), CBM indexes these attributes, allowing the query engine to filter traversals by service name or analyze cross‑service `CALLS` edges that link routes in one service to handlers imported from another.

### Can CBM trace routes across service boundaries?

Yes. Because all services write to the **same shared graph**, a route defined in Service A that invokes a handler imported from Service B creates a traversable edge across the service boundary. The graph represents the shared function node, allowing CBM to display the complete call‑site network for debugging and impact analysis without requiring runtime execution.