What Is Hybrid LSP and Which Languages Does It Support?
Hybrid LSP is the codebase-memory-mcp's built-in semantic type-resolution layer that enriches Tree-sitter ASTs with cross-file type information for nine major programming languages.
The codebase-memory-mcp project solves the problem of shallow syntactic analysis by embedding Hybrid LSP directly into its binary. Unlike traditional language servers that require external processes and complex configuration, this layer is implemented as a lightweight C library that runs after parsing to resolve imports, generics, and method dispatch across files.
What Is Hybrid LSP?
Hybrid LSP is a second-pass analysis stage that operates on every supported grammar after Tree-sitter completes its initial parse. While Tree-sitter excels at producing fast syntactic trees that identify names and call sites, it cannot semantically resolve a chain like user.profile.display_name() to the actual definition residing in another file.
The Hybrid LSP implementation walks the Tree-sitter AST, builds a cross-file registry, and emits enriched graph edges including RESOLVED_CALLS, USAGE, and DATA_FLOWS. This transforms raw syntax into a navigable semantic graph without requiring a separate language-server process, API keys, or per-project configuration files.
How Hybrid LSP Differs from Tree-Sitter
Tree-sitter supports 158 grammars but only provides syntactic information—it knows that a function is called, but not which concrete implementation is invoked, especially across module boundaries. Hybrid LSP adds the missing semantic layer:
- Tree-sitter: Identifies tokens, identifiers, and call expressions.
- Hybrid LSP: Resolves
importstatements, tracks inheritance hierarchies, handles generic type parameters, and maps method calls to their concrete implementations, including std-library types.
The mirror the behavior of major language servers (tsserver, pyright, gopls, Roslyn, rust-analyzer) while remaining 100% local.
Languages Supported by Hybrid LSP
While Tree-sitter handles 158 grammars syntactically, nine languages receive a full Hybrid LSP pass with deep semantic resolution. All other languages fall back to lighter textual resolution.
Python
The Python resolver in internal/cbm/lsp/py_lsp.c handles imports, dataclasses, Self returns, generics, @property decorators, pattern matching, SQLAlchemy 2.0 Mapped[T] types, Pydantic models, async/await constructs, and isinstance/walrus narrowing.
TypeScript, JavaScript, JSX, and TSX
Implemented in internal/cbm/lsp/ts_lsp.c, this resolver adds generics, JSX component dispatch, JSDoc inference, .d.ts declaration handling, re-export resolution, and method-chain return-type propagation.
PHP
The PHP module resolves namespaces, traits, late-static-binding, PHPDoc inference, and parameter/return-type inference across the project.
C#
The C# resolver supports global usings, file-scoped namespaces, records (including primary constructors), LINQ method syntax, async Task<T>/ValueTask<T> unwrapping, generic methods, var inference, and common Base Class Library (BCL) std-lib types.
Go
Implemented in internal/cbm/lsp/go_lsp.c, the Go resolver maintains a per-package cross-file registry, handles generics, embedded structs, interface satisfaction, and package-aware import resolution.
C and C++
The C/C++ resolver processes macro and typedef chains, links headers to sources (C), handles templates, namespaces, auto inference, and class-hierarchy method resolution (C++).
Java (v0.8.0+)
Added in version 0.8.0 via internal/cbm/lsp/java_lsp.c, the Java resolver supports imports (single-type, on-demand, static), this/super dispatch, generics, annotations, overload resolution, lambdas, method references, and the common JDK std-lib. It emits OVERLOAD_OF edges when matching methods by arity and parameter types.
Kotlin (v0.8.0+)
Also added in v0.8.0 in internal/cbm/lsp/kotlin_lsp.c, this resolver handles imports, same-package resolution, classes/objects/companions, extension functions, data classes, nullable-type unwrapping, scope functions (let, apply, run, also, with), infix calls, and common std-lib types.
Rust (v0.8.0+)
The Rust resolver in internal/cbm/lsp/rust_lsp.c processes use imports, module paths, impl blocks and trait methods, struct fields, generics with trait bounds, operator-trait desugaring, derive-macro methods, UFCS static paths, and the common std prelude.
Querying Hybrid LSP Data via the MCP API
The semantic edges generated by Hybrid LSP are accessible through the MCP API. Below are practical examples of how to query this enriched graph.
Resolve a Call Across Files
Use the trace_path tool to follow RESOLVED_CALLS edges across module boundaries:
# Resolve the target of profile.display_name() in myapp/views.py at line 42
result = cbm.trace_path(
src="myapp/views.py",
line=42,
depth=1,
mode="full"
)
# Returns the concrete definition with confidence 1.0
print(result)
# → {"callee_qn": "myapp/models/profile.py:Profile.display_name", "confidence": 1.0}
Find Unresolved Calls
Query for calls that the resolver could not type-resolve (emitted via ts_emit_unresolved_call or equivalents):
unresolved = cbm.search_graph(
query="MATCH (c:ResolvedCall) WHERE c.confidence = 0 RETURN c.callee_qn"
)
for call in unresolved:
print(call["callee_qn"])
Find Method Overloads (Java)
Query OVERLOAD_OF edges generated by the Java resolver:
overloads = cbm.search_graph(
query="""
MATCH (m:Method)-[:OVERLOAD_OF]->(base:Method)
WHERE base.name = 'add' AND base.owner = 'java.util.List'
RETURN m.signature, m.owner
"""
)
for o in overloads:
print(o["signature"], "in", o["owner"])
Key Implementation Files
The Hybrid LSP layer is defined in the following source files within the internal/cbm/lsp/ directory:
ts_lsp.c: TypeScript, JavaScript, JSX, and TSX resolutionpy_lsp.c: Python resolutiongo_lsp.c: Go resolutionjava_lsp.c: Java resolution (added v0.8.0)kotlin_lsp.c: Kotlin resolution (added v0.8.0)rust_lsp.c: Rust resolution (added v0.8.0)
These files define the language-specific resolution logic that builds the semantic graph edges.
Summary
- Hybrid LSP is a built-in semantic resolution layer that runs after Tree-sitter parsing to add cross-file type information.
- It is implemented as a lightweight C library compiled into the static binary, requiring no external language server processes or configuration.
- The system supports nine languages with full resolution: Python, TypeScript/JavaScript (including JSX/TSX), PHP, C#, Go, C/C++, Java, Kotlin, and Rust.
- It emits rich graph edges—including
RESOLVED_CALLS,USAGE,DATA_FLOWS, andOVERLOAD_OF—that enable deep code analysis across module boundaries.
Frequently Asked Questions
Is Hybrid LSP a separate language server process?
No. According to the source code in internal/cbm/lsp/, Hybrid LSP is not a separate process like tsserver or rust-analyzer. It is a lightweight C library compiled into the single static binary, requiring no per-project configuration, API keys, or external dependencies.
How does Hybrid LSP handle unresolved symbols?
When the resolver cannot determine a concrete type, it emits calls via ts_emit_unresolved_call (or language-specific equivalents) with a confidence of 0. These appear in the graph as ResolvedCall nodes with confidence: 0, allowing you to audit which symbols lack type resolution.
What is the difference between Tree-sitter and Hybrid LSP in codebase-memory-mcp?
Tree-sitter provides fast syntactic parsing across 158 grammars, identifying tokens, names, and call sites. Hybrid LSP runs as a second pass to perform semantic analysis—resolving imports, generics, inheritance, and method dispatch—to build a cross-file registry that Tree-sitter alone cannot create.
Which version of codebase-memory-mcp added Java, Kotlin, and Rust support?
Java, Kotlin, and Rust support were added in version 0.8.0, as reflected in the java_lsp.c, kotlin_lsp.c, and rust_lsp.c source files. These implementations support modern language features such as Kotlin's scope functions and Rust's trait-bound generics.
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 →