When Names Are Not Enough: Type, Meaning, and Service Boundaries
deusdata/codebase-memory-mcp begins with syntax and structure, but many of the questions an agent cares about cannot be settled by a short name or a single parse tree. This chapter examines how the project adds stronger evidence through Hybrid LSP resolution, semantic and similarity layers, and service-boundary matching. The goal is to distinguish what each layer can contribute—and to understand why codebase-memory-mcp sometimes leaves a relationship unresolved rather than making a confident but unsupported guess.
The distinction follows from the evidence-chain model. A graph result must have the right identity and relationship type before it can support a conclusion. Type information can improve identity and relationship resolution. Semantic signals can broaden discovery when words differ. Service matching can connect local evidence across a boundary. None of these additions should erase the difference between source-derived structure, algorithmic similarity, and a cross-project inference.
From syntax to type-aware relationships
A syntax tree answers questions about form. It can show that a file contains a class, a call expression, an import, or a method selector. It can record the names appearing in those constructs. But syntax alone usually cannot establish which declaration a name refers to when the answer depends on imports, receiver types, inheritance, generics, or a definition in another file.
Imagine two files in the same project. One defines a base type and its method; another defines a child type that inherits from it and calls the method through a typed receiver. A syntax-only index can see the spelling of the base type and the call-shaped expression. It does not necessarily know whether the base refers to the project type, a standard-library type, a dependency, or another same-named declaration. A short-name resolver can choose something, but choosing the first matching spelling would turn ambiguity into a fabricated edge.
Codebase-memory-mcp addresses this gap with two complementary layers. Tree-sitter supplies broad syntactic extraction across the language registry. A lightweight, in-process Hybrid LSP layer then uses language-specific type-resolution logic to refine calls and callable-value references where the necessary evidence is available. The project describes support for selected families including Python, TypeScript and JavaScript variants, PHP, C#, Go, C and C++, Java, Kotlin, Rust, and Perl. The important architectural point is not the list; it is that the LSP layer runs alongside the syntax pass rather than replacing it with a separate language-server process for every project.
The cross-file pass turns extracted definitions and imports into the input shape that the language resolvers need. It can qualify a base class when the project registry, declaring module, and import map provide strong evidence. It can build an inverted module index so each file sees the definitions from its own module and the modules it actually imports, instead of repeatedly scanning every definition in the project. For some languages, it builds a read-only cross-file registry once and lets resolution workers reuse it. This changes the relationship from “the same word exists somewhere” to “this call is supported by the file's import and type context.”
The returned resolution still has a boundary. If the base class is ambiguous, if a receiver type cannot be established, or if the target is not represented in the graph, the resolver can leave the relationship unresolved. An unresolved result is not always a missing capability.