# Language Server Protocol plugin

The LSP plugin gives coding agents compiler diagnostics, symbol definitions, references,
and approved renames or formatting. Install the plugin and a language server separately;
Jazz does not bundle a language server. Servers must support LSP 3.17 over stdio.

Install the published plugin through the normal [plugin lifecycle](./plugins.md). No repository
clone or local build is needed; the published artifact includes its diff renderer:

```sh
jazz plugin add com.jazz.plugins.lsp
jazz plugin inspect com.jazz.plugins.lsp
jazz plugin trust com.jazz.plugins.lsp
jazz plugin enable com.jazz.plugins.lsp --agent default
```

Replace `default` with your agent name or ID, or omit `--agent` to enable it for every agent.
Install your language-server executable separately and configure its command in `~/.jazz/lsp.json`:

```json
{
  "servers": [
    {
      "id": "typescript",
      "command": "typescript-language-server",
      "args": ["--stdio"],
      "extensions": {
        ".ts": "typescript",
        ".tsx": "typescriptreact"
      },
      "rootMarkers": ["tsconfig.json", "package.json"]
    },
    {
      "id": "javascript",
      "command": "typescript-language-server",
      "args": ["--stdio"],
      "extensions": {
        ".js": "javascript",
        ".jsx": "javascriptreact"
      },
      "rootMarkers": ["jsconfig.json", "package.json"]
    }
  ]
}
```

For HTML and CSS, use `vscode-langservers-extracted` (`npm install --global
vscode-langservers-extracted`) with `command: "vscode-html-language-server"` for
`.html`/`.htm` and `command: "vscode-css-language-server"` for `.css`, both with
`args: ["--stdio"]`. The HTML server also reports diagnostics for CSS inside inline
`<style>` blocks. Both use the LSP 3.17 pull model; the plugin queries
textDocument/diagnostic directly. Avoid the older
`vscode-html-languageserver-bin` / `vscode-css-languageserver-bin` packages: their
pinned `vscode-jsonrpc` crashes at startup (`messageReader.onClose is not a
function`). See the [plugin README](../../plugins/lsp/README.md) for a complete
`lsp.json` example.
Install `typescript-language-server` and a compatible `typescript` package containing
`tsserver.js` before using this example. To configure another
language, add an entry with its executable, argument vector, an extensions-to-language-ID
map, and project-root markers. Each extension is bound to exactly one language ID because the
language decides how the server parses the file: `.tsx` and `.jsx` must open as the React
dialects (`typescriptreact`, `javascriptreact`), never as plain `typescript` or `javascript`. A
legacy top-level `languageId` is rejected at load time with a message naming the replacement.
The first entry whose extensions name a file's extension wins. The
plugin walks upward from the current directory to identify configured project roots and starts
their servers before the first model request. It also starts a matching server when Jazz later
touches a supported file outside those roots. If no marker exists, it uses the agent's current
directory. `JAZZ_LSP_CONFIG` can point to another JSON file. A missing or invalid config is
reported to the agent.

The plugin reads source files from disk and sends `didOpen` and `didChange` as Jazz works with them.
It checks tracked files again before each model request, including after external edits. It keeps
diagnostics bounded and relevant to those files; it does not pour whole-workspace diagnostics into
every turn. A failed server start is retried after a short delay rather than on every model request;
an unavailable server does not stop the coding run. The model cannot choose the
executable or its arguments.

The tools use **1-based** line and UTF-16 character coordinates. `code_actions` lists available
actions for a range. `apply_code_action` selects one by its exact title, with a zero-based `index`
when titles repeat, and resolves lazy edits when the server supports it. Actions without an edit
are rejected. `rename_symbol`, `apply_code_action`, and `format_document` turn the returned
`WorkspaceEdit` into a multi-file diff for Jazz's normal approval flow. Jazz persists the prepared
edit if approval is parked. At execution, the plugin locks every target and checks its complete
content digest before replacing files; a changed file produces a stale-edit error and writes
nothing. It also rejects a server response if the source file changed while the server was
preparing the edit. Resource operations such as create, delete, and move are rejected explicitly. LSP
servers' unsolicited `workspace/applyEdit` requests are refused.

Semantic query tools are declared `read-only`. Rename, code action application, and formatting are
declared `high-risk` and use Jazz's approval gate. The configured server command is part of the
operator's plugin setup and trust decision, not a model-authored shell command. The operator must
trust that executable, just as with any plugin that runs local code. The server receives source
contents from files Jazz reads or changes during the agent's run, as well as files the agent
explicitly queries through an LSP tool. Its stdout is untrusted protocol data;
code action commands are never executed. The plugin performs no network egress itself, but a
configured server may have its own network behavior. Review the server before enabling the
plugin on a remote or unattended surface.

The current edit path validates all files before writing and uses sibling temporary files plus
atomic per-file renames. A multi-file edit is not one filesystem transaction: an I/O failure
during replacement can leave a partial set of files changed. External programs that ignore
Jazz's edit locks can also race after validation.