lsp-setup
Configure a Language Server (LSP) for a specific language so editor/agent tooling — diagnostics, go-to-definition, find-references, rename — works. Use when you need to: configure LSP, lsp setup, set up or install a language server, fix 'no LSP server configured' / 'server not in
Install
npx skills add https://github.com/code-yeongyu/oh-my-openagent/tree/dev/packages/shared-skills/skills/lsp-setup
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install code-yeongyu-oh-my-openagent@llmmart
git clone https://github.com/code-yeongyu/oh-my-openagent.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole code-yeongyu/oh-my-openagent collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
LSP Setup
Configure the right Language Server for a project so the lsp MCP tools
(diagnostics, goto_definition, find_references, symbols, rename)
actually work. This skill is an index: detect what a project needs, install the
server, write the config, then verify with a real roundtrip.
The list of servers we ship as builtin is the source of truth in
packages/lsp-tools-mcp/src/lsp/server-definitions.ts (BUILTIN_SERVERS +
LSP_INSTALL_HINTS). The per-language references below mirror it.
PHASE 0 — LANGUAGE GATE (run first)
Identify the language from the file extension, then read the matching reference before installing or configuring anything.
| Extension(s) | Reference |
|---|---|
.ts .tsx .js .jsx .mjs .cjs .mts .cts .vue .svelte .astro |
references/typescript/README.md |
.py .pyi |
references/python/README.md |
.go |
references/go/README.md |
.rs |
references/rust/README.md |
.c .cpp .cc .cxx .h .hpp .hh .hxx |
references/c-cpp/README.md |
.java |
references/java/README.md |
.kt .kts |
references/kotlin/README.md |
.cs .razor .cshtml |
references/csharp/README.md |
.swift |
references/swift/README.md |
.rb .rake .gemspec .ru |
references/ruby/README.md |
.php |
references/php/README.md |
.dart |
references/dart/README.md |
.ex .exs |
references/elixir/README.md |
.zig .zon |
references/zig/README.md |
.lua |
references/lua/README.md |
.sh .bash .zsh .ksh |
references/bash/README.md |
.yaml .yml |
references/yaml/README.md |
.tf .tfvars |
references/terraform/README.md |
.hs .lhs |
references/haskell/README.md |
.jl |
references/julia/README.md |
WORKFLOW — detect → install → configure → verify
1. Detect
Scan the project to see which languages are present and whether each server is installed and configured:
bun scripts/detect-lsp.ts <projectDir> # human report (default: cwd)
bun scripts/detect-lsp.ts <projectDir> --json
For each detected language it prints the builtin server id, the executable it
needs on PATH, whether that executable is installed, an install hint, and
whether a project config file already references it.
2. Install
Open references/<language>/README.md and run the install command for your OS.
Then confirm the executable resolves:
command -v <server-executable> # e.g. typescript-language-server, gopls, rust-analyzer
3. Configure
Most builtin servers need no config — they are resolved automatically by
file extension. Write config only to: pick between competing servers, set a
priority, pass initialization options, override extensions, set env, or
disable a server.
Two project-scoped config files, identical JSON shape:
- Codex harness →
.codex/lsp-client.json(user:~/.codex/lsp-client.json) - OpenCode/omo harness →
.opencode/lsp.json(also.omo/lsp.json)
{
"lsp": {
"<server-id>": {
"command": ["<bin>", "<args>"], // optional for builtin ids (supplied automatically)
"extensions": [".ext"], // optional override
"priority": 100, // higher wins when several servers match an extension
"initialization": { }, // server-specific initializationOptions
"env": { "KEY": "value" }, // optional
"disabled": false // set true to turn a server off
}
}
}
Rules enforced by config-loader.ts:
- In a project config (
.codex/lsp-client.json,.opencode/lsp.json) an entry whose id is a builtin server inheritscommandautomatically — you only overrideextensions/priority/initialization. A non-builtin id in a project config is ignored. - To define a fully custom (non-builtin) server with its own
command, put it in the user config (~/.codex/lsp-client.json, or the path set byLSP_TOOLS_MCP_USER_CONFIG), wherecommand+extensionsare honored. - Project entries win over user entries; both win over builtin defaults.
Each language reference gives a ready-to-paste snippet.
4. Verify
Run a real diagnostics roundtrip against a source file. This spawns the server,
opens the file, requests diagnostics, and reports OK/FAIL:
bun scripts/verify-lsp.ts <path/to/file.ext>
bun scripts/verify-lsp.ts <file> --timeout=90000
OK = the server started and answered. FAIL: language server not installed
= go back to step 2. Other FAIL text carries the server/startup error.
SKIP = the engine source could not be located; run from inside the omo
repo/worktree, or call the lsp MCP diagnostics tool directly.
Scripts
| Script | Purpose |
|---|---|
scripts/detect-lsp.ts |
Scan a directory; per detected language report server id, install status, install hint, config status. --json for machine output. |
scripts/verify-lsp.ts |
Real LSP diagnostics roundtrip for one file via the lsp-tools-mcp engine; OK/FAIL/SKIP + exit code 0/1/3. |
scripts/lsp-server-table.ts |
Embedded snapshot of the primary builtin server per language (mirrors server-definitions.ts). |
Run with Bun: curl -fsSL https://bun.sh/install | bash.
Files (oh-my-openagent)
-
references
-
bash
-
README.md 2.1 KB
# Bash — LSP setup - **Builtin server:** `bash` — `bash-language-server start` - **Extensions:** `.sh .bash .zsh .ksh` - **Install hint:** `npm install -g bash-language-server` An alias id `bash-ls` exists with the identical command; either id works. ## Install - **macOS:** `npm install -g bash-language-server` - **Linux:** `npm install -g bash-language-server` - **Windows:** `npm install -g bash-language-server` (PowerShell) For real diagnostics, also install `shellcheck`: - **macOS:** `brew install shellcheck` - **Linux:** `apt install shellcheck` (or `dnf install ShellCheck`) - **Windows:** `scoop install shellcheck` Confirm it resolves: ```bash command -v bash-language-server command -v shellcheck ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "bash": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) None commonly required. `bash-language-server` discovers `shellcheck` on PATH automatically. To point at a non-PATH binary, export `SHELLCHECK_PATH` via `env`: ```json { "lsp": { "bash": { "env": { "SHELLCHECK_PATH": "/opt/bin/shellcheck" } } } } ``` ## Alternatives - `shellcheck` standalone as a linter-only flow (no LSP). - `shfmt` for formatting (complements, does not replace, the LSP). ## Troubleshooting - **PATH:** `bash-language-server` on PATH; reopen shell after `npm -g` install. - **No diagnostics:** `shellcheck` missing — diagnostics are powered by it; install and reopen. - **Wrong shell dialect:** `.zsh`/`.ksh` are linted as bash; shellcheck may flag shell-specific syntax. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/file.sh ```
-
-
c-cpp
-
README.md 2.4 KB
# C / C++ — LSP setup - **Builtin server:** `clangd` — `clangd --background-index --clang-tidy` - **Extensions:** `.c .cpp .cc .cxx .c++ .h .hpp .hh .hxx .h++` - **Install hint:** `https://clangd.llvm.org/installation` ## Install - **macOS:** `brew install llvm` (clangd ships in the LLVM keg; add its `bin` to PATH) - **Linux:** `apt install clangd` (Debian/Ubuntu); use your distro package elsewhere - **Windows:** install LLVM from `https://releases.llvm.org` or `winget install LLVM.LLVM` See `https://clangd.llvm.org/installation` for other platforms. Confirm it resolves: ```bash command -v clangd ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "clangd": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) None commonly required. clangd reads flags from a project `.clangd` file rather than initializationOptions. The builtin command already passes `--background-index --clang-tidy`. ## Compile commands clangd needs a `compile_commands.json` at the project root (or in `build/`) for accurate diagnostics and cross-file navigation. Generate it with: - **CMake:** `cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON` (symlink/copy `build/compile_commands.json` to the root) - **Make / other:** `bear -- make` Without it, clangd falls back to heuristic flags and reports spurious errors. ## Alternatives None builtin. `ccls` exists as a third-party server but is not builtin — it would need a custom `command` in the USER config. ## Troubleshooting - **PATH:** `clangd` must be on PATH; reopen shell after install. Homebrew LLVM is keg-only — add `$(brew --prefix llvm)/bin` to PATH. - **Spurious "file not found" / unknown flags:** missing or stale `compile_commands.json` — regenerate it after changing the build. - **Header-only diagnostics wrong:** ensure the header's translation unit appears in the compile database, or add a `.clangd` `CompileFlags` block. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/file.cpp ```
-
-
csharp
-
README.md 2.4 KB
# C# — LSP setup - **Builtin server:** `csharp` — `csharp-ls` - **Extensions:** `.cs` - **Install hint:** `dotnet tool install -g csharp-ls` ## Install Requires the **.NET SDK**. Install the tool globally: - **macOS:** `dotnet tool install -g csharp-ls` - **Linux:** `dotnet tool install -g csharp-ls` - **Windows:** `dotnet tool install -g csharp-ls` Global .NET tools land in `~/.dotnet/tools` — ensure that directory is on PATH (Windows: `%USERPROFILE%\.dotnet\tools`). Confirm it resolves: ```bash command -v csharp-ls ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "csharp": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) None commonly required. `csharp-ls` picks up the nearest `.sln` or `.csproj`; keep the solution restorable (`dotnet restore`). ## Razor / Blazor Razor and Blazor files use a separate builtin server: - **Builtin server:** `razor` — `roslyn-language-server --stdio` - **Extensions:** `.razor .cshtml` - **Install hint:** `dotnet tool install -g roslyn-language-server --prerelease` (requires **v5.8.0+**; see [dotnet/razor](https://github.com/dotnet/razor)) ```bash dotnet tool install -g roslyn-language-server --prerelease command -v roslyn-language-server ``` Enable it in a project/user config: ```json { "lsp": { "razor": { } } } ``` ## Alternatives - **OmniSharp** — legacy C# language server (not builtin). Still works but is being superseded by the Roslyn-based servers; prefer `csharp-ls` / `roslyn-language-server`. ## Troubleshooting - **PATH:** `csharp-ls` / `roslyn-language-server` on PATH (`~/.dotnet/tools`); reopen shell after install. - **No .NET SDK:** install the SDK (not just the runtime) before installing the tool. - **No symbols:** run `dotnet restore`; an unrestored solution yields empty results. - **Razor needs v5.8.0+:** older `roslyn-language-server` builds lack the `--stdio` Razor support — install with `--prerelease`. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/File.cs ```
-
-
dart
-
README.md 1.9 KB
# Dart — LSP setup - **Builtin server:** `dart` — `dart language-server --lsp` - **Extensions:** `.dart` - **Install hint:** `Included with the Dart/Flutter SDK` ## Install The language server ships inside the Dart SDK (and the Flutter SDK, which bundles Dart). There is no separate package to install — just put `dart` (or `flutter`) on PATH. - **macOS:** `brew install dart` (or install Flutter and use its bundled `dart`) - **Linux:** install the Dart SDK from your package manager / `https://dart.dev/get-dart`, or install Flutter - **Windows:** install the Dart SDK or Flutter SDK and add its `bin` to PATH Confirm it resolves: ```bash command -v dart ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "dart": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) None commonly required. ## Alternatives None. ## Troubleshooting - **PATH:** `dart` must be on PATH; reopen the shell after installing the SDK. Flutter users: ensure `<flutter>/bin/cache/dart-sdk/bin` or the Flutter `bin` is exported. - **Flutter vs Dart:** if you only have Flutter installed, the bundled `dart` works — make sure Flutter's `bin` is on PATH rather than relying on a separate Dart install. - **SDK out of date:** run `dart --version` / `flutter upgrade` if analysis behaves oddly on newer language features. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/file.dart ```
-
-
elixir
-
README.md 2.1 KB
# Elixir — LSP setup - **Builtin server:** `elixir-ls` — `elixir-ls` - **Extensions:** `.ex .exs` - **Install hint:** `https://github.com/elixir-lsp/elixir-ls` ## Install ElixirLS needs Erlang/OTP and Elixir installed first. Build the release from `https://github.com/elixir-lsp/elixir-ls` and put the `elixir-ls` launcher script on PATH. - **macOS:** `brew install elixir-ls` (Homebrew provides the launcher), or build the release manually - **Linux:** clone elixir-ls, run `mix deps.get && mix compile && mix elixir_ls.release2 -o release`, then add `release/` to PATH - **Windows:** build the release and add the `release` dir (use the `.bat` launcher) to PATH Confirm it resolves: ```bash command -v elixir-ls ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "elixir-ls": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) None commonly required. ## Alternatives - **lexical** (not builtin): `lexical` — fast, modern alternative LSP. - **next-ls** (not builtin): `nextls --stdio` — from the elixir-tools project. ## Troubleshooting - **PATH:** `elixir-ls` must be on PATH; reopen the shell after install. - **asdf users:** the launcher is a shim — after `asdf install`, run `asdf reshim elixir` so the `elixir-ls` shim resolves, and ensure the Erlang/Elixir versions match the build. - **First start is slow:** ElixirLS compiles your deps on first run; initial diagnostics can take a while on large projects. - **OTP mismatch:** build elixir-ls with the same Erlang/Elixir versions you use for the project to avoid bytecode errors. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/file.ex ```
-
-
go
-
README.md 1.9 KB
# Go — LSP setup - **Builtin server:** `gopls` — `gopls` - **Extensions:** `.go` - **Install hint:** `go install golang.org/x/tools/gopls@latest` ## Install - **macOS:** `go install golang.org/x/tools/gopls@latest` (or `brew install gopls`) - **Linux:** `go install golang.org/x/tools/gopls@latest` - **Windows:** `go install golang.org/x/tools/gopls@latest` Requires the Go toolchain. `go install` drops the binary in `$GOPATH/bin` (default `~/go/bin`) — that directory must be on PATH. ```bash export PATH="$PATH:$(go env GOPATH)/bin" ``` Confirm it resolves: ```bash command -v gopls ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "gopls": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) None commonly required. To enable extra analyses or staticcheck: ```json { "lsp": { "gopls": { "initialization": { "staticcheck": true } } } } ``` ## Alternatives None — `gopls` is the official and de facto sole Go language server. ## Troubleshooting - **PATH:** `gopls` must be on PATH; ensure `$(go env GOPATH)/bin` is exported, then reopen the shell. - **No diagnostics / "no required module":** open the directory containing `go.mod` as the workspace root. Outside a module, gopls degrades. Run `go mod tidy` if dependencies are unresolved. - **Stale toolchain:** reinstall with `go install golang.org/x/tools/gopls@latest` after upgrading Go. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/file.go ```
-
-
haskell
-
README.md 2.3 KB
# Haskell — LSP setup - **Builtin server:** `haskell-language-server` — `haskell-language-server-wrapper --lsp` - **Extensions:** `.hs .lhs` - **Install hint:** `ghcup install hls` The `-wrapper` binary detects your project's GHC version and dispatches to the matching HLS build. ## Install - **macOS:** `ghcup install hls` (install ghcup via `brew install ghcup` or the official script) - **Linux:** `ghcup install hls` (ghcup script from https://www.haskell.org/ghcup/) - **Windows:** `ghcup install hls` (ghcup is installed via the Windows installer / PowerShell bootstrap) HLS needs a working GHC plus Cabal and/or Stack. Install a matching toolchain first: ```bash ghcup install ghc ghcup install cabal ``` Confirm it resolves: ```bash command -v haskell-language-server-wrapper ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "haskell-language-server": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) None commonly required. Per-project plugin/formatter settings normally live in a `hie.yaml` (cradle) and `.haskell-language-server` files rather than init options. ## Alternatives - `ghcide` (the core HLS engine, standalone) — largely superseded by HLS. - `hlint` standalone for lint-only checks; `ormolu`/`fourmolu` for formatting. ## Troubleshooting - **PATH:** `haskell-language-server-wrapper` on PATH; reopen shell after `ghcup install`. - **GHC mismatch:** the installed HLS must support your project's GHC version — run `ghcup install hls` for that GHC, or align GHC to a supported one. - **No cradle:** multi-package repos may need a `hie.yaml`; generate one with `gen-hie > hie.yaml`. - **Slow first load:** HLS compiles dependencies on first open; let it finish indexing. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/file.hs ```
-
-
java
-
README.md 2.6 KB
# Java — LSP setup - **Builtin server:** `jdtls` — `jdtls` - **Extensions:** `.java` - **Install hint:** `https://github.com/eclipse-jdtls/eclipse.jdt.ls` ## Install - **macOS:** `brew install jdtls` - **Linux:** Download from [eclipse-jdtls/eclipse.jdt.ls](https://github.com/eclipse-jdtls/eclipse.jdt.ls) releases, extract, and wrap the launcher as `jdtls` on PATH (some distros package it as `jdtls`/`jdt-language-server`). - **Windows:** Download the release archive and add the `jdtls` launcher (`bin/jdtls.bat` or the Python wrapper) to PATH. Requires a **JDK 17+** to run the language server itself (the project may target an older Java version). Confirm it resolves: ```bash command -v jdtls ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "jdtls": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) jdtls maintains a per-project **workspace data directory** and the **first index is slow** (it resolves the full classpath and builds). Point `JAVA_HOME` at a JDK 17+ if `jdtls` cannot find one: ```json { "lsp": { "jdtls": { "env": { "JAVA_HOME": "/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home" } } } } ``` Most settings (runtimes, format, import order) are passed via `settings.java.*` initialization options; defaults work for Maven/Gradle projects with a standard layout. ## Alternatives - **No mainstream alternative.** `jdtls` (Eclipse JDT Language Server) is the de-facto standard and powers the official VS Code Java extension. ## Troubleshooting - **PATH:** `jdtls` on PATH; reopen shell after install. - **No JDK found:** server exits immediately — set `JAVA_HOME` to a JDK 17+. - **Slow / no completions at first:** the initial classpath index can take a minute or more on large Maven/Gradle projects; wait for it to finish. - **Stale state:** delete the jdtls workspace data dir to force a clean re-index if results go wrong after big dependency changes. - **Build tool required:** keep `pom.xml` / `build.gradle` valid; a broken build descriptor breaks symbol resolution. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/File.java ```
-
-
julia
-
README.md 2.3 KB
# Julia — LSP setup - **Builtin server:** `julials` — `julia --startup-file=no --history-file=no -e using LanguageServer; runserver()` - **Extensions:** `.jl` - **Install hint:** `julia -e 'using Pkg; Pkg.add("LanguageServer")'` The PATH executable is `julia`; LanguageServer.jl is launched through the `-e` snippet, not as its own binary. ## Install Install Julia (juliaup recommended), then add the `LanguageServer` package: - **macOS:** `brew install juliaup && juliaup add release` - **Linux:** `curl -fsSL https://install.julialang.org | sh` (installs juliaup) - **Windows:** `winget install julia -s msstore` (installs juliaup) Then add the package — ideally into a shared `@lsp` environment so it is not tied to one project: ```bash julia --project=@lsp -e 'using Pkg; Pkg.add("LanguageServer")' ``` Confirm Julia resolves (the LSP binary IS `julia`): ```bash command -v julia ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "julials": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) None commonly required. To pin which environment hosts LanguageServer.jl, set `JULIA_PROJECT` (or `JULIA_DEPOT_PATH`) via `env`: ```json { "lsp": { "julials": { "env": { "JULIA_PROJECT": "@lsp" } } } } ``` ## Alternatives - The VS Code Julia extension bundles the same LanguageServer.jl server. ## Troubleshooting - **PATH:** `julia` on PATH (not a `julials` binary); reopen shell after juliaup install. - **First run precompiles — be patient:** the initial launch compiles LanguageServer.jl and may take minutes with no output; do not kill it. Subsequent starts are fast. - **Package not found:** `LanguageServer` must be installed in the environment the server runs in (e.g. `@lsp`); add it there and set `JULIA_PROJECT`. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/file.jl ```
-
-
kotlin
-
README.md 2.4 KB
# Kotlin — LSP setup - **Builtin server:** `kotlin-ls` — `kotlin-lsp` - **Extensions:** `.kt .kts` - **Install hint:** `https://github.com/Kotlin/kotlin-lsp` ## Install The official **JetBrains Kotlin LSP** is pre-release. Download a build from the [Kotlin/kotlin-lsp](https://github.com/Kotlin/kotlin-lsp) releases and put the `kotlin-lsp` launcher on PATH. - **macOS:** Download the release archive, extract, then symlink the launcher: `ln -s /path/to/kotlin-lsp/kotlin-lsp.sh /usr/local/bin/kotlin-lsp` - **Linux:** Same as macOS — extract the release and place/symlink `kotlin-lsp` on PATH. - **Windows:** Extract the release and add the directory containing `kotlin-lsp.bat` to PATH (invoke as `kotlin-lsp`). Requires a **JDK** on the machine to run the server. Confirm it resolves: ```bash command -v kotlin-lsp ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "kotlin-ls": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) None commonly required. If `kotlin-lsp` cannot find a Java runtime, set `JAVA_HOME`: ```json { "lsp": { "kotlin-ls": { "env": { "JAVA_HOME": "/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home" } } } } ``` The server resolves classpath from Gradle/Maven; keep the build descriptor importable. ## Alternatives - **`fwcd/kotlin-language-server`** — older community server (not builtin). Still usable but less actively maintained than the official JetBrains one. ## Troubleshooting - **PATH:** `kotlin-lsp` on PATH; reopen shell after install. - **Pre-release churn:** the JetBrains server is early; pin a known-good release and expect occasional breakage. - **No JDK:** server fails to start — install a JDK and/or set `JAVA_HOME`. - **Slow first import:** Gradle resolution on first open can be slow on large projects; let it complete. - **`.kts` scripts:** build/script files resolve more slowly than `.kt` sources; this is expected. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/File.kt ```
-
-
lua
-
README.md 2 KB
# Lua — LSP setup - **Builtin server:** `lua-ls` — `lua-language-server` - **Extensions:** `.lua` - **Install hint:** `https://github.com/LuaLS/lua-language-server` ## Install See `https://github.com/LuaLS/lua-language-server`. - **macOS:** `brew install lua-language-server` - **Linux:** download a release from GitHub, or `pacman -S lua-language-server` (Arch) / AUR - **Windows:** download a release from the GitHub releases page and add its `bin` to PATH Confirm it resolves: ```bash command -v lua-language-server ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "lua-ls": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) For Neovim config development, point the server at the Neovim runtime and set the Lua runtime version so `vim` globals and stdlib resolve: ```json { "lsp": { "lua-ls": { "initialization": { "Lua": { "runtime": { "version": "LuaJIT" }, "workspace": { "library": ["/usr/share/nvim/runtime/lua"] }, "diagnostics": { "globals": ["vim"] } } } } } } ``` ## Alternatives None. ## Troubleshooting - **PATH:** `lua-language-server` must be on PATH; reopen the shell after install. - **Undefined `vim` global:** add `vim` to `Lua.diagnostics.globals` and set `Lua.workspace.library` (see above) for Neovim work. - **Wrong runtime version:** set `Lua.runtime.version` (`LuaJIT`, `Lua 5.4`, etc.) to match your interpreter, or stdlib functions report as undefined. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/file.lua ```
-
-
php
-
README.md 1.9 KB
# PHP — LSP setup - **Builtin server:** `php` — `intelephense --stdio` - **Extensions:** `.php` - **Install hint:** `npm install -g intelephense` ## Install Intelephense is a Node package, so Node.js (and npm) must be installed first. - **macOS:** `npm install -g intelephense` - **Linux:** `npm install -g intelephense` - **Windows:** `npm install -g intelephense` Confirm it resolves: ```bash command -v intelephense ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "php": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) Intelephense's premium features (rename, find-all-implementations, declaration providers, etc.) require a licence key. Supply it via `initialization`: ```json { "lsp": { "php": { "initialization": { "licenceKey": "YOUR-LICENCE-KEY" } } } } ``` Without a key the server runs fine in free mode. ## Alternatives - **phpactor** (not builtin): `phpactor language-server`. Pure-PHP, no Node dependency. ## Troubleshooting - **PATH:** `intelephense` must be on PATH; reopen the shell after a global npm install. If missing, check `npm bin -g` is on PATH. - **No Node:** Intelephense fails to start without Node.js. Install Node, then reinstall. - **Wrong PHP version inference:** set `intelephense.environment.phpVersion` via `initialization` to match your project. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/file.php ```
-
-
python
-
README.md 2.9 KB
# Python — LSP setup - **Builtin server:** `basedpyright` — `basedpyright-langserver --stdio` - **Extensions:** `.py .pyi` - **Install hint:** `pip install basedpyright` ## Install - **macOS:** `pip install basedpyright` (or `uv tool install basedpyright`) - **Linux:** `pip install basedpyright` (or `uv tool install basedpyright`) - **Windows:** `pip install basedpyright` Prefer `uv tool install basedpyright` when the project uses uv — it keeps the server isolated from project venvs and always on PATH. Confirm it resolves: ```bash command -v basedpyright-langserver ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "basedpyright": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) None commonly required. Type-check mode is usually set via `pyrightconfig.json` or `[tool.basedpyright]` in `pyproject.toml`, not the LSP config. ## Choosing a server All four are builtin. Type checkers and the linter serve different roles — run a type server, and optionally `ruff` ALONGSIDE it (not instead). | id | command | install | role | | ------------- | ----------------------------- | ---------------------- | --------------------------------------- | | `basedpyright`| `basedpyright-langserver --stdio` | `pip install basedpyright` | strictest types, **default** | | `pyright` | `pyright-langserver --stdio` | `pip install pyright` | upstream Microsoft type checker | | `ty` | `ty server` | `pip install ty` | Astral, very fast, pre-1.0/experimental | | `ruff` | `ruff server` | `pip install ruff` | lint + format only, complements a type server | Recommended priority: **basedpyright** (default) > pyright > ty (experimental). `ruff` complements via priority — it does not type-check, so keep a type server enabled. Enable ruff alongside basedpyright, disabling pyright: ```json { "lsp": { "basedpyright": { "priority": 100 }, "ruff": { "priority": 90 }, "pyright": { "disabled": true } } } ``` ## Troubleshooting - **PATH:** `basedpyright-langserver` must be on PATH; reopen shell after install. `uv tool install` writes to `~/.local/bin`. - **Wrong interpreter / missing imports:** the server must see the project venv. Set `python.pythonPath` / `venvPath` in `pyrightconfig.json`, or activate the venv before launching. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/file.py ```
-
-
ruby
-
README.md 2.3 KB
# Ruby — LSP setup - **Builtin server:** `ruby-lsp` — `rubocop --lsp` - **Extensions:** `.rb .rake .gemspec .ru` - **Install hint:** `gem install ruby-lsp` > **Note:** the builtin id is `ruby-lsp`, but the executable actually invoked is **`rubocop`** (`rubocop --lsp`). RuboCop must be installed: `gem install rubocop`. ## Install - **macOS:** `gem install rubocop` (and `gem install ruby-lsp` for the install hint's gem) - **Linux:** `gem install rubocop` - **Windows:** `gem install rubocop` In a Bundler project, prefer adding `rubocop` to the `Gemfile` and running via `bundle exec`. Confirm it resolves (check `rubocop`, since that is what runs): ```bash command -v rubocop ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "ruby-lsp": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) None commonly required. Behavior is driven by your `.rubocop.yml`; the server surfaces RuboCop diagnostics, formatting, and code actions over LSP. ## Alternatives - **`ruby-lsp` gem server** (not builtin) — the standalone Shopify Ruby LSP binary (`ruby-lsp` executable), richer navigation than RuboCop alone. Configure as a custom server with `command: ["ruby-lsp"]` in the USER config. - **`solargraph`** (not builtin) — older completion/type server; install with `gem install solargraph`, custom `command: ["solargraph", "stdio"]`. ## Troubleshooting - **PATH:** `rubocop` on PATH (that is the invoked binary, not `ruby-lsp`); reopen shell after install. - **`rubocop` not found:** the builtin fails even if the `ruby-lsp` gem is installed — install RuboCop with `gem install rubocop`. - **Bundler mismatch:** if the project pins RuboCop in its `Gemfile`, run inside the bundle so versions match. - **No diagnostics:** check `.rubocop.yml` is valid and not disabling everything. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/file.rb ```
-
-
rust
-
README.md 2 KB
# Rust — LSP setup - **Builtin server:** `rust` — `rust-analyzer` - **Extensions:** `.rs` - **Install hint:** `rustup component add rust-analyzer` ## Install - **macOS:** `rustup component add rust-analyzer` (or `brew install rust-analyzer`) - **Linux:** `rustup component add rust-analyzer` - **Windows:** `rustup component add rust-analyzer` The rustup component is the recommended path — it stays pinned to your toolchain. `rust-analyzer` also needs the `rust-src` component to index the standard library (`rustup component add rust-src`). Confirm it resolves: ```bash command -v rust-analyzer ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "rust": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) None commonly required. To switch the check command to clippy: ```json { "lsp": { "rust": { "initialization": { "check": { "command": "clippy" } } } } } ``` ## Alternatives None — `rust-analyzer` is the official and sole Rust language server. ## Troubleshooting - **PATH:** `rust-analyzer` must be on PATH; reopen shell after install. The rustup shim lives in `~/.cargo/bin`. - **Exits while loading rust-src:** if rust-analyzer crashes during stdlib indexing, reinstall the source component: ```bash rustup component remove rust-src && rustup component add rust-src ``` - **No proc-macro / build script support:** ensure the project builds with `cargo check`; rust-analyzer reuses the same toolchain. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/file.rs ```
-
-
swift
-
README.md 2.2 KB
# Swift — LSP setup - **Builtin server:** `sourcekit-lsp` — `sourcekit-lsp` - **Extensions:** `.swift .objc .objcpp` - **Install hint:** `Included with Xcode or the Swift toolchain` ## Install `sourcekit-lsp` ships with the Swift toolchain — no separate install. - **macOS:** `xcode-select --install` (or install full Xcode). It resolves to the active toolchain selected by `xcode-select`. - **Linux:** Install a swift.org toolchain (`sourcekit-lsp` ships inside it); add the toolchain's `usr/bin` to PATH. - **Windows:** Install the swift.org Windows toolchain; `sourcekit-lsp` is bundled. Confirm it resolves: ```bash command -v sourcekit-lsp ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "sourcekit-lsp": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) None commonly required. For best results the project needs a **SwiftPM `Package.swift`** or a `compile_commands.json` compilation database so the server can resolve modules. Pure-Xcode-project files without these resolve poorly. ## Alternatives - **No mainstream alternative.** `sourcekit-lsp` is the official Apple/swift.org server and the only practical choice. ## Troubleshooting - **PATH:** `sourcekit-lsp` on PATH; reopen shell after install (or after `xcode-select -s`). - **Wrong toolchain (macOS):** point `xcode-select` at the right Xcode/toolchain; mismatches cause stale or missing results. - **No `Package.swift` / compile db:** add a SwiftPM manifest or generate `compile_commands.json` for accurate indexing. - **Objective-C (`.objc`/`.objcpp`):** needs a compilation database to resolve headers and frameworks. - **First build slow:** the server builds the module graph on first open; wait for it. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/File.swift ```
-
-
terraform
-
README.md 2.2 KB
# Terraform — LSP setup - **Builtin server:** `terraform` — `terraform-ls serve` - **Extensions:** `.tf .tfvars` - **Install hint:** See https://github.com/hashicorp/terraform-ls An alias id `terraform-ls` exists with the identical command; either id works. ## Install - **macOS:** `brew install hashicorp/tap/terraform-ls` - **Linux:** download a release from https://github.com/hashicorp/terraform-ls/releases and place `terraform-ls` on PATH (or `apt`/`dnf` via the HashiCorp repo) - **Windows:** `choco install terraform-ls` (or download a release zip) `terraform-ls` requires the `terraform` binary itself to be installed and on PATH. Confirm both resolve: ```bash command -v terraform-ls command -v terraform ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "terraform": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) None commonly required. Provider/module completion comes from schemas generated by `terraform init`. Run it in each module root before expecting full completion: ```bash terraform init ``` To force schema indexing, you can set experimental features via `initialization`: ```json { "lsp": { "terraform": { "initialization": { "experimentalFeatures": { "validateOnSave": true } } } } } ``` ## Alternatives - `tflint` standalone for opinionated linting (complements the LSP). - `terraform fmt` for formatting. ## Troubleshooting - **PATH:** `terraform-ls` AND `terraform` both on PATH; reopen shell after install. - **No provider completion:** run `terraform init` so the `.terraform/` schema cache exists. - **`.tfvars` not analyzed:** open the containing module so the server has root context. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/file.tf ```
-
-
typescript
-
README.md 3.2 KB
# TypeScript / JavaScript — LSP setup - **Builtin server:** `typescript` — `typescript-language-server --stdio` - **Extensions:** `.ts .tsx .js .jsx .mjs .cjs .mts .cts` - **Install hint:** `npm install -g typescript-language-server typescript` ## Install - **macOS:** `npm install -g typescript-language-server typescript` - **Linux:** `npm install -g typescript-language-server typescript` - **Windows:** `npm install -g typescript-language-server typescript` (PowerShell or cmd) `typescript-language-server` is only a thin wrapper — it needs the `typescript` package (`tsserver`) present too, either globally or in the project's `node_modules`. Always install both. Confirm it resolves: ```bash command -v typescript-language-server ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "typescript": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) None commonly required. To favor inlay hints or tweak preferences: ```json { "lsp": { "typescript": { "initialization": { "preferences": { "includeInlayParameterNameHints": "all" } } } } } ``` ## Alternatives All builtin — pick by toolchain. Raise the alternative's `priority` and/or `disabled` the default so the file routes to your choice: | id | command | when to choose | | ------------ | ---------------------------------------- | --------------------------------------- | | `deno` | `deno lsp` | Deno projects (handles `.ts/.tsx/.js`) | | `biome` | `biome lsp-proxy --stdio` | Biome lint/format as the LSP | | `eslint` | `vscode-eslint-language-server --stdio` | ESLint diagnostics (install below) | | `oxlint` | `oxlint --lsp` | fast Oxc-based linting | | `vue` | `vue-language-server --stdio` | `.vue` single-file components | | `svelte` | `svelteserver --stdio` | `.svelte` files | | `astro` | `astro-ls --stdio` | `.astro` files | `eslint` install: `npm i -g vscode-langservers-extracted`. Pick Deno or Biome over the default: ```json { "lsp": { "typescript": { "disabled": true }, "deno": { "priority": 100 } } } ``` (Swap `"deno"` for `"biome"` to use Biome instead.) ## Troubleshooting - **PATH:** `typescript-language-server` must be on PATH; reopen shell after `npm i -g`. Check your global bin with `npm bin -g`. - **Missing tsserver:** errors like "Could not find tsserver" mean the `typescript` package is absent — install it globally or in the project. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/file.ts ```
-
-
yaml
-
README.md 2.3 KB
# YAML — LSP setup - **Builtin server:** `yaml-ls` — `yaml-language-server --stdio` - **Extensions:** `.yaml .yml` - **Install hint:** `npm install -g yaml-language-server` ## Install - **macOS:** `npm install -g yaml-language-server` - **Linux:** `npm install -g yaml-language-server` - **Windows:** `npm install -g yaml-language-server` (PowerShell) Confirm it resolves: ```bash command -v yaml-language-server ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "yaml-ls": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) Schema association is the main reason to configure yaml-ls. Map globs to a schema URL or local path under `yaml.schemas`: ```json { "lsp": { "yaml-ls": { "initialization": { "yaml": { "schemas": { "https://json.schemastore.org/github-workflow.json": ".github/workflows/*.yml", "https://json.schemastore.org/kustomization.json": "kustomization.yaml", "./schemas/my-config.schema.json": "config/*.yaml" }, "validate": true, "completion": true, "format": { "enable": true } } } } } } ``` Set `"yaml.schemaStore": { "enable": true }` to auto-resolve schemas from SchemaStore (catalog at https://www.schemastore.org/). Inline `# yaml-language-server: $schema=<url>` modelines also work without config. ## Alternatives - `redhat.vscode-yaml` bundles the same server in editors. - `yamllint` standalone for style/lint-only checks. ## Troubleshooting - **PATH:** `yaml-language-server` on PATH; reopen shell after `npm -g` install. - **No validation:** no schema matched — add a `yaml.schemas` glob or a `$schema` modeline. - **Wrong schema applied:** SchemaStore guessed by filename; pin explicitly under `yaml.schemas`. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/file.yaml ```
-
-
zig
-
README.md 1.7 KB
# Zig — LSP setup - **Builtin server:** `zls` — `zls` - **Extensions:** `.zig .zon` - **Install hint:** `https://github.com/zigtools/zls` ## Install ZLS (the Zig Language Server) must be built against the **same Zig version** you use. See `https://github.com/zigtools/zls`. - **macOS:** `brew install zls` - **Linux:** download a prebuilt release matching your Zig version, or `zig build -Doptimize=ReleaseSafe` from the zls source - **Windows:** download the matching release from the zls GitHub releases, or build from source Confirm it resolves: ```bash command -v zls ``` ## Configure Builtin — usually NO config needed (auto-resolved by extension). Configure only to set priority, init options, override extensions, or disable. Same JSON shape in `.codex/lsp-client.json` (Codex) AND `.opencode/lsp.json` (OpenCode/omo): ```json { "lsp": { "zls": { "priority": 100 } } } ``` For builtin ids in a PROJECT config, `command` is supplied automatically — only set `priority`/`initialization`/`extensions`/`disabled`/`env`. A fully custom (non-builtin) server with its own `command` must go in the USER config (`~/.codex/lsp-client.json`). ### Initialization options (only if commonly needed) None commonly required. ## Alternatives None. ## Troubleshooting - **VERSION MATCH (critical):** zls version MUST match your zig version — build/install zls against the exact same Zig. A mismatch causes crashes, parse errors, or silent failures. After upgrading Zig, upgrade/rebuild zls too. - **PATH:** `zls` must be on PATH; reopen the shell after install. - **zig not found:** zls invokes `zig` for builds — make sure `zig` itself is also on PATH. ## Verify ```bash bun ../../scripts/verify-lsp.ts path/to/file.zig ```
-
-
-
scripts
-
detect-lsp.ts 6.5 KB
#!/usr/bin/env bun // detect-lsp.ts <targetDir> [--json] — scan a directory for source languages and // report, per detected language: the builtin LSP server, whether its executable // is on PATH, an install hint, and whether a project LSP config references it. import { existsSync, readFileSync, readdirSync, statSync } from "node:fs" import { delimiter, extname, join, sep } from "node:path" import process from "node:process" import { LANGUAGES, type LanguageServer, PROJECT_CONFIG_FILES } from "./lsp-server-table" const SKIP_DIRECTORIES = new Set<string>([ "node_modules", ".git", "dist", "build", ".next", "out", "target", ".venv", "venv", "vendor", ".cache", "__pycache__", ".turbo", "coverage", ]) const MAX_FILES = 50_000 // Mirrors effectiveExtension() in packages/lsp-tools-mcp/src/lsp/effective-extension.ts: // extensionless Dockerfile/Containerfile resolve to .dockerfile (exact-case basenames). const BASENAME_EXTENSIONS: Record<string, string> = { Dockerfile: ".dockerfile", Containerfile: ".dockerfile", } interface ConfigFileState { readonly path: string readonly exists: boolean readonly serverIds: readonly string[] } interface DetectionResult { readonly server: LanguageServer readonly executable: string readonly installed: boolean readonly resolvedPath: string | null readonly configuredIn: readonly string[] } function collectExtensions(root: string): ReadonlySet<string> { const found = new Set<string>() const stack: string[] = [root] let visited = 0 while (stack.length > 0) { const current = stack.pop() if (current === undefined) break let entries: string[] try { entries = readdirSync(current) } catch { continue } for (const entry of entries) { if (visited >= MAX_FILES) return found const fullPath = join(current, entry) let kind: "dir" | "file" | "other" = "other" try { const stat = statSync(fullPath) kind = stat.isDirectory() ? "dir" : stat.isFile() ? "file" : "other" } catch { continue } if (kind === "dir") { if (!SKIP_DIRECTORIES.has(entry)) stack.push(fullPath) } else if (kind === "file") { visited += 1 const ext = (BASENAME_EXTENSIONS[entry] ?? extname(entry)).toLowerCase() if (ext.length > 0) found.add(ext) } } } return found } function pathDirectories(): readonly string[] { return (process.env["PATH"] ?? "").split(delimiter).filter((dir: string) => dir.length > 0) } function resolveExecutable(command: string): string | null { const extensions = process.platform === "win32" ? (process.env["PATHEXT"]?.split(";") ?? [".EXE", ".CMD", ".BAT"]) : [""] const bases = command.includes("/") || command.includes(sep) ? [command] : pathDirectories().map((dir: string) => join(dir, command)) for (const base of bases) { for (const ext of extensions) { const candidate = ext.length > 0 ? `${base}${ext}` : base try { if (existsSync(candidate) && statSync(candidate).isFile()) return candidate } catch { continue } } } return null } function isRecord(value: unknown): value is Record<string, unknown> { return typeof value === "object" && value !== null && !Array.isArray(value) } function parseConfiguredServerIds(path: string): readonly string[] { let parsed: unknown try { parsed = JSON.parse(readFileSync(path, "utf-8")) } catch { return [] } if (!isRecord(parsed)) return [] const lsp = parsed["lsp"] return isRecord(lsp) ? Object.keys(lsp) : [] } function readConfigState(root: string): readonly ConfigFileState[] { return PROJECT_CONFIG_FILES.map((relative: string): ConfigFileState => { const path = join(root, relative) if (!existsSync(path)) return { path: relative, exists: false, serverIds: [] } return { path: relative, exists: true, serverIds: parseConfiguredServerIds(path) } }) } function detect(root: string, configState: readonly ConfigFileState[]): readonly DetectionResult[] { const extensions = collectExtensions(root) const results: DetectionResult[] = [] for (const server of LANGUAGES) { if (!server.extensions.some((ext: string) => extensions.has(ext))) continue const executable = server.command[0] ?? server.serverId const resolvedPath = resolveExecutable(executable) const configuredIn = configState .filter((state) => state.serverIds.includes(server.serverId)) .map((state) => state.path) results.push({ server, executable, installed: resolvedPath !== null, resolvedPath, configuredIn }) } return results } function renderReport(root: string, results: readonly DetectionResult[], configState: readonly ConfigFileState[]): string { const lines: string[] = [`LSP setup scan: ${root}`] const configSummary = configState .map((state) => `${state.path} (${state.exists ? `present: ${state.serverIds.length} server(s)` : "absent"})`) .join(", ") lines.push(`Config files: ${configSummary}`, "") if (results.length === 0) { lines.push("No languages with a builtin LSP server were detected here.") return lines.join("\n") } lines.push("DETECTED LANGUAGES (primary builtin server per language)") for (const result of results) { const mark = result.installed ? "OK " : "MISS" const state = result.installed ? `installed (${result.resolvedPath})` : "NOT installed" const config = result.configuredIn.length > 0 ? `configured in ${result.configuredIn.join(", ")}` : "builtin-default" lines.push(`[${mark}] ${result.server.language.padEnd(12)} server=${result.server.serverId} exe=${result.executable} ${state} ${config}`) if (!result.installed) lines.push(` install: ${result.server.installHint}`) } const missing = results.filter((result) => !result.installed) lines.push( "", missing.length === 0 ? `All ${results.length} detected server(s) installed.` : `${missing.length}/${results.length} server(s) NOT installed: ${missing.map((m) => m.server.language).join(", ")}`, "Next: read references/<language>/README.md, then configure .codex/lsp-client.json AND .opencode/lsp.json.", ) return lines.join("\n") } function main(): void { const args = process.argv.slice(2) const wantsJson = args.includes("--json") const root = args.find((arg: string) => !arg.startsWith("--")) ?? process.cwd() if (!existsSync(root)) { process.stderr.write(`detect-lsp: target directory does not exist: ${root}\n`) process.exit(2) } const configState = readConfigState(root) const results = detect(root, configState) if (wantsJson) { process.stdout.write(`${JSON.stringify({ root, configState, results }, null, 2)}\n`) return } process.stdout.write(`${renderReport(root, results, configState)}\n`) } main() -
lsp-server-table.ts 5.1 KB
// SOURCE OF TRUTH: packages/lsp-tools-mcp/src/lsp/server-definitions.ts // (BUILTIN_SERVERS + LSP_INSTALL_HINTS). This is a hand-maintained snapshot of // the primary builtin server per reference language, embedded so detect-lsp.ts // runs standalone in any user project. Mirror command/extensions when that file // changes. export interface LanguageServer { readonly language: string readonly serverId: string readonly command: readonly string[] readonly extensions: readonly string[] readonly installHint: string } export const LANGUAGES: readonly LanguageServer[] = [ { language: "typescript", serverId: "typescript", command: ["typescript-language-server", "--stdio"], extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"], installHint: "npm install -g typescript-language-server typescript", }, { language: "python", serverId: "basedpyright", command: ["basedpyright-langserver", "--stdio"], extensions: [".py", ".pyi"], installHint: "pip install basedpyright (or: uv tool install basedpyright)", }, { language: "go", serverId: "gopls", command: ["gopls"], extensions: [".go"], installHint: "go install golang.org/x/tools/gopls@latest", }, { language: "rust", serverId: "rust", command: ["rust-analyzer"], extensions: [".rs"], installHint: "rustup component add rust-analyzer", }, { language: "c-cpp", serverId: "clangd", command: ["clangd", "--background-index", "--clang-tidy"], extensions: [".c", ".cpp", ".cc", ".cxx", ".c++", ".h", ".hpp", ".hh", ".hxx", ".h++"], installHint: "macOS: brew install llvm | Linux: apt install clangd | https://clangd.llvm.org/installation", }, { language: "java", serverId: "jdtls", command: ["jdtls"], extensions: [".java"], installHint: "macOS: brew install jdtls | https://github.com/eclipse-jdtls/eclipse.jdt.ls", }, { language: "kotlin", serverId: "kotlin-ls", command: ["kotlin-lsp"], extensions: [".kt", ".kts"], installHint: "https://github.com/Kotlin/kotlin-lsp", }, { language: "csharp", serverId: "csharp", command: ["csharp-ls"], extensions: [".cs"], installHint: "dotnet tool install -g csharp-ls", }, { language: "razor", serverId: "razor", command: ["roslyn-language-server", "--stdio"], extensions: [".razor", ".cshtml"], installHint: "dotnet tool install -g roslyn-language-server --prerelease (see references/csharp/README.md)", }, { language: "swift", serverId: "sourcekit-lsp", command: ["sourcekit-lsp"], extensions: [".swift", ".objc", ".objcpp"], installHint: "Included with Xcode (xcode-select --install) or the Swift toolchain", }, { language: "ruby", serverId: "ruby-lsp", command: ["rubocop", "--lsp"], extensions: [".rb", ".rake", ".gemspec", ".ru"], installHint: "gem install ruby-lsp (builtin runs `rubocop --lsp`; gem install rubocop)", }, { language: "php", serverId: "php", command: ["intelephense", "--stdio"], extensions: [".php"], installHint: "npm install -g intelephense", }, { language: "dart", serverId: "dart", command: ["dart", "language-server", "--lsp"], extensions: [".dart"], installHint: "Included with the Dart/Flutter SDK", }, { language: "elixir", serverId: "elixir-ls", command: ["elixir-ls"], extensions: [".ex", ".exs"], installHint: "https://github.com/elixir-lsp/elixir-ls", }, { language: "zig", serverId: "zls", command: ["zls"], extensions: [".zig", ".zon"], installHint: "https://github.com/zigtools/zls (match zls version to your zig version)", }, { language: "lua", serverId: "lua-ls", command: ["lua-language-server"], extensions: [".lua"], installHint: "macOS: brew install lua-language-server | https://github.com/LuaLS/lua-language-server", }, { language: "bash", serverId: "bash", command: ["bash-language-server", "start"], extensions: [".sh", ".bash", ".zsh", ".ksh"], installHint: "npm install -g bash-language-server", }, { language: "yaml", serverId: "yaml-ls", command: ["yaml-language-server", "--stdio"], extensions: [".yaml", ".yml"], installHint: "npm install -g yaml-language-server", }, { language: "terraform", serverId: "terraform", command: ["terraform-ls", "serve"], extensions: [".tf", ".tfvars"], installHint: "macOS: brew install hashicorp/tap/terraform-ls | https://github.com/hashicorp/terraform-ls", }, { language: "dockerfile", serverId: "dockerfile", command: ["docker-langserver", "--stdio"], extensions: [".dockerfile"], installHint: "npm install -g dockerfile-language-server-nodejs", }, { language: "haskell", serverId: "haskell-language-server", command: ["haskell-language-server-wrapper", "--lsp"], extensions: [".hs", ".lhs"], installHint: "ghcup install hls", }, { language: "julia", serverId: "julials", command: ["julia", "--startup-file=no", "--history-file=no", "-e", "using LanguageServer; runserver()"], extensions: [".jl"], installHint: "julia -e 'using Pkg; Pkg.add(\"LanguageServer\")' (see references/julia/README.md)", }, ] as const export const PROJECT_CONFIG_FILES: readonly string[] = [ ".codex/lsp-client.json", ".opencode/lsp.json", ".omo/lsp.json", ".omo/lsp-client.json", ] as const -
tsconfig.json 397 B
{ "compilerOptions": { "target": "ESNext", "module": "ESNext", "moduleResolution": "bundler", "strict": true, "noUncheckedIndexedAccess": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "noEmit": true, "lib": ["ESNext"], "types": ["bun-types"] }, "include": ["*.ts"] } -
verify-lsp.ts 5.1 KB
#!/usr/bin/env bun // verify-lsp.ts <file> [--timeout=ms] — perform a real LSP diagnostics roundtrip // for <file> through the lsp-tools-mcp engine and report ok/fail with error text. // The engine source is located by walking up from this script and the cwd, so // run it inside the omo repo/worktree (where packages/lsp-tools-mcp/src exists). import { existsSync, statSync } from "node:fs" import { dirname, isAbsolute, join, resolve } from "node:path" import process from "node:process" import { fileURLToPath, pathToFileURL } from "node:url" const ENGINE_TOOLS = "packages/lsp-tools-mcp/src/tools.ts" const ENGINE_CONTEXT = "packages/lsp-tools-mcp/src/request-context.ts" const ENGINE_MANAGER = "packages/lsp-tools-mcp/src/lsp/manager.ts" const DEFAULT_TIMEOUT_MS = 60_000 interface ToolExecutionResult { readonly content: ReadonlyArray<{ readonly type: "text"; readonly text: string }> readonly isError?: boolean readonly details?: unknown } interface DiagnosticsDetails { readonly mode: "file" | "directory" readonly totalDiagnostics: number readonly error?: string readonly errorKind?: "missing_dependency" | "no_files" | "invalid_path" } interface ToolsModule { readonly executeLspDiagnostics: (params: Record<string, unknown>, signal?: AbortSignal) => Promise<ToolExecutionResult> } interface ContextModule { readonly runWithRequestContext: <T>(context: { cwd?: string; env?: Record<string, string> }, fn: () => T) => T } interface ManagerModule { readonly disposeDefaultLspManager: () => Promise<void> } function findUp(relativeTarget: string): string | null { const starts = [dirname(fileURLToPath(import.meta.url)), process.cwd()] for (const start of starts) { let current = start while (true) { const candidate = join(current, relativeTarget) if (existsSync(candidate)) return candidate const parent = dirname(current) if (parent === current) break current = parent } } return null } function buildEnv(): Record<string, string> { const env: Record<string, string> = {} for (const [key, value] of Object.entries(process.env)) { if (value !== undefined) env[key] = value } return env } function isDiagnosticsDetails(value: unknown): value is DiagnosticsDetails { return typeof value === "object" && value !== null && "mode" in value && "totalDiagnostics" in value } async function loadModule<T>(relativeTarget: string): Promise<T> { const path = findUp(relativeTarget) if (path === null) { throw new EngineNotFoundError(relativeTarget) } return (await import(pathToFileURL(path).href)) as T } class EngineNotFoundError extends Error { constructor(public readonly target: string) { super(`lsp-tools-mcp engine not found (looked for ${target}). Run verify-lsp.ts inside the omo repo/worktree.`) this.name = "EngineNotFoundError" } } function parseTimeout(args: readonly string[]): number { const flag = args.find((arg) => arg.startsWith("--timeout=")) if (flag === undefined) return DEFAULT_TIMEOUT_MS const parsed = Number.parseInt(flag.slice("--timeout=".length), 10) return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_TIMEOUT_MS } async function run(filePath: string, timeoutMs: number): Promise<number> { const tools = await loadModule<ToolsModule>(ENGINE_TOOLS) const context = await loadModule<ContextModule>(ENGINE_CONTEXT) const manager = await loadModule<ManagerModule>(ENGINE_MANAGER) const absolute = isAbsolute(filePath) ? filePath : resolve(process.cwd(), filePath) try { const signal = AbortSignal.timeout(timeoutMs) const result = await context.runWithRequestContext({ cwd: process.cwd(), env: buildEnv() }, () => tools.executeLspDiagnostics({ filePath: absolute, severity: "all" }, signal), ) const details = isDiagnosticsDetails(result.details) ? result.details : null const text = result.content.map((part) => part.text).join("\n") if (details?.errorKind === "missing_dependency") { process.stdout.write(`FAIL ${absolute}: language server not installed\n${text}\n`) return 1 } if (result.isError === true || details?.errorKind === "invalid_path" || details?.errorKind === "no_files") { process.stdout.write(`FAIL ${absolute}: ${details?.error ?? text}\n`) return 1 } const count = details?.totalDiagnostics ?? 0 process.stdout.write(`OK ${absolute}: LSP roundtrip succeeded (${count} diagnostic(s))\n${text}\n`) return 0 } finally { await manager.disposeDefaultLspManager() } } async function main(): Promise<void> { const args = process.argv.slice(2) const filePath = args.find((arg) => !arg.startsWith("--")) if (filePath === undefined) { process.stderr.write("Usage: bun verify-lsp.ts <file> [--timeout=ms]\n") process.exit(2) } if (!existsSync(filePath) || !statSync(filePath).isFile()) { process.stderr.write(`verify-lsp: not a file: ${filePath}\n`) process.exit(2) } try { const code = await run(filePath, parseTimeout(args)) process.exit(code) } catch (error) { if (error instanceof EngineNotFoundError) { process.stderr.write(`SKIP: ${error.message}\n`) process.exit(3) } process.stderr.write(`FAIL ${filePath}: ${error instanceof Error ? error.message : String(error)}\n`) process.exit(1) } } await main()
-
-
SKILL.md 5.4 KB
--- name: lsp-setup description: "Configures a language server so editor/agent tooling (diagnostics, go-to-definition, references, rename) works. Use when a project needs an LSP installed or wired, or a 'no LSP server configured' error appears." --- # LSP Setup Configure the right Language Server for a project so the `lsp` MCP tools (`diagnostics`, `goto_definition`, `find_references`, `symbols`, `rename`) actually work. This skill is an index: detect what a project needs, install the server, write the config, then verify with a real roundtrip. The list of servers we ship as **builtin** is the source of truth in `packages/lsp-tools-mcp/src/lsp/server-definitions.ts` (`BUILTIN_SERVERS` + `LSP_INSTALL_HINTS`). The per-language references below mirror it. --- ## PHASE 0 — LANGUAGE GATE (run first) Identify the language from the file extension, then **read the matching reference before installing or configuring anything**. | Extension(s) | Reference | |---|---| | `.ts .tsx .js .jsx .mjs .cjs .mts .cts .vue .svelte .astro` | `references/typescript/README.md` | | `.py .pyi` | `references/python/README.md` | | `.go` | `references/go/README.md` | | `.rs` | `references/rust/README.md` | | `.c .cpp .cc .cxx .h .hpp .hh .hxx` | `references/c-cpp/README.md` | | `.java` | `references/java/README.md` | | `.kt .kts` | `references/kotlin/README.md` | | `.cs .razor .cshtml` | `references/csharp/README.md` | | `.swift` | `references/swift/README.md` | | `.rb .rake .gemspec .ru` | `references/ruby/README.md` | | `.php` | `references/php/README.md` | | `.dart` | `references/dart/README.md` | | `.ex .exs` | `references/elixir/README.md` | | `.zig .zon` | `references/zig/README.md` | | `.lua` | `references/lua/README.md` | | `.sh .bash .zsh .ksh` | `references/bash/README.md` | | `.yaml .yml` | `references/yaml/README.md` | | `.tf .tfvars` | `references/terraform/README.md` | | `.hs .lhs` | `references/haskell/README.md` | | `.jl` | `references/julia/README.md` | --- ## WORKFLOW — detect → install → configure → verify ### 1. Detect Scan the project to see which languages are present and whether each server is installed and configured: ```bash bun scripts/detect-lsp.ts <projectDir> # human report (default: cwd) bun scripts/detect-lsp.ts <projectDir> --json ``` For each detected language it prints the builtin server id, the executable it needs on `PATH`, whether that executable is installed, an install hint, and whether a project config file already references it. ### 2. Install Open `references/<language>/README.md` and run the install command for your OS. Then confirm the executable resolves: ```bash command -v <server-executable> # e.g. typescript-language-server, gopls, rust-analyzer ``` ### 3. Configure Most builtin servers need **no config** — they are resolved automatically by file extension. Write config only to: pick between competing servers, set a `priority`, pass `initialization` options, override `extensions`, set `env`, or `disable` a server. Two project-scoped config files, **identical JSON shape**: - Codex harness → `.codex/lsp-client.json` (user: `~/.codex/lsp-client.json`) - OpenCode/omo harness → `.opencode/lsp.json` (also `.omo/lsp.json`) ```jsonc { "lsp": { "<server-id>": { "command": ["<bin>", "<args>"], // optional for builtin ids (supplied automatically) "extensions": [".ext"], // optional override "priority": 100, // higher wins when several servers match an extension "initialization": { }, // server-specific initializationOptions "env": { "KEY": "value" }, // optional "disabled": false // set true to turn a server off } } } ``` Rules enforced by `config-loader.ts`: - In a **project** config (`.codex/lsp-client.json`, `.opencode/lsp.json`) an entry whose id is a **builtin** server inherits `command` automatically — you only override `extensions` / `priority` / `initialization`. A non-builtin id in a project config is **ignored**. - To define a **fully custom** (non-builtin) server with its own `command`, put it in the **user** config (`~/.codex/lsp-client.json`, or the path set by `LSP_TOOLS_MCP_USER_CONFIG`), where `command` + `extensions` are honored. - Project entries win over user entries; both win over builtin defaults. Each language reference gives a ready-to-paste snippet. ### 4. Verify Run a real diagnostics roundtrip against a source file. This spawns the server, opens the file, requests diagnostics, and reports `OK`/`FAIL`: ```bash bun scripts/verify-lsp.ts <path/to/file.ext> bun scripts/verify-lsp.ts <file> --timeout=90000 ``` `OK` = the server started and answered. `FAIL: language server not installed` = go back to step 2. Other `FAIL` text carries the server/startup error. `SKIP` = the engine source could not be located; run from inside the omo repo/worktree, or call the `lsp` MCP `diagnostics` tool directly. --- ## Scripts | Script | Purpose | |---|---| | `scripts/detect-lsp.ts` | Scan a directory; per detected language report server id, install status, install hint, config status. `--json` for machine output. | | `scripts/verify-lsp.ts` | Real LSP diagnostics roundtrip for one file via the `lsp-tools-mcp` engine; `OK`/`FAIL`/`SKIP` + exit code 0/1/3. | | `scripts/lsp-server-table.ts` | Embedded snapshot of the primary builtin server per language (mirrors `server-definitions.ts`). | Run with [Bun](https://bun.sh): `curl -fsSL https://bun.sh/install | bash`.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.