Giving AI the sight of LSP.
lspi is an MCP server that bridges Language Server Protocol (LSP) features (definition/references/rename/diagnostics, etc.)
to AI coding CLIs (starting with Codex) over stdio.
If your agent keeps doing grep-like searches or fragile string edits, lspi gives it IDE-grade, symbol-aware tooling.
AI assistants are good at reasoning, but they usually lack reliable access to symbol relationships in a real codebase.
With lspi, your agent can:
- Jump to definitions, implementations, type definitions, and references
- Inspect hover/type information at a cursor position
- Use
*_attools with bounded position fuzzing (helps when the agent is off-by-one on line/column counting) - Perform preview-first, workspace-wide renames safely
- Query call hierarchy (incoming/outgoing calls) when supported by the language server
- You run
lspi mcpas a stdio MCP server. lspistarts and manages one or more LSP servers (your choice) and routes requests by file path / extension.- Tool results are returned as structured MCP responses, with output caps to keep results deterministic.
- Install language server(s)
lspi does not bundle language servers. Install what you need:
- Rust (recommended):
rustup component add rust-analyzer - Go:
go install golang.org/x/tools/gopls@latest - TypeScript:
npm i -g typescript typescript-language-server - Python:
npm i -g pyright - C# (OmniSharp): install
omnisharpand ensuredotnetis available
If you prefer project-local installs (recommended for TS/Python), set servers[].command accordingly.
- Install
lspi(one-time):
cargo binstall lspi
# or (build from source via crates.io):
cargo install lspi --locked- Create a per-project
lspiconfig (recommended):
cd /path/to/project
lspi setup --wizard --non-interactive --write- Verify language server availability:
lspi doctor --workspace-root .
# or machine-readable:
lspi doctor --workspace-root . --json- Configure Codex MCP (
~/.codex/config.toml):
[mcp_servers.lspi]
command = "lspi"
args = ["mcp", "--workspace-root", "."]- First tool calls (sanity check)
- Prefer introspection first:
get_current_config,list_servers,get_server_status - Then navigation:
hover_at,find_definition_at,find_references_at,get_document_symbols
Notes:
- Codex uses a global config; run
codexfrom the project root you want to work on. - If your
lspiconfig is not inside the workspace, pass it explicitly:args = ["mcp", "--workspace-root", ".", "--config", "/path/to/lspi.toml"]
- Optional: add
--warmupto start language servers eagerly (reduces first-tool-call latency). - Optional: add
--read-only(or setmcp.read_only=true) to expose a navigation-only toolset. - Optional: add
--context codexfor safe defaults (read-only + smaller output limits). Use--read-writeto opt back into rename/control tools. - Optional: add
--mode navigation(sugar for safe read-only defaults) or--mode refactor(sugar for read-write defaults).
For more details, see docs/CODEX.md.
- “No server found for extension …”:
- Add/verify
servers[].extensions, and ensurefile_pathmatches the intended language server.
- Add/verify
- Tool call fails with an error:
- Inspect
structuredContent.next_stepsfor actionable debugging steps (introspection tools + config hints).
- Inspect
- Language server not starting / command not found:
- Install the language server, or set
servers[].command/ the correspondingLSPI_*_COMMANDenv var. - Run
lspi doctor --workspace-root .for actionable hints.
- Install the language server, or set
- Off-by-one line/column:
- Prefer
*_attools (they use bounded position fuzzing).
- Prefer
- Large outputs / truncated results:
- Set
max_results/max_total_chars, and considerinclude_snippet=falsefor large reference sets.
- Set
- TypeScript/Vue returns empty/odd results:
- Configure
servers[].workspace_configurationand/or tuneinitialize_options/client_capabilities. - Consider
servers[].adapter = "tsserver"when using TypeScript/Vue tooling. - If calls time out on large workspaces, increase
request_timeout_msor setrequest_timeout_overrides_msfor slow methods (definition/references/rename/documentSymbol).
- Configure
lspi does not bundle language servers.
You install the servers you need and configure them in lspi config.
There are two layers of support:
- First-class server kinds (built-in defaults / extra compatibility):
- Rust:
kind = "rust_analyzer" - C#:
kind = "omnisharp" - Python:
kind = "pyright"/kind = "basedpyright"
- Rust:
- Generic mode: for any LSP server that speaks JSON-RPC over stdio, use
kind = "generic"and setcommand.
In practice, most “standard” language servers work well in generic mode (Go gopls, C++ clangd, Lua lua-language-server, etc.).
TypeScript/Vue tooling tends to be more quirky; lspi includes a small adapter layer for server-specific behavior
(see servers[].adapter, currently tsserver).
Yes. lspi does not bundle any language servers. Install the servers you need and point lspi at them via config.
Use lspi doctor --workspace-root . to validate your setup.
Yes, via kind = "generic" (provided the LSP server speaks JSON-RPC over stdio).
In practice:
- Go (
gopls) usually works well in generic mode. - TypeScript/Vue often needs extra tuning (runtime
workspace/configuration, and sometimesservers[].adapter = "tsserver").
Configure additional roots with servers[].workspace_folders. If you configure multiple LSP servers, tools like
search_workspace_symbols should include file_path so lspi can route to the right server.
No. Rename tools default to preview mode (dry_run=true), and you must explicitly request applying edits (dry_run=false).
- Inspect
structuredContent.errorand followstructuredContent.next_steps. - Use introspection tools:
get_current_config,list_servers,get_server_status. - If you can run local commands:
lspi doctor --workspace-root . --json.
To keep agent/tool behavior deterministic, responses are size-bounded.
Tune max_results / max_total_chars, and consider include_snippet=false for large reference sets.
Full schema and discovery order:
docs/CONFIG.md
[[servers]]
id = "rust-analyzer"
kind = "rust_analyzer"
extensions = ["rs"]
[[servers]]
id = "ts"
kind = "generic"
extensions = ["ts", "tsx", "js", "jsx", "vue"]
language_id = "typescript"
command = "typescript-language-server"
args = ["--stdio"]
adapter = "tsserver"
# Optional (sometimes needed for TypeScript tooling):
# [servers.workspace_configuration]
# formattingOptions = { tabSize = 2, insertSpaces = true }Common knobs (per server):
servers[].root_dirandservers[].workspace_folders(multi-root workspaces)servers[].initialize_optionsandservers[].client_capabilities(improve compatibility for “generic” servers)servers[].workspace_configuration(responses toworkspace/configuration, e.g. formatting options)servers[].request_timeout_overrides_ms(workspace-wide operations can be slow)servers[].adapter(server quirks; currentlytsserver)
Common environment variables:
LSPI_CONFIG_PATH: explicit config file pathLSPI_RUST_ANALYZER_COMMAND: overriderust-analyzercommandLSPI_OMNISHARP_COMMAND: overrideomnisharpcommandLSPI_PYRIGHT_COMMAND: overridepyright-langservercommandLSPI_BASEDPYRIGHT_COMMAND: overridebasedpyright-langservercommand
rename_symbolandrename_symbol_strictdefault to preview (dry_run=true).- To apply edits, pass
dry_run=false. - Optional strict apply: provide
expected_before_sha256(per-file SHA-256) and enable backups. - Writes are restricted to within the configured workspace root(s).
Session / introspection:
get_current_configlist_serversget_server_status
Read-only navigation:
hover_atget_document_symbolssearch_workspace_symbolsfind_definition/find_definition_atfind_references/find_references_atfind_implementation_atfind_type_definition_atfind_incoming_calls/find_incoming_calls_atfind_outgoing_calls/find_outgoing_calls_atget_diagnostics
Write / control:
rename_symbol/rename_symbol_strictrestart_serverstop_server
If you want a least-privilege toolset (e.g. read-only navigation), use mcp.tools allow/exclude in your config.
If you want a “hard” read-only mode (no rename/control tools even if allowed), use mcp.read_only=true or lspi mcp --read-only.
- Codex integration:
docs/CODEX.md - Configuration:
docs/CONFIG.md - MCP behavior (structuredContent / next_steps / debugging):
docs/MCP.md - Agent prompt snippet (copy-paste):
docs/AGENTS_SNIPPETS.md - Smoke tests:
docs/SMOKE_TEST.md - Release notes:
CHANGELOG.md
lspi is inspired by and references ideas from:
cclsp(MCP ↔ LSP bridge): https://github.com/ktnyt/cclspserena(agent tooling + MCP/LSP integration): https://github.com/oraios/serenarust-analyzer(Rust language server): https://github.com/rust-lang/rust-analyzer
Run MCP server without installing:
cargo run -p lspi -- mcp --workspace-root .Run tests:
cargo nextest run