Claudeclaude-opus-5-5

What a language server does, and what it would add to code review

A walk through how a language server works inside, an experiment comparing it with a code graph on three real pull requests, and why saving its answers as an index file removes the memory cost.

Nov 15, 2026 · 13 min read

MainEnglish
Download

Original text

MMatteo Rossiasks
in conversation with
CClaudeclaude-opus-5-5

6 exchanges · 13 min

MMatteo RossiQuestion№ 01

What are language specific servers and how can they help us

CClaude

"Language-specific servers" almost always means language servers, the per-language analysis engines behind the Language Server Protocol (LSP): tsserver / typescript-language-server for TypeScript, pyright or pylsp for Python, gopls for Go, rust-analyzer for Rust, clangd for C/C++, jdtls for Java.

Each one loads a whole project the way its compiler would, resolves imports and types, and then answers a fixed set of JSON-RPC questions: go to definition, find references, hover (type and doc), document symbols, call hierarchy, diagnostics, rename.

Editors talk to them over the protocol, so one server serves VS Code, Neovim and anything else, including a script of ours.

The key difference from what the ingestion engine does today is resolution, not parsing. Tree-sitter and the IR give us syntax: a call site named foo, an import of ./bar. A language server gives us semantics: which foo, after overloads, re-exports, barrel files, tsconfig path aliases, generics and inheritance. That is exactly the layer where the graph currently relies on the resolver plus LLM prose, and where RULE 1 keeps biting.

Where they could help us, in rough order of payoff

  • Call and import edges with compiler-grade precision. Replace or cross-check the resolver's CALLS and IMPORTS_FILE edges using textDocument/definition and callHierarchy. Fewer wrong edges means collateral_damage and dragnet stop fanning out through name collisions, and the cross-repo join gets real targets instead of best-effort matches.
  • Ground truth for the review stage. The file session in file.ts judges hunks with graph tools and read_dependency. A language server on the checked-out PR commit could answer "who calls the function this hunk changed" and "what type does this now return" deterministically, before the LLM reasons. It also yields type errors from the diff itself, which are confirmed bugs by construction and fit the "post confirmed bugs only" rule.
  • Type information as evidence rather than LLM claims. Hover gives the signature, parameter types and return type verbatim, which could populate contracts from the compiler instead of from analysis prose.
  • Rename and references for blast radius. textDocument/references across the workspace is the exact query behind /blast, done by the tool that the language itself trusts.
  • Better unit spans and symbols. documentSymbol gives nested symbol ranges, useful as a second opinion for span repair work.

The costs, which are real for our setup

  • Each server needs the project buildable-ish. Dependencies installed, tsconfig found, Go modules fetched, Python environment present. For arbitrary indexed repos at arbitrary commits that is the expensive part, and it conflicts with the no-installs habit. Many servers degrade gracefully without deps, but precision drops to roughly tree-sitter level for external symbols.
  • Memory and startup. tsserver on a monorepo like godot-scale TypeScript or a large Python repo takes seconds to minutes to warm and can hold gigabytes. Fine per PR review, heavy per full-history ingestion.
  • One server per language, so polyglot repos mean orchestrating several, each with its own quirks. Multilspy, Sourcegraph's scip indexers, and Serena are existing wrappers worth looking at before writing our own client; scip in particular produces a static index file, which fits a batch ingestion job better than a long-lived server.

If you want a concrete first experiment, the smallest one with a measurable result is the review path: start typescript-language-server on the PR checkout, run references and definition on the symbols in each hunk, and compare that set with what the graph's read_dependency returns on the same PRs the benchmark already scores.

Keep reading

5 more exchanges in this ailog. Confirm you are a person to read the rest.