# Hybrid LSP Implementation in codebase-memory-mcp: Multi-Language Type Resolution

> Discover the hybrid LSP implementation in codebase-memory-mcp. It uses C-based type resolvers on tree-sitter ASTs for precise CALLS, USAGE, and RESOLVED_CALLS edges across Python, TypeScript, Go, Rust & more.

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

---

**The hybrid LSP implementation in `codebase-memory-mcp` embeds lightweight C-based type resolvers that run directly on tree-sitter ASTs, eliminating external LSP process overhead while enabling precise `CALLS`, `USAGE`, and `RESOLVED_CALLS` edges across Python, TypeScript, Go, Rust, and seven other languages.**

The `codebase-memory-mcp` repository (DeusData/codebase-memory-mcp) introduces a novel **hybrid LSP implementation** that bridges the gap between syntactic parsing and full language-server intelligence. Unlike traditional approaches that spawn external processes like `pyright` or `rust-analyzer`, this system embeds native C implementations that replicate core type-resolution logic while operating directly on existing tree-sitter abstract syntax trees. This architecture enables the graph builder to create type-aware edges without the latency and resource overhead of inter-process communication.

## What Is the Hybrid LSP Layer?

The **Hybrid LSP** layer serves as the second phase of the indexing pipeline, running immediately after the initial tree-sitter syntactic pass (as described in the README at lines 662-689). Rather than implementing full Language Server Protocol specifications, each module provides targeted **type-resolution logic** that mirrors the behavior of mainstream language servers.

The system resides in `internal/cbm/lsp/` and consists of individual C implementations for each supported language. A central dispatcher in [`internal/cbm/lsp_all.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/lsp_all.c) routes files to their appropriate resolver, while [`src/pipeline/pass_lsp_cross.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/pipeline/pass_lsp_cross.c) integrates the results into the cross-file registry and refines raw call edges with type information.

## Supported Languages and Implementation Files

Each language implementation resides in a dedicated C file under `internal/cbm/lsp/`. These files reproduce the specific type-resolution behaviors of their respective mainstream language servers.

### Python

**File:** [`internal/cbm/lsp/py_lsp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/lsp/py_lsp.c)

The Python hybrid LSP resolves imports and dotted sub-module walks, dataclasses, `Self` return types, and generics. It handles `@property` decorators, pattern-matching class patterns, and framework-specific types including SQLAlchemy 2.0 `Mapped[T]` and Pydantic models. Additional capabilities include `typing.Annotated` resolution, type narrowing via `isinstance` and `is not None` checks, walrus operator assignments, and `typing.cast` or `assert_type` assertions. The implementation also manages async/await patterns, class/static method dispatch, and stdlib modules such as `logging`, `pathlib`, `json`, and `functools`.

### TypeScript and JavaScript

**File:** [`internal/cbm/lsp/ts_lsp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/lsp/ts_lsp.c)

This implementation covers TypeScript, JavaScript, JSX, and TSX files. It resolves generics, JSX component dispatch, and JSDoc inference for plain JavaScript. The LSP handles [`.d.ts`](https://github.com/DeusData/codebase-memory-mcp/blob/main/.d.ts) declarations, re-exports, and method-chaining via return-type propagation, maintaining a cross-file registry for module resolution.

### Go

**File:** [`internal/cbm/lsp/go_lsp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/lsp/go_lsp.c)

The Go implementation provides per-package cross-file registry management, generic type resolution, and embedded struct analysis. It determines interface satisfaction and handles package-aware import resolution without requiring the Go toolchain.

### Rust

**File:** [`internal/cbm/lsp/rust_lsp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/lsp/rust_lsp.c)

Rust support covers `use` declarations, module paths, and `impl` blocks including trait methods. The resolver handles struct fields, generics with trait bounds, operator-trait desugaring, and derive-macro method synthesis. It implements Universal Function Call Syntax (UFCS) for static path resolution and recognizes common stdlib prelude items.

### Java

**File:** [`internal/cbm/lsp/java_lsp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/lsp/java_lsp.c)

The Java LSP resolves single-type and on-demand imports, including static imports. It navigates class hierarchies with `this` and `super` dispatch, handles generics, annotations, and overload matching. The implementation also supports lambdas, method references, and field-type inference for common JDK stdlib classes.

### C#

**File:** [`internal/cbm/lsp/cs_lsp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/lsp/cs_lsp.c)

For C#, the resolver manages global usings and file-scoped namespaces. It handles records including C# 12 primary constructors, LINQ method syntax, and async `Task<T>` or `ValueTask<T>` unwrapping. The implementation covers generic methods, `this`/`base` dispatch, `var` inference, and common Base Class Library (BCL) types.

### C and C++

**File:** [`internal/cbm/lsp/c_lsp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/lsp/c_lsp.c)

The C/C++ implementation distinguishes between the two languages. For C, it resolves macro expansion, `typedef` chains, and header-source linking. For C++, it handles templates, namespaces, `auto` inference, and method resolution via class hierarchies.

### Kotlin

**File:** [`internal/cbm/lsp/kotlin_lsp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/lsp/kotlin_lsp.c)

Kotlin support includes imports, classes/objects, and companion objects. The LSP resolves extension functions, data classes, nullable-type unwrapping, and scope functions (`let`, `apply`, `run`, `also`, `with`). It also handles infix calls and stdlib resolution.

### PHP

**File:** [`internal/cbm/lsp/php_lsp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/lsp/php_lsp.c)

The PHP implementation resolves namespaces, traits, and late-static-binding. It performs PHPDoc inference, parameter binding, and return-type inference across the codebase.

### Perl

**File:** [`internal/cbm/lsp/perl_lsp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/lsp/perl_lsp.c)

Perl support covers packages, `@ISA`/`use parent`/`use base` inheritance, and `SUPER::` calls. The resolver handles Exporter import maps, `bless`/`ref` self-type inference, and qualified `Pkg::sub` static calls for curated perlfunc and CPAN OOP stdlib usage.

## Architecture and Pipeline Integration

The hybrid LSP pass operates as a refinement layer within the indexing pipeline. After the tree-sitter syntactic pass constructs the initial graph, the system invokes [`internal/cbm/lsp_all.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/lsp_all.c) to dispatch each source file to its language-specific resolver.

The [`src/pipeline/pass_lsp_cross.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/pipeline/pass_lsp_cross.c) file coordinates cross-file analysis, building a registry of type definitions and method signatures that enable resolution of calls across module boundaries. This produces **type-aware edges** such as `RESOLVED_CALLS` that distinguish between syntactically similar but semantically distinct function invocations.

Languages without a dedicated implementation fall back to the syntactic resolver, ensuring the pipeline always produces results, albeit with reduced precision for type-dependent queries.

## Querying Type-Resolved Graph Edges

Once the repository has been indexed, the CLI exposes the type-resolved relationships through specific graph queries. The following commands demonstrate how to leverage the hybrid LSP results:

```bash

# Find the qualified name of a function resolved via LSP

codebase-memory-mcp cli search_graph \
  '{"project":"my-project","label":"Function","name_pattern":"^User\\.profile\\.display_name$"}'

# Retrieve the call-graph edge that was type-resolved

codebase-memory-mcp cli trace_path \
  '{"project":"my-project","function_name":"User.profile.display_name","direction":"inbound"}'

# Verify the edge originates from a different module (type-aware resolution)

codebase-memory-mcp cli get_code_snippet \
  '{"project":"my-project","qualified_name":"Service.account.get_user"}'

```

In the Python example above, a call to `user.profile.display_name()` resolves to the specific `Profile.display_name` method implementation rather than any syntactically matching function, thanks to the type information provided by [`py_lsp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/py_lsp.c).

## Summary

- **Embedded C implementations** in `internal/cbm/lsp/` provide language-specific type resolution without external LSP processes.
- **Ten languages** are supported: Python, TypeScript/JavaScript, Go, Rust, Java, C#, C/C++, Kotlin, PHP, and Perl.
- **Type-aware edges** including `CALLS`, `USAGE`, and `RESOLVED_CALLS` are generated by refining tree-sitter ASTs through the hybrid LSP layer.
- **Pipeline integration** occurs via [`src/pipeline/pass_lsp_cross.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/pipeline/pass_lsp_cross.c) and the dispatcher in [`internal/cbm/lsp_all.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/lsp_all.c).
- **Fallback behavior** ensures syntactic resolution remains available for languages without dedicated LSP implementations.

## Frequently Asked Questions

### How does the hybrid LSP differ from a traditional language server?

Traditional language servers like `pyright` or `rust-analyzer` run as separate processes and communicate via the Language Server Protocol, requiring JSON-RPC overhead and significant memory footprint. The hybrid LSP implementation in `codebase-memory-mcp` embeds lightweight C resolvers that operate directly on tree-sitter ASTs within the same process, eliminating IPC latency while providing the specific type information needed for call-graph construction.

### Which languages support full type resolution in the hybrid LSP?

According to the repository README (lines 662-682), full hybrid LSP support is available for Python, TypeScript/JavaScript (including JSX/TSX), Go, Rust, Java, C#, C/C++, Kotlin, PHP, and Perl. Each language has a dedicated implementation file in `internal/cbm/lsp/` that replicates the core type-resolution logic of its mainstream language server.

### What happens when a language lacks a hybrid LSP implementation?

When indexing a language without a dedicated LSP module, the system falls back to the syntactic resolver based purely on tree-sitter parsing. This guarantees that indexing succeeds and produces results, but call edges will not be type-resolved. For example, a method call might link to multiple candidates rather than the specific overriding implementation determined by type hierarchy.

### How does the Python LSP handle framework-specific types like SQLAlchemy?

The Python hybrid LSP in [`internal/cbm/lsp/py_lsp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/internal/cbm/lsp/py_lsp.c) includes specific logic for popular frameworks. It recognizes SQLAlchemy 2.0 `Mapped[T]` types, Pydantic model definitions, and `typing.Annotated` wrappers. This allows the graph builder to resolve relationships between ORM models and their actual column types, producing accurate `USAGE` edges for database-dependent codebases.