Files
cleveragents-core/docs/api/lsp.md

9.3 KiB

cleveragents.lsp — Language Server Protocol Integration

The cleveragents.lsp package wires Language Server Protocol (LSP) features into CleverAgents. It provides:

  • Pydantic models describing servers, transports, and bindings.
  • A thread-safe registry for language server configurations.
  • Runtime orchestration (start, health-check, restart, stop) with automatic reference counting.
  • A synchronous protocol client and lifecycle manager around stdio transports.
  • A tool adapter that exposes LSP capabilities to the generic tool system.
  • A multi-layer language discovery engine that selects servers for files.

All exports listed below are available directly from cleveragents.lsp.


Quick start

from cleveragents.lsp import (
    LanguageDiscovery,
    LspCapability,
    LspRegistry,
    LspRuntime,
    LspServerConfig,
    LspToolAdapter,
)

registry = LspRegistry()
registry.register(
    LspServerConfig(
        name="local/pyright",
        description="Pyright language server",
        command="pyright-langserver",
        languages=["python"],
        capabilities=[
            LspCapability.DIAGNOSTICS,
            LspCapability.COMPLETIONS,
            LspCapability.HOVER,
        ],
    )
)

runtime = LspRuntime(registry=registry)
runtime.start_server("local/pyright", workspace_path="/workspace")

adapter = LspToolAdapter(runtime)
pyright_tools = adapter.generate_tool_specs(registry.get_or_raise("local/pyright"))
print([spec["name"] for spec in pyright_tools])
# ['local/pyright/diagnostics', 'local/pyright/completions', 'local/pyright/hover']

language = LanguageDiscovery().detect_file_language("/workspace/app/models.py")
print(language)  # 'python'

Models

LspTransport

StrEnum describing how the runtime communicates with a server:

  • STDIO (default) — spawn the server and communicate via stdin/stdout JSON-RPC.
  • TCP — connect to a server listening on a TCP socket.

LspCapability

Enum of LSP features that can be surfaced as ToolSpecs. Members map directly to protocol operations:

Capability Description
DIAGNOSTICS Fetch diagnostics, warnings, and errors for a file.
HOVER Retrieve hover information (type hints, docs) at a position.
COMPLETIONS Request completion candidates at a position.
DEFINITIONS Go to definition for a symbol.
REFERENCES Find references to a symbol.
RENAME Request a rename operation across the workspace.
CODE_ACTIONS Obtain code actions for a range.
FORMATTING Format a document or selection.
SIGNATURE_HELP Fetch call signature help.
DOCUMENT_SYMBOLS List symbols defined in a document.
WORKSPACE_SYMBOLS Search for symbols across the workspace.

Note: The runtime currently provides concrete handlers for diagnostics, completions, hover, and definitions. Other capabilities raise LspNotAvailableError until their protocol flows are implemented.

LspServerConfig

Pydantic model describing a language server.

Field Type Description
name str Namespaced identifier (namespace/name); validated to contain a slash.
description str Optional human-readable description.
languages list[str] Lowercased language identifiers served by the process.
command str Executable used to start the server.
args list[str] Extra CLI arguments.
transport LspTransport Communication mode (STDIO by default).
env dict[str, str] Environment overrides for the process.
capabilities list[LspCapability] Enabled protocol features.
initialization dict[str, Any] initialize request initializationOptions.
workspace_settings dict[str, Any] Settings pushed after initialization.

Helper properties:

  • namespace — portion before the last /.
  • short_name — portion after the last /.
  • to_dict() — JSON-compatible serialisation respecting model validators.

LspBinding

Binds a server to an actor graph node. Used by the compiler/runtime to know which servers to activate. Validates that lsp_server_name is namespaced and supports optional per-node language filters.


Registry and discovery

LspRegistry

Thread-safe registry for LspServerConfig objects. Public methods acquire a re-entrant lock, making concurrent registration and lookup safe.

  • register(config) — add a new server; rejects duplicates.
  • get(name) — return a config or None when missing.
  • get_or_raise(name) — raise LspServerNotFoundError if absent.
  • list_servers(namespace=None, language=None) — filter by namespace prefix or supported language, sorted by name.
  • remove(name) — unregister a server; returns True if removed.

LanguageDiscovery

Four-layer language detection engine used by the runtime and compiler. Layers are consulted in priority order:

  1. File extension (.py, .tsx, …) via a built-in lookup table.
  2. Shebang inspection for ambiguous or extension-less files.
  3. Optional UKO (Universal Knowledge Ontology) classifications supplied at construction.
  4. Explicit project-level language declarations passed to the constructor.

Key methods:

  • detect_file_language(path) — returns a language ID (falls back to "plaintext"). Results are cached per path.
  • detect_directory_languages(path) — walk a tree and return sorted unique languages (ignoring plaintext).
  • invalidate(path) / invalidate_all() — manage the detection cache.
  • get_servers_for_language(language, configs) — helper to match registered servers to a detected language.

Runtime orchestration

LspLifecycleManager

Manages running server processes with reference counting. Guarantees that the underlying transport is started only once per server/workspace combination and shutdown occurs when the last reference is released. Provides:

  • start_server(config, workspace_path) — start or reuse a server, returning an LspClient.
  • stop_server(name) — release a reference; raises LspServerNotFoundError if the server was never acquired.
  • health_check(name) — return True if the process is alive.
  • restart_server(name) — restart a crashed server while preserving reference counts.
  • stop_all() / list_running() — maintenance helpers for graceful shutdown and observability.

LspClient

Synchronous JSON-RPC client layered on top of StdioTransport. Exposes typed methods for the protocol flows currently needed by CleverAgents:

  • initialize(workspace_path, initialization_options=None) and shutdown() for lifecycle management.
  • Document operations: did_open, did_close, get_diagnostics, get_completions, get_hover, get_definitions.
  • Accessors is_initialized and server_capabilities for telemetry or testing.

Internally the client keeps a bounded queue of notifications, correlates responses by ID, and raises LspError on timeouts or protocol failures.

LspRuntime

High-level facade combining the registry and lifecycle manager. It performs input validation, logs significant events, and exposes convenience methods:

  • start_server(name, workspace_path) / stop_server(name) — manage process lifecycle.
  • get_diagnostics, get_completions, get_hover, get_definitions — delegate to the underlying LspClient, reading file contents and translating between filesystem paths and LSP URIs.
  • activate_bindings(bindings, workspace_path) and deactivate_bindings(bindings) — start/stop all servers referenced by LspBinding objects produced during actor compilation.
  • stop_all() — forward to LspLifecycleManager.stop_all().

If a server crashes, LspRuntime attempts to restart it automatically before servicing the next request.

StdioTransport

Process wrapper that launches language servers via subprocess, reads/writes JSON-RPC messages, and exposes is_alive, start(), stop(), send_message(), read_message(). It is the default transport used by the lifecycle manager and client.


Tool adapter

LspToolAdapter

Generates tool specifications from LspServerConfig.capabilities so the standard tool runtime can invoke language-server functionality.

from cleveragents.lsp import LspToolAdapter

adapter = LspToolAdapter(runtime)
for spec in adapter.generate_tool_specs(config):
    tool_registry.register(spec)
  • When constructed with an LspRuntime, capability handlers delegate to runtime methods (diagnostics, completions, hover, definitions today). Missing runtime capabilities raise LspNotAvailableError with a structured payload.
  • Without a runtime (local/offline mode), generated handlers always raise LspNotAvailableError, signalling that remote execution is required.
  • Each spec includes a minimal JSON Schema input contract tailored to the capability (file-only, position-based, rename, or query-based).

Error types

The package exports structured exceptions for callers to handle:

  • LspError — generic protocol or transport failure.
  • LspNotAvailableError — capability invoked without an available runtime.
  • LspServerNotFoundError — registry or lifecycle lookup of an unknown server.

Catch these errors in higher layers to provide user-facing remediation (e.g. retry prompts, error banners in the TUI, or fallback tool behaviour).