Project-detail
Python Extended 2025

Serena

LSP-powered code intelligence — because RAG over a codebase is guesswork

The Problem

The internal tooling team at Booking.com manages a codebase of roughly 200,000 lines across 15 microservices. When a new engineer joins a service team, getting them to the point where they can confidently make changes typically takes two to three weeks of pairing sessions.

The bottleneck isn’t understanding the domain — it’s understanding the code topology: where functions are defined, what calls what, which interfaces are stable versus internal, which patterns are idiomatic versus legacy. Standard RAG over a codebase answers “find files that mention payment” — it doesn’t answer “show me everything that calls processRefund and whether any of those callers bypass the audit log.”

What We Built

Serena extended with a custom MCP interface for the team’s specific codebases. The key insight driving the extension: Language Server Protocol already has this information. Every modern LSP server — Pyright, rust-analyzer, typescript-language-server — maintains a full symbol graph. Instead of building a custom indexer, Serena wraps the existing LSP via multilspy.

How It Works

The multilspy abstraction starts a language server, sends LSP requests, and returns structured results — without the engineer needing to interact with the protocol directly:

from multilspy import SyncLanguageServer from multilspy.multilspy_config import MultilspyConfig, Language async def find_all_references( repo_path: str, file_path: str, line: int, character: int ) -> list[Location]: config = MultilspyConfig.from_dict({"code_language": Language.PYTHON}) async with SyncLanguageServer.create(config, repo_path) as lsp: refs = await lsp.request_references(file_path, line, character) return refs

This returns every call site for a function across the entire repository in under 200ms — the LSP server keeps a live index. Compare this to embedding-based search, which would miss callers in files that don’t use the function name literally (e.g., dynamic dispatch via getattr).

The 30-tool MCP interface exposed to Claude includes:

  • find_references — all call sites for a symbol
  • get_definition — jump to definition, cross-file
  • list_symbols_in_file — full outline of a file’s public interface
  • search_codebase — text + semantic combined search with relevance scoring
  • get_call_hierarchy — incoming and outgoing call tree for a function
  • check_diagnostics — current LSP errors/warnings for a file

The Outcome

50% review cycle time reduction
faster junior onboarding
<200ms cross-repo symbol lookup

Code review changed from “I’m not sure what this touches” to “I can see everything this call site reaches”. Reviewers use the get_call_hierarchy tool to verify that new changes don’t unintentionally modify audit-sensitive paths.

Junior engineers onboarding to a new service now start with a Serena session: “explain what processOrder does, show me where it’s called, and list every place it writes to the database.” This gives them a working mental model in one session that previously took days of reading.