A production-ready, async-first Python client for the Language Server Protocol (LSP). Built for developers who need fine-grained control, container isolation, and extensibility when integrating language intelligence into their tools.
lsp-client is engineered for developers building production-grade tooling that requires precise control over language server environments:
- 🧩 Intelligent Capability Management: Zero-overhead mixin system with automatic registration, negotiation, and availability checks. Only access methods for registered capabilities.
- 🎯 Complete LSP 3.17 Support: Full specification implementation with pre-configured clients for multiple popular language servers. Easily extendable for custom servers and capabilities.
- 🐳 Container-First Architecture: Containers as first-class citizens with workspace mounting, path translation, and lifecycle management. Pre-built images available, seamless switching between local and container environments.
- ⚡ Production-Ready & Modern: Explicit environment control with no auto-downloads. Built with async patterns, comprehensive error handling, retries, and full type safety.
# Recommended: Use uv for modern Python dependency management
uv add lsp-client
# Or with pip
pip install lsp-clientThe following code snippet can be run as-is, try it out:
# install pyrefly with `uv tool install pyrefly` first
import anyio
from lsp_client import Position
from lsp_client.clients.pyrefly import PyreflyClient
async def main():
async with PyreflyClient() as client:
refs = await client.request_references(
file_path="example.py",
position=Position(10, 5)
)
for ref in refs:
print(f"Reference at {ref.uri}: {ref.range}")
anyio.run(main)async def main():
workspace = Path.cwd()
async with PyrightClient(
server=PyrightContainerServer(),
workspace=workspace
) as client:
# Find definition of a symbol
definitions = await client.request_definition_locations(
file_path="example.py",
position=Position(10, 5)
)
for def_loc in definitions:
print(f"Definition at {def_loc.uri}: {def_loc.range}")
anyio.run(main)The examples/ directory contains comprehensive usage examples:
pyright_container.py- Using Pyright in Docker for Python analysisrust_analyzer.py- Rust code intelligence with Rust-Analyzerpyrefly.py- Python linting and analysis with Pyreflyprotocol.py- Direct LSP protocol usage
Run examples with:
uv run examples/pyright_container.pyDefining a custom client is super easy with the capability mixin:
@define
class MyPythonClient(
Client,
WithRequestHover, # textDocument/hover
WithRequestDefinition, # textDocument/definition
WithRequestReferences, # textDocument/references
WithNotifyDidChangeConfiguration, # workspace/didChangeConfiguration
# ... and other capabilities as needed
):
def create_default_servers(self) -> DefaultServers:
return DefaultServers(
# support both local ...
local=LocalServer(program="pylsp", args=["--stdio"]),
# ... and containerized server!
container=ContainerServer(image="ghcr.io/observerw/lsp-client/python-lsp-server")
)
def create_initialization_options(self) -> dict:
return {"plugins": {"pyflakes": {"enabled": True}}} # custom init options
def check_server_compatibility(self, info: lsp_type.ServerInfo | None) -> None:
return # Custom compatibility checks if needed| Language Server | Module Path | Language | Container Image |
|---|---|---|---|
| Pyright | lsp_client.clients.pyright |
Python | ghcr.io/lsp-client/pyright:latest |
| Pyrefly | lsp_client.clients.pyrefly |
Python | ghcr.io/lsp-client/pyrefly:latest |
| Ty | lsp_client.clients.ty |
Python | ghcr.io/lsp-client/ty:latest |
| Rust Analyzer | lsp_client.clients.rust_analyzer |
Rust | ghcr.io/lsp-client/rust-analyzer:latest |
| Deno | lsp_client.clients.deno |
TypeScript/JavaScript | ghcr.io/lsp-client/deno:latest |
| TypeScript Language Server | lsp_client.clients.typescript |
TypeScript/JavaScript | ghcr.io/lsp-client/typescript:latest |
| Gopls | lsp_client.clients.gopls |
Go | ghcr.io/lsp-client/gopls:latest |
Container images are automatically updated weekly to ensure access to the latest language server versions.
- Method Safety: You can only call methods for capabilities you've registered. No runtime surprises from unavailable capabilities.
- Automatic Registration: The mixin system automatically handles client registration, capability negotiation, and availability checks behind the scenes.
- Zero Boilerplate: No manual capability checking, no complex initialization logic, no error handling for missing capabilities.
- Type Safety: Full type annotations ensure you get compile-time guarantees about available methods.
- Composability: Mix and match exactly the capabilities you need, creating perfectly tailored clients.
We welcome contributions! Please see our Contributing Guide for details on:
- Adding new language server support
- Extending protocol capabilities
- Container image updates
- Development workflow
This project is licensed under the MIT License - see the LICENSE file for details.
- Built on the Language Server Protocol specification
- Uses lsprotocol for LSP type definitions
- Architecture inspired by multilspy and other LSP clients