Files
cleveragents-core/docs/reference/lsp.md
T
freemo efc11022ab docs: update CHANGELOG and reference docs for session 3377
- CHANGELOG: add CI log artifacts (PR #2782), provider benchmarks (PR #3022)
  to [Unreleased] Added section
- CHANGELOG: add LSP restart_server() deadlock fix (PR #3165) to [Unreleased]
  Fixed section
- docs/reference/lsp.md: document LspLifecycleManager 3-phase lock concurrency
  model for restart_server() thread safety
- docs/development/ci-cd.md: document CI log artifact names, download API, and
  agent usage patterns

ISSUES CLOSED: #3377
2026-04-28 09:25:02 +00:00

6.1 KiB

LSP Integration

Language Server Protocol (LSP) servers are registered in a global LSP Registry (namespaced like all other entities) and bound to actor graph nodes via YAML configuration. When an actor activates, the LSP Runtime starts the appropriate language servers. LSP capabilities (diagnostics, type info, symbol navigation, completions, references, rename, code actions) are exposed to the actor as callable tools (via the LspToolAdapter) and as automatic context enrichment.

Local-Mode Constraints

In local mode the LSP subsystem operates as a set of stubs:

Component Local-Mode Behavior
LspRegistry Fully functional — register, list, remove servers
LspRuntime Raises LspNotAvailableError on every operation
LspToolAdapter Generates tool specs whose handlers always raise

The registry is available for configuration and validation purposes even in local mode. The runtime and tool adapter become functional only when server mode is enabled.

Models

LspCapability

Enum of LSP features that can be exposed as tools:

Value Description
DIAGNOSTICS Retrieve diagnostics (errors/warnings)
HOVER Get type information and documentation
COMPLETIONS Get code completion suggestions
DEFINITIONS Go to the definition of a symbol
REFERENCES Find all references to a symbol
RENAME Rename a symbol across the workspace
CODE_ACTIONS Retrieve available code actions for a range
FORMATTING Format a document or selection
SIGNATURE_HELP Get function signature information at a call
DOCUMENT_SYMBOLS List all symbols in a file
WORKSPACE_SYMBOLS Search for symbols across the workspace

LspServerConfig

Registration record for a language server:

Field Type Description
name str Namespaced server name
languages list[str] Programming languages served
command str Executable command to start server
args list[str] Additional CLI arguments
env dict[str, str] Environment variables
capabilities list[LspCapability] Capabilities exposed by this server

LspBinding

Binds an LSP server to an actor graph node:

Field Type Description
node_name str Actor graph node name
lsp_server_name str Namespaced LSP server name
languages list[str] Subset of languages (empty = all)
auto_detect bool Auto-detect language from file context

Error Hierarchy

All errors inherit from CleverAgentsError:

CleverAgentsError
  └── LspError                  (base for all LSP errors)
        ├── LspNotAvailableError    (operation attempted in local mode)
        └── LspServerNotFoundError  (server not in registry)

CLI Commands

Command Description
agents lsp add Register server from YAML config
agents lsp remove Remove a registered server
agents lsp list List servers with optional filters
agents lsp show Show full server details

Examples

# Register a Pyright LSP server
agents lsp add --config ./lsp/pyright.yaml

# List all Python language servers
agents lsp list --language python

# Show details for a specific server
agents lsp show local/pyright

# Remove a server
agents lsp remove local/pyright --yes

YAML Configuration

name: local/pyright
command: pyright-langserver
args: ["--stdio"]
languages: ["python"]
capabilities:
  - diagnostics
  - completions
  - hover
  - definitions
  - references
  - rename
env:
  PYTHONPATH: ./src

Thread Safety

The LspRegistry uses a threading.RLock to ensure safe concurrent access. All public methods (register, get, list_servers, remove) acquire the lock before reading or mutating state.

Namespacing

LSP server names follow the project-wide namespacing pattern: [[server:]namespace/]name. For example:

  • local/pyright — a locally-defined Pyright server
  • devops/clangd — a clangd server in the devops namespace

LspLifecycleManager — Concurrency Model

The LspLifecycleManager uses a 3-phase lock pattern for all server lifecycle operations to prevent deadlocks under concurrent access:

Phase Lock held? What happens
1 short Read current state, snapshot fields, mutate _servers dict
2 none All blocking I/O: transport.stop(), transport.start(), client.initialize()
3 short Insert updated _ManagedServer into _servers

This pattern applies to both start_server() and restart_server(). Releasing the lock during Phase 2 ensures that concurrent callers of health_check(), list_running(), or stop_server() are never blocked for the duration of blocking I/O (which can take up to 60 seconds on timeout).

During a restart_server() call, the server is removed from _servers in Phase 1 and re-inserted in Phase 3. Concurrent callers will see the server as absent during the restart window — this is consistent with the start_server() contract. The ref_count from the original _ManagedServer is carried forward to the new instance.