karellen-lsp-mcp
LSP-backed code intelligence for Claude Code via MCP and native LSP. Navigate definitions, references, call hierarchies, type hierarchies, h
Open source Open in the app JSON README (API)
About
LSP-backed code intelligence for Claude Code via MCP and native LSP. Navigate definitions, references, call hierarchies, type hierarchies, hover documentation, symbols, and diagnostics across C/C++ (clangd), Java/Kotlin (jdtls), and any language with a custom LSP server. Two interfaces, one shared daemon: - **Native LSP proxy** (`karellen-lsp`): Transparent code intelligence — no permission prompts, no manual registration. Auto-detects languages and routes to backend LSP servers. - **MCP interface** (`karellen-lsp-mcp`): Explicit tool calls with structured responses for fine-grained control. Multiple Claude sessions share a single daemon and single LSP server per project, avoiding duplicate indexing. Supports autodetection of CMake, Meson, Gradle, Maven, and other build systems.
Details
- Kind
- Plugins
- Topic
- Developer tools
- Publisher
- karellen
- Origin
- marketplace
- Category
- ferramentas
- Stars
- 2
- Forks
- 1
- Last push
- 2026-04-10T17:40:19Z
- Repository state
- ativo
- Language
- Python
- License
- Apache-2.0
- Added
- 2026-08-30 01:48:58
- Updated
- 2026-08-30 01:48:58
- Origin id
karellen/karellen-lsp-mcp/karellen-lsp-mcp
README
# MCP Server for LSP Code Intelligence (karellen-lsp-mcp)
[](https://gitter.im/karellen/Lobby)
[](https://github.com/karellen/karellen-lsp-mcp/actions/workflows/build.yml)
[](https://coveralls.io/r/karellen/karellen-lsp-mcp?branch=master)
[](https://pypi.org/project/karellen-lsp-mcp/)
[](https://pypi.org/project/karellen-lsp-mcp/)
[](https://pypi.org/project/karellen-lsp-mcp/)
[](https://pypi.org/project/karellen-lsp-mcp/)
[](https://pypi.org/project/karellen-lsp-mcp/)
## Overview
`karellen-lsp-mcp` gives LLM clients structured code intelligence via
[LSP](https://microsoft.github.io/language-server-protocol/) (Language Server Protocol)
servers. Instead of reading through entire codebases, the LLM can query definitions,
references, call hierarchies, type hierarchies, hover documentation, symbols, and
diagnostics — the same information a human developer gets from their IDE.
Two interfaces, one daemon:
- **MCP interface** (`karellen-lsp-mcp`): Explicit tool calls with structured responses.
Requires MCP tool approval or allow-rules.
- **LSP proxy interface** (`karellen-lsp`): Native LSP server that Claude Code uses
transparently — no permission prompts, no manual project registration. Auto-detects
languages and routes to backend LSP servers.
### Architecture
Multiple Claude sessions share a single daemon process and a single LSP server per project,
avoiding duplicate server instances and redundant indexing:
```
Claude Code LSP Claude MCP Session 1 Claude MCP Session 2
│ (stdio) │ (stdio) │ (stdio)
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ LSP proxy │ │ MCP stdio │ │ MCP stdio │
│ frontend │ │ frontend │ │ frontend │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ (Unix socket) │ │
▼ ▼ ▼
┌──────────────────────────────────────────────────────────────┐
│ karellen-lsp-mcp daemon │
│ │
│ Project Registry (refcounted) │
│ project-A refcount=3 ──► clangd │
│ project-B refcount=1 ──► jdtls │
└──────────────────────────────────────────────────────────────┘
```
- **Daemon**: Persistent process, owns all LSP server subprocesses and the project
registry. Listens on Unix domain socket. User-scoped. Auto-starts when the first
frontend connects, auto-exits after idle timeout with no connections.
- **MCP frontend**: Thin stdio process per Claude session. Connects to daemon, proxies
MCP tool calls. Returns structured data (dataclasses) for fast LLM processing.
- **LSP proxy frontend**: Standard LSP server over stdio. Auto-detects languages on
`initialize`, registers projects, routes LSP requests to the correct backend.
Reuses the same adapters, normalizers, and readiness tracking as MCP.
### Supported Languages
| Language | Default LSP Server | Autodetection | Details |
|----------|-------------------|---------------|---------|
| C / C++ | [clangd](https://clangd.llvm.org/) | CMake, Meson, autotools, Make, Bazel | [docs/c-cpp.md](docs/c-cpp.md) |
| Java / Kotlin | [jdtls](https://github.com/karellen/karellen-jdtls-kotlin) | Gradle, Maven, Ant | [docs/java-kotlin.md](docs/java-kotlin.md) |
| Python | [pyright](https://github.com/microsoft/pyright) | PyBuilder, pyproject.toml, setup.py, Pipfile, requirements.txt | [docs/python.md](docs/python.md) |
| Rust | [rust-analyzer](https://rust-analyzer.github.io/) | Cargo.toml, workspace detection | [docs/rust.md](docs/rust.md) |
| Any | Custom via `lsp_command` parameter | — | Provide the command in `lsp_register_project` |
Projects are autodetected when `language` is omitted from `lsp_register_project`, or
inspected explicitly via `lsp_detect_project`. Detection scans build system markers,
IDE metadata (JetBrains `.idea/`, Eclipse, VS Code), and source file conventions.
## Requirements
- **Python** >= 3.10
- **An LSP server** for your language, installed and on PATH
- **Linux** or **macOS** (uses Unix domain sockets; Windows 10 build 17063+ also works)
## Installation
```bash
pip install --user karellen-lsp-mcp
```
Install with LSP server dependencies:
```bash
pip install --user karellen-lsp-mcp[clangd] # C/C++ support
pip install --user karellen-lsp-mcp[jdtls] # Java/Kotlin support
pip install --user karellen-lsp-mcp[pyright] # Python support
pip install --user karellen-lsp-mcp[all] # All LSP servers
# Rust: rustup component add rust-analyzer
```
Or with pipx for an isolated environment:
```bash
pipx install 'karellen-lsp-mcp[all]'
```
## Claude Code Integration
### Plugin Installation (Recommended)
The plugin provides both interfaces in one package:
- **Native LSP server** (`karellen-lsp`): Transparent code intelligence — Claude Code uses
it automatically for supported file types. No permission prompts, no manual registration.
Auto-detects languages from CWD and routes to backend LSP servers.
- **MCP tools** (`karellen-lsp-mcp`): Explicit tool calls with structured responses, plus
hooks (prerequisite checks, compiler error detection),
skills (`/karellen-lsp-mcp:lsp-register`, `/karellen-lsp-mcp:lsp-investigate`), and
an autonomous `lsp-investigator` agent.
Both share the same daemon — MCP-registered projects are visible to native LSP queries
and vice versa.
**From Karellen marketplace:**
```bash
claude plugin marketplace add karellen/claude-plugins
claude plugin install karellen-lsp-mcp@karellen-plugins
```
**From local checkout:**
```bash
claude --plugin-dir /path/to/karellen-lsp-mcp
```
### Manual MCP Configuration (Alternative)
If you prefer not to use the plugin system, configure the MCP server directly:
```bash
claude mcp add --transport stdio karellen-lsp-mcp -- karellen-lsp-mcp
```
Or manually add to `~/.claude.json` (user scope) or `.mcp.json` in your project root
(project scope, shared via version control):
```json
{
"mcpServers": {
"karellen-lsp-mcp": {
"type": "stdio",
"command": "karellen-lsp-mcp"
}
}
}
```
If installed with pipx:
```bash
claude mcp add --transport stdio karellen-lsp-mcp -- pipx run karellen-lsp-mcp
```
or manually:
```json
{
"mcpServers": {
"karellen-lsp-mcp": {
"type": "stdio",
"command": "pipx",
"args": ["run", "karellen-lsp-mcp"]
}
}
}
```
Note: manual MCP configuration provides tools only, without hooks, skills, or agents.
### Auto-approve MCP tools
The native LSP interface requires no approval — Claude Code uses it transparently.
For MCP tools, Claude Code prompts for confirmation by default. To auto-approve, add
a permission rule to your user settings
(`~/.claude/settings.json`):
```json
{
"permissions": {
"allow": [
"mcp__karellen-lsp-mcp__*",
"mcp__plugin_karellen-lsp-mcp_karellen-lsp-mcp__*"
]
}
}
```
Or for a project-scoped setting, add the same rule to `.claude/settings.json` in your
project root (this file can be committed to version control so all team members get it).
### Teach Claude the LSP workflow
Claude will automatically discover all `lsp_*` tools, but to teach it **when and how** to
use them effectively, add the following to your project's `CLAUDE.md`:
````markdown
## LSP Code Intelligence
### When to Use LSP Tools
Use the `lsp_*` MCP tools for navigating and understanding codebases — especially when
you need to trace call chains, find all references to a symbol, understand type
hierarchies, or check compiler diagnostics without reading entire files.
### Setup
Register your project once at the start of a session. Do NOT ask the user for
configuration details. Do NOT narrate intermediate steps. Do the entire setup
(search, generate if needed, register) in one action sequence without pausing for
confirmation.
**C/C++ projects** use clangd as the default LSP server. clangd needs a
`compile_commands.json` for accurate results. Most projects don't have one pre-built.
Do all of this in one go:
1. Search for an existing `compile_commands.json` — use `Glob` to check the project
tree. Also check if there's an existing build directory (look for `build/`,
`cmake-build-*/`, `out/`, or a `compile_commands.json` symlink in the project root).
2. If not found, detect the build system and generate it:
- `CMakeLists.txt` → check if there's already a build directory with cmake cache
(in-tree or out-of-tree). If an existing build dir has `CMakeCache.txt`, re-run
cmake there with `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON`. Otherwise run
`cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -B build`.
- `meson.build` → `meson setup build` (generates it automatically)
- `Makefile`/`configure`/autotools → use Bear if available (`bear -- make`), or
run `make -n` to get compiler commands and write `compile_commands.json` yourself
- No build system → register without `build_info` (clangd still works for basics)
3. Register immediately — don't ask for confirmation:
```
lsp_register_project(
project_path="/path/to/project",
language="cpp", # or "c"
build_info={"compile_commands_dir": "/path/to/dir/containing/compile_commands.json"}
)
```
**Other languages**: pass a custom `lsp_command`:
```
lsp_register_project(
project_path="/path/to/project",
language="go",
lsp_command=["gopls"]
)
```
### Key Rules
- **Use LSP instead of grepping**: `lsp_find_references` is semantically aware — it finds
actual references, not string matches. It won't return comments, strings, or unrelated
symbols with the same name
- **Hover before reading**: `lsp_hover` gives you the type signature and documentation
for any symbol — often enough to understand usage without reading the full definition
- **Call hierarchy for impact analysis**: before changing a function, use
`lsp_call_tree_incoming` to get the full recursive call tree in one shot, or
`lsp_call_hierarchy_incoming` for a single level
- **Single-file queries work immediately**: `lsp_read_definition`, `lsp_hover`, and
`lsp_document_symbols` don't wait for background indexing. Use these freely even on
large codebases that are still indexing
- **Cross-file queries wait for indexing automatically**: `lsp_find_references`,
call hierarchy, type hierarchy, and `lsp_diagnostics` wait for indexing to finish,
with the timeout extending automatically as long as progress is being made. No need
to poll or sleep. Use `lsp_indexing_status(project_id)` to check progress on large
codebases
- **All positions are 1-based**: line and character offsets in both input and output
start at 1. Values from one tool's output (e.g. `lsp_workspace_symbols`) can be
fed directly into another tool's input (e.g. `lsp_read_definition`)
- **Stale compile_commands.json is auto-detected**: if build config files
(`CMakeLists.txt`, `meson.build`) are newer than `compile_commands.json`, or
5% or more of referenced source files no longer exist, the server regenerates
it automatically for CMake/Meson projects. For other build systems, the stale
file is used with a warning. Use `lsp_regenerate_index` to force regeneration
- **Per-tool timeout**: all tools accept an optional `timeout` parameter (seconds)
to override the default readiness timeout for that call. Useful for large
codebases or debugging timeout issues
- **Deregister when done**: `lsp_deregister_project` takes the `registration_id` returned
by register; the LSP server shuts down when all registrations are released
````
## Available Tools
### Project Lifecycle
| Tool | Description |
|------|-------------|
| `lsp_scan_languages` | Scan project for file extensions and recommend LSP registrations. Lightweight alternative to detect. |
| `lsp_detect_project` | Detect languages and build systems without registering. Analyzes build markers, IDE metadata, source conventions. |
| `lsp_register_project` | Register a project for LSP analysis. Returns a `project_id` (for queries) and a unique `registration_id` (for deregistering). Multiple sessions sharing the same project get the same LSP server. Use `regenerate=True` to clean managed data and force-restart. |
| `lsp_regenerate_index` | Clean managed data (compilation databases, workspace caches) and force-restart the LSP server. |
| `lsp_deregister_project` | Deregister a project by `registration_id`. Decrements refcount; stops LSP server at 0. Each token can only be used once. |
| `lsp_list_projects` | List all registered projects with status, refcounts, and paths. |
| `lsp_indexing_status` | Query indexing progress for a project: state, elapsed time, active tasks with percentages, completed task count. Returns immediately without waiting for readiness. |
### Code Navigation
| Tool | Description |
|------|-------------|
| `lsp_read_definition` | Go to definition of symbol at position. |
| `lsp_read_declaration` | Go to declaration (e.g., header in C/C++, interface in Java). |
| `lsp_read_type_definition` | Go to the type definition of a variable/expression. |
| `lsp_find_references` | Find all references to symbol at position. |
| `lsp_find_implementations` | Find all implementations of an interface/abstract method. |
| `lsp_hover` | Get type signature and documentation for symbol at position. |
### Symbols and Structure
| Tool | Description |
|------|-------------|
| `lsp_document_symbols` | List all symbols (functions, classes, variables, etc.) in a file. |
| `lsp_workspace_symbols` | Search for symbols across the entire project by name or pattern. |
| `lsp_call_hierarchy_incoming` | Find all callers of function/method at position (single level). |
| `lsp_call_hierarchy_outgoing` | Find all functions/methods called by function at position (single level). |
| `lsp_call_tree_incoming` | Recursively find all callers, returning a tree (default depth 3, has_more on truncated nodes). |
| `lsp_call_tree_outgoing` | Recursively find all callees, returning a tree (default depth 3, has_more on truncated nodes). |
| `lsp_type_hierarchy_supertypes` | Find base classes/interfaces of type at position (single level). |
| `lsp_type_hierarchy_subtypes` | Find derived classes/implementations of type at position (single level). |
| `lsp_type_tree_supertypes` | Recursively find all supertypes, returning a tree (default depth 3, has_more on truncated nodes). |
| `lsp_type_tree_subtypes` | Recursively find all subtypes, returning a tree (default depth 3, has_more on truncated nodes). |
### Diagnostics
| Tool | Description |
|------|-------------|
| `lsp_diagnostics` | Get compiler diagnostics (errors, warnings) for a file. |
## Structured Responses
All tools return structured data (dataclasses), not plain text. This enables fast LLM
processing without parsing. Examples:
**`lsp_read_definition`** returns `LocationResult`:
```json
{
"locations": [
{"file": "/path/to/impl.cpp", "line": 42, "character": 5}
]
}
```
**`lsp_document_symbols`** returns `DocumentSymbolsResult`:
```json
{
"symbols": [
{"name": "MyClass", "kind": "Class", "line": 10, "children": [
{"name": "method1", "kind": "Method", "line": 12},
{"name": "method2", "kind": "Method", "line": 18}
]}
]
}
```
**`lsp_call_hierarchy_incoming`** returns `CallHierarchyResult`:
```json
{
"direction": "incoming",
"items": [
{"name": "main", "kind": "Function", "file": "/path/to/main.cpp", "line": 5, "call_sites": 1}
],
"indexing": true
}
```
**`lsp_call_tree_incoming`** returns `CallTreeResult` (recursive, default depth 3):
```json
{
"direction": "incoming",
"root": {
"name": "target_func", "kind": "Function", "file": "/path/to/file.cpp", "line": 42,
"call_sites": 1,
"children": [
{"name": "caller_a", "kind": "Function", "file": "/path/to/a.cpp", "line": 10,
"call_sites": 2, "has_more": true, "children": [
{"name": "main", "kind": "Function", "file": "/path/to/main.cpp", "line": 5,
"call_sites": 1, "children": []}
]},
{"name": "caller_b", "kind": "Function", "file": "/path/to/b.cpp", "line": 20,
"call_sites": 1, "children": []}
]
},
"indexing": false
}
```
Nodes with `has_more: true` have deeper levels available — increase `max_depth` to explore.
Cross-file queries include `"indexing": true` when the LSP server is still building its
index, signaling that results may be incomplete.
**`lsp_indexing_status`** returns `IndexingStatusResult`:
```json
{
"state": "indexing",
"elapsed_seconds": 45.2,
"active_tasks": [
{"title": "indexing", "message": "loading index shards", "percentage": 30}
],
"completed_tasks": 2
}
```
## Configuration
### Timeouts
All timeouts are configurable via environment variables (in seconds). Set them in your
MCP server configuration:
```json
{
"mcpServers": {
"karellen-lsp-mcp": {
"type": "stdio",
"command": "karellen-lsp-mcp",
"env": {
"LSP_MCP_READY_TIMEOUT": "300",
"LSP_MCP_REQUEST_TIMEOUT": "120"
}
}
}
}
```
#### Per-Tool Timeout
All tools accept an optional `timeout` parameter (in seconds) that overrides the
daemon's default readiness timeout for that specific call. This is useful for
debugging or for large codebases that need more time to index:
```
lsp_find_references(project_id="<id>", file_path="...", line=42, character=10, timeout=300)
```
Default timeouts: 30s for lifecycle tools (scan, detect, deregister, list, indexing_status),
120s for query tools and registration.
#### Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `LSP_MCP_READY_TIMEOUT` | 120 | Base timeout (seconds) for cross-file queries to wait for indexing. Overridden by per-tool `timeout` parameter. Actual timeout extends dynamically based on indexing progress |
| `LSP_MCP_REQUEST_TIMEOUT` | 60 | Max seconds to wait for a single LSP JSON-RPC response |
| `LSP_MCP_CLIENT_TIMEOUT` | 180 | Max seconds for the MCP frontend to wait for a daemon response (must exceed ready + request timeouts) |
| `LSP_MCP_IDLE_TIMEOUT` | 300 | Seconds before the daemon auto-exits when idle (no connections, no projects) |
| `LSP_MCP_LOG_LEVEL` | `INFO` | Log verbosity for daemon and LSP proxy. Values: `DEBUG`, `INFO`, `WARNING`, `ERROR` |
For large codebases (e.g. LLVM, Linux kernel), cross-file query timeouts extend
automatically as long as indexing is making progress. Single-file queries (definition,
hover, document symbols) only wait for the server to start, not for indexing. Ensure
`LSP_MCP_CLIENT_TIMEOUT` exceeds your expected maximum indexing time +
`LSP_MCP_REQUEST_TIMEOUT`.
## C/C++ Setup with clangd
### compile_commands.json
clangd needs a [compilation database](https://clang.llvm.org/docs/JSONCompilationDatabase.html)
to understand your project's build flags, include paths, and defines. Generate one with:
**CMake:**
```bash
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -B build
```
**Bear (any build system):**
```bash
bear -- make
```
**Meson:**
```bash
meson setup build # compile_commands.json is generated automatically
```
Then register with the directory containing `compile_commands.json`:
```python
lsp_register_project(
project_path="/path/to/project",
language="cpp",
build_info={"compile_commands_dir": "/path/to/build"}
)
```
### Background indexing
For large projects, pass `--background-index` to clangd for cross-file features:
```python
lsp_register_project(
project_path="/path/to/project",
language="cpp",
lsp_command=["clangd", "--background-index"],
build_info={"compile_commands_dir": "/path/to/build"}
)
```
### Installing clangd
**Fedora / RHEL / CentOS:**
```bash
sudo dnf install clang-tools-extra
```
**Ubuntu / Debian:**
```bash
sudo apt install clangd
```
**Arch Linux:**
```bash
sudo pacman -S clang
```
**macOS:**
```bash
brew install llvm
```
## Troubleshooting
### Daemon files
The daemon stores its files in platform-standard directories (via
[platformdirs](https://pypi.org/project/platformdirs/)):
| Directory | Linux | macOS | Windows | Contents |
|-----------|-------|-------|---------|----------|
| Runtime | `~/.local/share/karellen-lsp-mcp/` | `~/Library/Caches/karellen-lsp-mcp/` | `%LOCALAPPDATA%/karellen-lsp-mcp/` | `daemon.sock`, `daemon.lock` |
| Logs | `~/.local/share/karellen-lsp-mcp/log/` | `~/Library/Logs/karellen-lsp-mcp/` | `%LOCALAPPDATA%/karellen-lsp-mcp/Logs/` | `daemon.log` |
| Data | `~/.local/share/karellen-lsp-mcp/` | `~/Library/Application Support/karellen-lsp-mcp/` | `%LOCALAPPDATA%/karellen-lsp-mcp/` | compile_commands copies, jdtls workspaces |
If the daemon gets into a bad state, remove the socket file and it will be restarted
automatically on the next MCP tool call. Check `daemon.log` to diagnose crashes or
unexpected behavior.
### LSP server not starting
If tools return errors about the LSP server, check:
1. The LSP server binary is on PATH (e.g. `which clangd`)
2. The project path is an absolute path
3. For C/C++, `compile_commands.json` exists in the specified `compile_commands_dir`
### Incomplete results during indexing
Cross-file queries (references, call hierarchy, type hierarchy, diagnostics) automatically
wait for indexing to finish, with the timeout extending dynamically based on progress.
If a query completes while indexing is still in progress (e.g. the server became "ready"
before full indexing finished), the response includes an `indexing: true` flag — results
may be incomplete. Use `lsp_indexing_status` to check progress, or re-query later.
Single-file queries (definition, hover, document symbols) always work immediately.
### Debugging
Set `LSP_MCP_LOG_LEVEL` to control log verbosity for both the LSP proxy and the daemon.
Values: `DEBUG`, `INFO` (default), `WARNING`, `ERROR`.
**LSP proxy logs** go to stderr. When launched by Claude Code, use `claude --debug` to
see LSP server output. For standalone testing:
```bash
LSP_MCP_LOG_LEVEL=DEBUG karellen-lsp 2>lsp-debug.log
```
**Daemon logs** are written to `daemon.log` (see table above for location). To enable
debug logging, kill the daemon and restart with the env var set:
```bash
pkill -f "karellen_lsp_mcp.daemon"
LSP_MCP_LOG_LEVEL=DEBUG karellen-lsp-mcp # daemon auto-starts with debug level
```
### Stale daemon
If you update `karellen-lsp-mcp` to a new version, the running daemon may still be the
old version. Kill the daemon to force a restart:
```bash
pkill -f "karellen_lsp_mcp.daemon"
```
The next MCP tool call or LSP query will auto-start the new version.
## License
Apache-2.0