multilspy: a Python client for driving language servers
multilspy is a lsp client library in Python intended to be used to build applications around language servers.
At a glance
- What is it?
- multilspy is a Python library for building applications on top of language servers. It downloads the server binaries, starts and stops them, speaks JSON-RPC, and exposes a handful of static analysis queries as ordinary Python calls. The package metadata still classifies it as Pre-Alpha.
- Who is it for?
- Adopt multilspy if you are building a tool that needs real static analysis rather than a language model's guess, for example a code generation system that has to resolve symbol names before it emits a patch, because the library handles server binaries, configuration and the protocol so your code only issues queries.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 8 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A client library, not an editor plugin
The repository description is one line and it sets the scope precisely. multilspy is an LSP client library in Python, intended to be used to build applications around language servers. That word application is doing the work. The alternative to a language server is a code intelligence product with a user interface, an editor, or a completion popup, and multilspy is not that. It is the part that a program can call. The context is in the research it came from. The library was developed as part of the work behind a NeurIPS 2023 paper on Monitor-Guided Decoding of Code LMs with Static Analysis of Repository Context, also available on arXiv under the title Guiding Language Models of Code with Global Context using Monitors. In that work a monitor uses static analysis to guide decoding, so that generated code respects properties such as the absence of hallucinated symbol names and a valid order of method calls, and the repository for that work is microsoft/monitors4codegen. So the audience for multilspy is people building AI-assisted programming tools and code analysis systems, not people looking for a better editor. The topics list agrees, with ai4code, program-synthesis, code-analysis, lsp-client and language-server-protocol alongside the model oriented tags.
The four jobs the library takes off your hands
The README states plainly that multilspy eases the process of using language servers by handling various steps, and then lists them. It automatically handles the download of platform specific server binaries and the setup and teardown of language servers, so the tool does not have to know where jedi-language-server or gopls lives on a given machine. It handles the JSON-RPC based communication between the client and the server, which is the part where most hand written integrations turn into a maintenance burden. It maintains and passes hand tuned server and language specific configuration parameters, so each server gets the start up options it needs without every caller re-deriving them. And it provides a simple API to the user while executing all the server specific protocol steps needed to execute the query or request. That last item is the design statement of the whole library. The alternative is to write the JSON-RPC framing, the lifecycle and the configuration for every language you want to support, and multilspy's value is that it has already done that for the servers in its table.
The query surface is small, and deliberately so
The set of analyses multilspy exposes is listed in the README, and each one maps onto a specific LSP request. There is finding the definition of a function or a class, which is the textDocument/definition request. There is finding the callers of a function or the instantiations of a class, which is textDocument/references. There is type based dereference completions, from textDocument/completion. There is the information shown when hovering over a symbol, such as a method signature, from textDocument/hover. And there is the list or tree of all symbols defined in a file, with their kinds such as class or method, from textDocument/documentSymbol. What is notable is what is not in the list, because the README closes the enumeration with an instruction to create an issue or a pull request to add any other LSP request that is not listed. So the query surface is a curated subset rather than a generic protocol wrapper, and the project treats adding a request as a contribution rather than as something you do yourself. For a code generation pipeline, that subset is chosen well. Definition, references and hover are exactly the queries a monitor needs in order to check whether a symbol the model invented actually exists, and the tree of document symbols is what tells it what a file contains.
Two APIs, synchronous and asyncio
The usage example shows the shape of a call, and it is short enough to take in one pass. You build a configuration from a dictionary with the code language, create a logger, then create the server for a project root, and you open it as a context manager so that it is started and torn down around your work.
from multilspy import SyncLanguageServer
from multilspy.multilspy_config import MultilspyConfig
from multilspy.multilspy_logger import MultilspyLogger
...
config = MultilspyConfig.from_dict({"code_language": "java"}) # Also supports "python", "rust", "csharp", "typescript", "javascript", "go", "dart", "ruby", "kotlin", "php"
logger = MultilspyLogger()
lsp = SyncLanguageServer.create(config, logger, "/abs/path/to/project/root/")
with lsp.start_server():
result = lsp.request_definition(
"relative/path/to/code_file.java", # Filename of location where request is being made
163, # line number of symbol for which request is being made
4 # column number of symbol for which request is being made
)The requests take a file path and a position, so a definition request is a file, a line and a column, and the same shape is used for completions, references, document symbols and hover. There is a second, asynchronous API for async contexts, where the server is created from the LanguageServer class, entered with an async with on start_server, and the requests are awaited.
from multilspy import LanguageServer
...
lsp = LanguageServer.create(...)
async with lsp.start_server():
result = await lsp.request_definition(
...
)The context manager is the detail that matters for a tool, because it is what guarantees the server process is shut down when the analysis is done, and because it means the same code can be used from a script, a notebook or a service.
The language server table, and what each one drags in
The supported languages table maps twelve languages to their servers, and it is the clearest description of the project's coverage. Java is served by Eclipse JDTLS, Python by jedi-language-server, Rust by Rust Analyzer, C# by OmniSharp or RazorSharp, TypeScript and JavaScript both by TypeScriptLanguageServer, Go by gopls, Dart by the Dart server, Ruby by Solargraph, Kotlin by KotlinLanguageServer, PHP by Intelephense, and C++ by clangd. The dependency list in the packaging metadata is much shorter than the table, and the reason is the design. The install requires jedi-language-server, requests, typing-extensions and psutil, and the packaging declares the language agnostic part of the stack rather than pinning every server. The cost therefore lands at runtime, when a server binary for the language you asked for is downloaded, because the library handles that download itself. That is a deliberate trade. You get a uniform API across twelve languages for the price of a first-run download per language and the disk space that server needs. The other dependency worth knowing about is psutil, which is there so the library can watch the server process, and the packaging pins it to the 7.x range, so a host environment with a different psutil will need attention.
Why the library exists: Monitor-Guided Decoding
The AI4Code section explains the payoff. The README says multilspy provides all the features that the language server protocol provides to IDEs like VSCode, and that it is useful to develop toolsets that can interface with AI systems like large language models. The example given is Monitor-Guided Decoding, which is the research project the library was extracted from. The mechanism is worth spelling out, because it explains which queries matter and why the subset is what it is. A code language model generates code that may reference symbols that do not exist, may call methods in an order that does not compile, and may ignore the types of the repository it is working in. A monitor built on static analysis can check those properties before the generated code is accepted, and to do that it needs to ask a real language server what the symbols in the repository actually are. Definition, references, hover and document symbols are the queries that make that check possible, and the paper and the monitors4codegen repository are linked for the full method. The reason a library is the right shape here is that this checking has to happen inside a generation loop, repeatedly, for many candidate outputs, which is exactly the situation where hand managing a dozen language server processes becomes impractical.
Pre-Alpha status, auto tags and a fast release cadence
The packaging metadata is where the maturity is stated, and it is not flattering. The development status classifier is Development Status 2 - Pre-Alpha, the version is 0.1.1, and the release tags include auto-published builds, with v0.1.0-auto from 2026-04-16 and then v0.1.1 and v0.1.1-auto both on 2026-10-13, followed by a last push on 2026-10-15. The auto suffix is a signal about the workflow rather than about the version, and it means the safe thing to depend on is a specific published version rather than the head of the branch. The package is built with flit, requires Python from 3.10 up to 4.0, and ships under the MIT licence, which is permissive enough to use inside a commercial tool. The repository is not archived, and it carries the usual set of governance files, a code of conduct, a security policy, a support document, a citation file for the paper and an Azure pipeline definition. The tests are the practical way to judge the library, since the README points at the tests directory as the place with detailed usage examples, and they run with pytest tests/multilspy, with pytest-timeout and pytest-asyncio in the requirements to handle servers that hang and async tests.
Editorial conclusion
Adopt multilspy if you are building a tool that needs real static analysis rather than a language model's guess, for example a code generation system that has to resolve symbol names before it emits a patch, because the library handles server binaries, configuration and the protocol so your code only issues queries. It does not fit a team that needs a stable API, since the package is classified as Pre-Alpha and the release tags include auto-published builds, and it does not fit a project that only needs one language and one query, where starting the server yourself may be simpler. Before you build on it, run the test suite with pytest tests/multilspy, because the README points there for detailed usage examples, and confirm that the server binary for your language downloads in your environment.
Frequently asked questions
What is multilspy?
It is a cross platform Python client library for the Language Server Protocol, intended to be used to build applications around language servers. It was developed as part of the research behind a NeurIPS 2023 paper on Monitor-Guided Decoding of code language models with static analysis of repository context.
How do I install multilspy?
Multilspy requires Python 3.10 or newer and the README recommends a fresh virtual environment, with conda create -n multilspy_env python=3.10 followed by conda activate multilspy_env as the example. The package itself is then installed with pip install multilspy.
Which languages and language servers does multilspy support?
The table in the README lists Java with Eclipse JDTLS, Python with jedi-language-server, Rust with Rust Analyzer, C# with OmniSharp or RazorSharp, TypeScript and JavaScript with TypeScriptLanguageServer, Go with gopls, Dart, Ruby with Solargraph, Kotlin with KotlinLanguageServer, PHP with Intelephense, and C++ with clangd.
Which LSP requests can multilspy answer?
The README lists finding a definition, finding references or callers, type based dereference completions, hover information such as a method signature, and the list or tree of symbols in a file with their kinds. The README asks you to open an issue or pull request for any other LSP request that is not listed.
Does multilspy have an asynchronous API?
Yes. Alongside the synchronous SyncLanguageServer, the library provides an asyncio based API where the server is created from the LanguageServer class, entered with an async with on start_server, and the requests are awaited, for example await lsp.request_definition(...).
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/microsoft-multilspy)Was this useful?