|
|
|
@@ -1,105 +1,405 @@
|
|
|
|
|
# ADR-027: Language Server Protocol (LSP) Integration
|
|
|
|
|
|
|
|
|
|
**Status:** Accepted
|
|
|
|
|
**Date:** 2026-02-17
|
|
|
|
|
**Date:** 2026-02-20
|
|
|
|
|
**Supersedes:** None
|
|
|
|
|
**Author(s):** Jeffrey Phillips Freeman <Jeffrey.Freeman@CleverThis.com>
|
|
|
|
|
**Approver(s):** Jeffrey Phillips Freeman <Jeffrey.Freeman@CleverThis.com>
|
|
|
|
|
|
|
|
|
|
## Context
|
|
|
|
|
|
|
|
|
|
CleverAgents needs deep IDE integration for plan-aware workflows: validation feedback displayed inline, context exploration on hover, and code actions that trigger plan operations. Building bespoke plugins for each editor (VS Code, JetBrains, Neovim, Emacs, etc.) would be expensive and prone to divergence. The architecture requires a single, standard, editor-agnostic interface that connects to the CleverAgents backend and behaves identically across local and server modes.
|
|
|
|
|
Actors in CleverAgents that perform software development tasks — writing code, refactoring, reviewing, debugging — operate on source code as raw text. Without semantic understanding of the code they manipulate, actors lack the ability to resolve types, navigate symbol definitions, discover references, identify compilation errors, or understand the structural relationships within a codebase. Human developers gain these capabilities through IDE features powered by language servers. Actors need the same language intelligence to work effectively.
|
|
|
|
|
|
|
|
|
|
Building bespoke code analysis for each programming language would be prohibitively expensive and would perpetually lag behind the state of the art in language tooling. The **Language Server Protocol (LSP)** is an open standard that decouples language intelligence from any specific editor or tool — it defines a JSON-RPC interface through which any client can request diagnostics, type information, symbol navigation, completions, and more from a language-specific server process. The ecosystem already provides production-quality language servers for virtually every mainstream language (Pyright for Python, typescript-language-server for TypeScript/JavaScript, clangd for C/C++, rust-analyzer for Rust, gopls for Go, etc.).
|
|
|
|
|
|
|
|
|
|
CleverAgents should leverage this ecosystem directly, giving actors the same rich language intelligence that human developers enjoy — but integrated into the agent's reasoning loop and tool-calling pipeline rather than into an IDE.
|
|
|
|
|
|
|
|
|
|
## Decision
|
|
|
|
|
|
|
|
|
|
CleverAgents implements the IDE plugin as a **Language Server Protocol (LSP)** server. The LSP server surfaces plan-aware code actions, diagnostics, and context lookups, and communicates with the backend exclusively through ACP (ADR-026).
|
|
|
|
|
CleverAgents adopts LSP as the standard protocol for attaching **language intelligence capabilities** to actors and agents. LSP servers are registered in a global **LSP Registry** (namespaced like all other entities), and bound to specific actor graph nodes via YAML configuration. LSP capabilities are exposed to actors as **tools** (via an `LSPToolAdapter`) and as **context enrichment** (automatic injection of diagnostics and type information into the actor's ACMS context). This integration lives in the **Infrastructure layer**, not the Presentation layer — LSP serves the AI agents, not human users in editors.
|
|
|
|
|
|
|
|
|
|
## Design
|
|
|
|
|
|
|
|
|
|
### LSP Server Role
|
|
|
|
|
### LSP Registry
|
|
|
|
|
|
|
|
|
|
The LSP server is a Presentation-layer adapter. It translates LSP requests into ACP calls and maps ACP responses back to LSP messages.
|
|
|
|
|
Language servers are registered as first-class entities in a global **LSP Registry**, following the same patterns as the Tool Registry, Skill Registry, and Actor Registry.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
Editor ←— LSP JSON-RPC —→ LSP Server ←— ACP —→ Service Facade
|
|
|
|
|
Each LSP entry is:
|
|
|
|
|
|
|
|
|
|
- **Namespaced** using the standard `[[server:]namespace/]name` format (e.g., `local/pyright`, `local/clangd`, `cleverthis/rust-analyzer`).
|
|
|
|
|
- **Defined via YAML configuration** and registered via `agents lsp add --config <file>`.
|
|
|
|
|
- **Independently versioned and managed** — language servers can be added, updated, listed, shown, and removed like any other registered entity.
|
|
|
|
|
|
|
|
|
|
#### LSP Configuration Schema
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
name: local/pyright
|
|
|
|
|
description: "Pyright language server for Python type checking and intelligence"
|
|
|
|
|
|
|
|
|
|
command: pyright-langserver
|
|
|
|
|
args: ["--stdio"]
|
|
|
|
|
transport: stdio # stdio (default) or tcp
|
|
|
|
|
|
|
|
|
|
languages: # Languages this server covers
|
|
|
|
|
- python
|
|
|
|
|
|
|
|
|
|
capabilities: # LSP capabilities this server provides
|
|
|
|
|
- diagnostics # textDocument/publishDiagnostics
|
|
|
|
|
- hover # textDocument/hover
|
|
|
|
|
- completions # textDocument/completion
|
|
|
|
|
- definitions # textDocument/definition
|
|
|
|
|
- references # textDocument/references
|
|
|
|
|
- rename # textDocument/rename
|
|
|
|
|
- code_actions # textDocument/codeAction
|
|
|
|
|
- formatting # textDocument/formatting
|
|
|
|
|
- signature_help # textDocument/signatureHelp
|
|
|
|
|
- document_symbols # textDocument/documentSymbol
|
|
|
|
|
- workspace_symbols # workspace/symbol
|
|
|
|
|
|
|
|
|
|
initialization: # LSP initialization options
|
|
|
|
|
python:
|
|
|
|
|
pythonPath: "${PYTHON_PATH:/usr/bin/python3}"
|
|
|
|
|
venvPath: "${VENV_PATH:}"
|
|
|
|
|
analysis:
|
|
|
|
|
typeCheckingMode: "basic"
|
|
|
|
|
autoSearchPaths: true
|
|
|
|
|
|
|
|
|
|
workspace_settings: # Per-workspace LSP settings
|
|
|
|
|
python.analysis.diagnosticMode: "workspace"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Feature Mapping
|
|
|
|
|
Key configuration fields:
|
|
|
|
|
|
|
|
|
|
| LSP Feature | CleverAgents Source | ACP Operation |
|
|
|
|
|
|-------------|--------------------|--------------|
|
|
|
|
|
| **Diagnostics** | Validation results (required + informational) | `validation.list_results` |
|
|
|
|
|
| **Code actions** | Run validation, propose fix, apply plan, cancel plan | `plan.execute`, `plan.apply`, `validation.run` |
|
|
|
|
|
| **Hover** | Context snippets, decision provenance, UKO entity summaries | `context.inspect` |
|
|
|
|
|
| **Workspace symbols** | Registered resources, active plans, attached validations | `resource.list`, `plan.list` |
|
|
|
|
|
| Field | Type | Required | Description |
|
|
|
|
|
|-------|------|----------|-------------|
|
|
|
|
|
| `name` | string | Yes | Namespaced identifier (`<namespace>/<name>`). |
|
|
|
|
|
| `description` | string | No | Human-readable description. |
|
|
|
|
|
| `command` | string | Yes | Executable command to launch the language server. |
|
|
|
|
|
| `args` | list[string] | No | Command-line arguments for the server process. |
|
|
|
|
|
| `transport` | string | No | Communication transport: `stdio` (default) or `tcp`. |
|
|
|
|
|
| `languages` | list[string] | Yes | Programming languages this server provides intelligence for. |
|
|
|
|
|
| `capabilities` | list[string] | No | Explicit list of LSP capabilities. When omitted, capabilities are discovered from the server's `initialize` response. |
|
|
|
|
|
| `initialization` | object | No | LSP `initializationOptions` passed during the `initialize` handshake. Supports `${ENV_VAR}` interpolation. |
|
|
|
|
|
| `workspace_settings` | object | No | LSP workspace configuration settings sent via `workspace/didChangeConfiguration`. |
|
|
|
|
|
|
|
|
|
|
### Workspace and Resource Resolution
|
|
|
|
|
### Resource Language Discovery
|
|
|
|
|
|
|
|
|
|
- LSP workspace roots are matched to registered resource paths in the Resource Registry.
|
|
|
|
|
- File URI paths are resolved through the Resource Registry so that sandbox-resident files map correctly to their logical resources.
|
|
|
|
|
- If a workspace root does not correspond to a registered resource, plan-aware features degrade gracefully (standard editing still works).
|
|
|
|
|
Resources attached to a project represent codebases, file trees, and repositories that contain source code in one or more programming languages. The system determines what languages a resource contains through a multi-layered discovery process:
|
|
|
|
|
|
|
|
|
|
### Diagnostic Severity Mapping
|
|
|
|
|
1. **File extension analysis** — The most common and fastest method. File extensions (`.py`, `.ts`, `.rs`, `.go`, `.cpp`, etc.) are mapped to language identifiers using a built-in extension-to-language table.
|
|
|
|
|
2. **Content analysis** — For files without extensions or with ambiguous extensions, content markers are inspected: shebang lines (`#!/usr/bin/env python3`), magic comments, file headers, and structural patterns.
|
|
|
|
|
3. **UKO classification** — The Universal Knowledge Ontology (ACMS) classifies resources at the technology-specific layer (Python, TypeScript, Rust, etc.), providing semantic language identification that persists across sessions.
|
|
|
|
|
4. **Explicit project-level declaration** — Projects may explicitly declare the languages they contain via configuration, overriding or supplementing automatic discovery.
|
|
|
|
|
|
|
|
|
|
| Validation Mode | LSP Severity |
|
|
|
|
|
|----------------|-------------|
|
|
|
|
|
| `required` — failed | `Error` |
|
|
|
|
|
| `required` — passed | (suppressed) |
|
|
|
|
|
| `informational` — failed | `Warning` |
|
|
|
|
|
| `informational` — passed | `Hint` |
|
|
|
|
|
Language discovery results are cached per resource and invalidated when the resource content changes (detected via file watchers or resource refresh).
|
|
|
|
|
|
|
|
|
|
### Lifecycle
|
|
|
|
|
### Actor LSP Binding
|
|
|
|
|
|
|
|
|
|
The LSP server is a long-lived process launched by the editor. It:
|
|
|
|
|
Actors declare their LSP dependencies in their YAML configuration via the `lsp:` field. The binding model supports three levels of specificity, from fully explicit to fully automatic:
|
|
|
|
|
|
|
|
|
|
1. Initializes an ACP client (in-process for local mode, HTTPS for server mode).
|
|
|
|
|
2. Subscribes to plan and validation events via ACP streaming.
|
|
|
|
|
3. Pushes diagnostics to the editor as events arrive.
|
|
|
|
|
4. Handles editor-initiated requests (code actions, hover, completion) synchronously via ACP.
|
|
|
|
|
#### Explicit Binding (by server name)
|
|
|
|
|
|
|
|
|
|
The actor lists specific registered LSP servers by their namespaced names:
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
actors:
|
|
|
|
|
code_analyzer:
|
|
|
|
|
type: llm
|
|
|
|
|
config:
|
|
|
|
|
actor: anthropic/claude-3-opus
|
|
|
|
|
system_prompt: |
|
|
|
|
|
You are a Python code analyst. Use LSP tools to understand
|
|
|
|
|
type information and identify issues in the codebase.
|
|
|
|
|
skills:
|
|
|
|
|
- local/file-ops
|
|
|
|
|
lsp:
|
|
|
|
|
- local/pyright
|
|
|
|
|
- local/ruff-lsp
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### Language-Based Binding
|
|
|
|
|
|
|
|
|
|
The actor specifies languages and the runtime resolves the appropriate LSP servers from the registry:
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
actors:
|
|
|
|
|
polyglot_reviewer:
|
|
|
|
|
type: llm
|
|
|
|
|
config:
|
|
|
|
|
actor: openai/gpt-4
|
|
|
|
|
system_prompt: |
|
|
|
|
|
Review code for correctness and style issues.
|
|
|
|
|
lsp:
|
|
|
|
|
languages:
|
|
|
|
|
- python
|
|
|
|
|
- typescript
|
|
|
|
|
- rust
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
At activation, the runtime queries the LSP Registry for servers covering each declared language. If multiple servers cover the same language, the first registered match is used (or the user can disambiguate with explicit names).
|
|
|
|
|
|
|
|
|
|
#### Resource-Auto Binding
|
|
|
|
|
|
|
|
|
|
The actor requests automatic LSP resolution based on the languages present in the resources it is processing:
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
actors:
|
|
|
|
|
universal_developer:
|
|
|
|
|
type: llm
|
|
|
|
|
config:
|
|
|
|
|
actor: anthropic/claude-3-opus
|
|
|
|
|
system_prompt: |
|
|
|
|
|
You are a software developer. Use LSP capabilities to understand
|
|
|
|
|
the codebase you are working in.
|
|
|
|
|
lsp:
|
|
|
|
|
auto: true
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
When `auto: true` is set, the runtime inspects the project's bound resources via language discovery, determines which languages are present, and binds the appropriate LSP servers automatically. This is the most reusable pattern — an actor configured with `lsp: { auto: true }` works correctly regardless of the language of the project it is assigned to.
|
|
|
|
|
|
|
|
|
|
#### Jinja2 Dynamic Binding
|
|
|
|
|
|
|
|
|
|
For advanced use cases, Jinja2 templates can dynamically resolve LSP bindings at preprocessing time:
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
actors:
|
|
|
|
|
dynamic_analyst:
|
|
|
|
|
type: llm
|
|
|
|
|
config:
|
|
|
|
|
actor: openai/gpt-4
|
|
|
|
|
lsp:
|
|
|
|
|
{% for lang in context.project_languages %}
|
|
|
|
|
- {{ lsp_registry.for_language(lang) | first }}
|
|
|
|
|
{% endfor %}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Template variables available during LSP resolution:
|
|
|
|
|
|
|
|
|
|
| Variable | Type | Description |
|
|
|
|
|
|----------|------|-------------|
|
|
|
|
|
| `lsp_registry` | object | The LSP Registry, supporting `.for_language(lang)` and `.all()` queries. |
|
|
|
|
|
| `context.project_languages` | list[string] | Languages detected in the current project's resources. |
|
|
|
|
|
| `context.resource_languages` | list[string] | Languages detected in the specific resource being processed. |
|
|
|
|
|
|
|
|
|
|
#### Per-Node Binding in Actor Graphs
|
|
|
|
|
|
|
|
|
|
Different nodes in an actor's graph can have different LSP configurations. This allows fine-grained control — for example, giving a strategy actor read-only diagnostics while giving an execution actor full LSP capabilities:
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
actors:
|
|
|
|
|
strategy_planner:
|
|
|
|
|
type: llm
|
|
|
|
|
config:
|
|
|
|
|
actor: openai/gpt-4
|
|
|
|
|
system_prompt: |
|
|
|
|
|
Plan the implementation strategy.
|
|
|
|
|
lsp:
|
|
|
|
|
- local/pyright
|
|
|
|
|
lsp_capabilities: # Restrict to read-only capabilities
|
|
|
|
|
- diagnostics
|
|
|
|
|
- hover
|
|
|
|
|
- definitions
|
|
|
|
|
- references
|
|
|
|
|
|
|
|
|
|
code_implementer:
|
|
|
|
|
type: llm
|
|
|
|
|
config:
|
|
|
|
|
actor: anthropic/claude-3-opus
|
|
|
|
|
system_prompt: |
|
|
|
|
|
Implement code changes according to the strategy.
|
|
|
|
|
lsp:
|
|
|
|
|
auto: true
|
|
|
|
|
lsp_capabilities: all # Full capabilities (default)
|
|
|
|
|
|
|
|
|
|
routes:
|
|
|
|
|
dev_workflow:
|
|
|
|
|
type: graph
|
|
|
|
|
entry_point: plan
|
|
|
|
|
nodes:
|
|
|
|
|
- name: plan
|
|
|
|
|
type: agent
|
|
|
|
|
agent: strategy_planner
|
|
|
|
|
- name: implement
|
|
|
|
|
type: agent
|
|
|
|
|
agent: code_implementer
|
|
|
|
|
edges:
|
|
|
|
|
- source: plan
|
|
|
|
|
target: implement
|
|
|
|
|
- source: implement
|
|
|
|
|
target: end
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The `lsp_capabilities` field controls which LSP features are exposed to the actor as tools. When omitted or set to `all`, every capability declared by the bound LSP servers is available.
|
|
|
|
|
|
|
|
|
|
| Capability | LSP Method | Tool Name | Description |
|
|
|
|
|
|-----------|-----------|-----------|-------------|
|
|
|
|
|
| `diagnostics` | `textDocument/publishDiagnostics` | `lsp/diagnostics` | Retrieve compilation errors, warnings, and hints for a file |
|
|
|
|
|
| `hover` | `textDocument/hover` | `lsp/hover` | Get type information and documentation at a position |
|
|
|
|
|
| `completions` | `textDocument/completion` | `lsp/completions` | Get code completion suggestions at a position |
|
|
|
|
|
| `definitions` | `textDocument/definition` | `lsp/definition` | Go to the definition of a symbol |
|
|
|
|
|
| `references` | `textDocument/references` | `lsp/references` | Find all references to a symbol |
|
|
|
|
|
| `rename` | `textDocument/rename` | `lsp/rename` | Compute a rename refactoring across files |
|
|
|
|
|
| `code_actions` | `textDocument/codeAction` | `lsp/code-actions` | Get available code actions (quick fixes, refactors) |
|
|
|
|
|
| `formatting` | `textDocument/formatting` | `lsp/format` | Format a document according to language conventions |
|
|
|
|
|
| `signature_help` | `textDocument/signatureHelp` | `lsp/signature` | Get function signature information at a call site |
|
|
|
|
|
| `document_symbols` | `textDocument/documentSymbol` | `lsp/symbols` | List all symbols (classes, functions, variables) in a file |
|
|
|
|
|
| `workspace_symbols` | `workspace/symbol` | `lsp/workspace-symbols` | Search for symbols across the entire workspace |
|
|
|
|
|
|
|
|
|
|
### LSP Capability Exposure
|
|
|
|
|
|
|
|
|
|
LSP capabilities reach actors through two complementary mechanisms:
|
|
|
|
|
|
|
|
|
|
#### As Tools (via LSPToolAdapter)
|
|
|
|
|
|
|
|
|
|
The `LSPToolAdapter` is an Infrastructure-layer adapter (analogous to `MCPToolAdapter` for MCP) that translates registered LSP server capabilities into CleverAgents tools. When an actor with LSP bindings is activated:
|
|
|
|
|
|
|
|
|
|
1. The runtime resolves the actor's `lsp:` configuration to concrete LSP server entries from the LSP Registry.
|
|
|
|
|
2. For each bound server, the `LSPToolAdapter` generates tool definitions for each exposed capability.
|
|
|
|
|
3. These tools are injected into the actor's available tool surface alongside skill-provided tools.
|
|
|
|
|
4. The actor can call LSP tools during its reasoning loop (e.g., `lsp/diagnostics` to check for errors before committing a change, `lsp/definition` to understand a symbol before modifying it).
|
|
|
|
|
|
|
|
|
|
Tool signatures follow a consistent pattern:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
lsp/diagnostics(file_path: str) -> list[Diagnostic]
|
|
|
|
|
lsp/hover(file_path: str, line: int, column: int) -> HoverResult
|
|
|
|
|
lsp/definition(file_path: str, line: int, column: int) -> list[Location]
|
|
|
|
|
lsp/references(file_path: str, line: int, column: int) -> list[Location]
|
|
|
|
|
lsp/completions(file_path: str, line: int, column: int) -> list[CompletionItem]
|
|
|
|
|
lsp/rename(file_path: str, line: int, column: int, new_name: str) -> WorkspaceEdit
|
|
|
|
|
lsp/code-actions(file_path: str, start_line: int, start_col: int, end_line: int, end_col: int) -> list[CodeAction]
|
|
|
|
|
lsp/format(file_path: str) -> FormattedText
|
|
|
|
|
lsp/symbols(file_path: str) -> list[DocumentSymbol]
|
|
|
|
|
lsp/workspace-symbols(query: str) -> list[SymbolInformation]
|
|
|
|
|
lsp/signature(file_path: str, line: int, column: int) -> SignatureHelp
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
When multiple LSP servers are bound to an actor (e.g., `local/pyright` for Python and `local/typescript-lsp` for TypeScript), the tool adapter routes requests to the appropriate server based on the file's detected language. The tool names remain the same — the routing is transparent to the actor.
|
|
|
|
|
|
|
|
|
|
#### As Context Enrichment
|
|
|
|
|
|
|
|
|
|
In addition to explicit tool calls, LSP servers can automatically enrich the actor's context when code files are loaded into the ACMS hot context:
|
|
|
|
|
|
|
|
|
|
- **Diagnostic injection**: When a source file enters the actor's hot context, the LSP server's diagnostics for that file are automatically appended as structured annotations. This means the actor "sees" type errors, unused imports, and other issues without needing to explicitly call `lsp/diagnostics`.
|
|
|
|
|
- **Type annotation overlay**: Hover information for key symbols (function signatures, class hierarchies, variable types) can be pre-fetched and included as context metadata, giving the actor type-level understanding of the code it is examining.
|
|
|
|
|
|
|
|
|
|
Context enrichment is controlled by the actor's `lsp_context_enrichment` configuration:
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
actors:
|
|
|
|
|
enriched_reviewer:
|
|
|
|
|
type: llm
|
|
|
|
|
config:
|
|
|
|
|
actor: openai/gpt-4
|
|
|
|
|
lsp:
|
|
|
|
|
auto: true
|
|
|
|
|
lsp_context_enrichment:
|
|
|
|
|
diagnostics: true # Auto-inject diagnostics (default: true)
|
|
|
|
|
type_annotations: false # Auto-inject type info (default: false, can be expensive)
|
|
|
|
|
max_diagnostics_per_file: 50 # Limit to avoid context bloat
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### LSP Server Lifecycle
|
|
|
|
|
|
|
|
|
|
LSP servers are long-lived processes managed by the **LSP Runtime** in the Infrastructure layer:
|
|
|
|
|
|
|
|
|
|
1. **Startup**: When an actor with LSP bindings is activated (either for a plan phase or a direct `agents actor run` invocation), the LSP Runtime starts the required language server processes. Each server is initialized with the `initialize` LSP handshake, passing `initializationOptions` from the registry entry and workspace root paths mapped from the actor's bound resources.
|
|
|
|
|
|
|
|
|
|
2. **Workspace Mapping**: LSP workspace roots are mapped to the physical paths of registered resources. When the actor operates within a sandbox (Execute phase), the sandbox's working directory is used as the workspace root, ensuring the language server analyzes the sandboxed code — not the original.
|
|
|
|
|
|
|
|
|
|
3. **File Synchronization**: As the actor reads and modifies files (via tools), the LSP Runtime sends `textDocument/didOpen`, `textDocument/didChange`, and `textDocument/didClose` notifications to the language server, keeping the server's view of the codebase synchronized with the actor's mutations.
|
|
|
|
|
|
|
|
|
|
4. **Shared Instances**: If multiple actors in the same plan or session require the same language server for the same workspace, the LSP Runtime shares a single server instance. Reference counting ensures the server stays alive as long as at least one actor needs it.
|
|
|
|
|
|
|
|
|
|
5. **Shutdown**: When the actor deactivates (plan phase completes, session ends, or direct run finishes), the LSP Runtime sends the `shutdown` and `exit` lifecycle messages. Shared instances are shut down only when the last referencing actor deactivates.
|
|
|
|
|
|
|
|
|
|
### LSP in the Plan Lifecycle
|
|
|
|
|
|
|
|
|
|
LSP integration enhances each phase of the plan lifecycle:
|
|
|
|
|
|
|
|
|
|
| Phase | LSP Role | Example Use |
|
|
|
|
|
|-------|---------|-------------|
|
|
|
|
|
| **Strategize** | Read-only intelligence — diagnostics, type information, and symbol navigation inform strategy decisions | Strategy actor calls `lsp/diagnostics` to assess codebase health, `lsp/symbols` to understand module structure, `lsp/references` to gauge impact of proposed changes |
|
|
|
|
|
| **Execute** | Full intelligence — all capabilities available for code generation and modification | Execution actor calls `lsp/definition` to understand APIs before using them, `lsp/diagnostics` to verify changes compile, `lsp/rename` to perform safe refactorings |
|
|
|
|
|
| **Apply** | Validation intelligence — diagnostics confirm the final changeset is clean | Apply-phase validations invoke `lsp/diagnostics` on all modified files to ensure no regressions before merge |
|
|
|
|
|
|
|
|
|
|
### Comparison with MCP
|
|
|
|
|
|
|
|
|
|
LSP and MCP are both standard protocols for external capability servers, but they serve different purposes:
|
|
|
|
|
|
|
|
|
|
| Aspect | MCP (Model Context Protocol) | LSP (Language Server Protocol) |
|
|
|
|
|
|--------|------------------------------|-------------------------------|
|
|
|
|
|
| **Purpose** | General-purpose tool discovery and invocation | Language-specific code intelligence |
|
|
|
|
|
| **Scope** | Any callable operation (file I/O, API calls, data queries) | Code analysis capabilities (diagnostics, navigation, completions) |
|
|
|
|
|
| **Registry** | Tool Registry (via `MCPToolAdapter`) | LSP Registry (via `LSPToolAdapter`) |
|
|
|
|
|
| **Binding** | Skills compose MCP tools; actors acquire via skill references | Actors bind LSP servers directly via `lsp:` configuration |
|
|
|
|
|
| **Tool generation** | Tools pre-defined by MCP server, registered at discovery | Tools generated dynamically from LSP server capabilities at activation |
|
|
|
|
|
| **Lifecycle** | Managed by MCP SDK connection lifecycle | Managed by LSP Runtime with workspace mapping and file synchronization |
|
|
|
|
|
|
|
|
|
|
Both adapters follow the same architectural pattern: a standard protocol server in the Infrastructure layer is bridged into the actor's tool surface through a typed adapter. The key difference is that MCP tools are general-purpose and explicitly authored, while LSP tools are language-specific and automatically derived from the protocol's capability model.
|
|
|
|
|
|
|
|
|
|
## Constraints
|
|
|
|
|
|
|
|
|
|
- The LSP server must not access storage, database, or domain services directly — all operations go through ACP.
|
|
|
|
|
- IDE features must remain strictly editor-agnostic; no editor-specific APIs outside the LSP specification.
|
|
|
|
|
- LSP diagnostics must reflect validation results without modifying any resources (read-only at the presentation boundary).
|
|
|
|
|
- The LSP server must function in both local mode (in-process ACP) and server mode (remote ACP over HTTPS).
|
|
|
|
|
- LSP servers must be registered in the LSP Registry before they can be referenced by actors. There is no inline LSP server definition.
|
|
|
|
|
- Actor LSP bindings are resolved at actor activation time — unresolvable bindings (missing registry entry, no server for a requested language) produce a clear error.
|
|
|
|
|
- LSP tools must not modify resources outside the sandbox boundary during Execute phase. Mutation operations (rename, code actions that apply edits) must route through the sandbox.
|
|
|
|
|
- The LSP Runtime must handle language server crashes gracefully — restarting the server and re-synchronizing file state without disrupting the actor's execution.
|
|
|
|
|
- LSP servers must operate within the same security boundary as the actor. Server processes run with the same permissions as the CleverAgents process.
|
|
|
|
|
- Context enrichment (automatic diagnostic/type injection) must respect the actor's context budget — diagnostic results are truncated if they would exceed the hot context token limit.
|
|
|
|
|
|
|
|
|
|
## Consequences
|
|
|
|
|
|
|
|
|
|
### Positive
|
|
|
|
|
- One integration serves all LSP-compatible editors (VS Code, JetBrains via LSP bridge, Neovim, Emacs, Helix, etc.).
|
|
|
|
|
- IDE features stay exactly aligned with CLI/TUI/Web behavior because they share the same ACP contract.
|
|
|
|
|
- Reduced maintenance burden compared to N bespoke editor plugins.
|
|
|
|
|
- Diagnostics arrive in real-time via ACP event streaming, keeping editors up to date during long-running plan execution.
|
|
|
|
|
- Actors gain the same rich language intelligence that human developers use in IDEs, dramatically improving code quality in generated output.
|
|
|
|
|
- The LSP ecosystem provides production-quality language servers for virtually every mainstream language — CleverAgents benefits from years of community investment without building custom analyzers.
|
|
|
|
|
- Language-agnostic actor definitions (using `lsp: { auto: true }`) work correctly across any project language, maximizing actor reusability.
|
|
|
|
|
- Per-node LSP binding gives fine-grained control over which actors in a graph receive which capabilities, enabling cost-conscious configurations (e.g., diagnostics-only for strategy, full capabilities for execution).
|
|
|
|
|
- The adapter pattern (`LSPToolAdapter`) mirrors the established `MCPToolAdapter` pattern, maintaining architectural consistency.
|
|
|
|
|
|
|
|
|
|
### Negative
|
|
|
|
|
- LSP's fixed feature set may limit richer custom UI experiences that specific editors could offer (e.g., custom webview panels in VS Code).
|
|
|
|
|
- ACP round-trips add latency to interactive IDE operations compared to direct in-process access.
|
|
|
|
|
- Language servers are resource-intensive processes (memory, CPU). Running multiple servers simultaneously (e.g., Pyright + typescript-language-server + clangd for a polyglot project) increases system resource consumption.
|
|
|
|
|
- LSP startup time adds latency to actor activation. Large workspaces may take several seconds to index before diagnostics become available.
|
|
|
|
|
- File synchronization overhead — every file read/write by the actor triggers LSP notifications, adding I/O overhead proportional to the actor's file activity.
|
|
|
|
|
- LSP protocol complexity — the protocol has many capabilities and edge cases. The `LSPToolAdapter` must handle server-specific quirks and partial capability support.
|
|
|
|
|
|
|
|
|
|
### Risks
|
|
|
|
|
- Future LSP specification changes could require compatibility work in the server.
|
|
|
|
|
- High-frequency IDE events (typing, hover) could place excessive load on ACP endpoints if not debounced.
|
|
|
|
|
- Language server stability varies. Some servers may crash under heavy load, produce incorrect diagnostics, or fail to handle large files. The LSP Runtime must be robust to these failure modes.
|
|
|
|
|
- LSP diagnostic latency may not keep up with rapid file modifications during Execute phase, leading to stale diagnostics if the actor reads diagnostics immediately after writing a file.
|
|
|
|
|
- The `auto: true` binding mode depends on accurate language discovery. Misidentified languages would bind incorrect servers, producing confusing tool results.
|
|
|
|
|
- Deep Jinja2 templating for LSP resolution may produce configurations that are difficult to debug when binding fails.
|
|
|
|
|
|
|
|
|
|
## Alternatives Considered
|
|
|
|
|
|
|
|
|
|
**Editor-specific plugins** — More tailored UX per editor but requires separate implementations and ongoing maintenance for each editor. Rejected as the primary strategy (may be revisited for value-add features on top of the LSP baseline).
|
|
|
|
|
**Custom language analysis per language** — Build bespoke code analysis (AST parsing, type checking) for each supported language. Maximum control but enormous development cost, perpetual maintenance burden, and inevitable lag behind the state of the art in language tooling. The LSP ecosystem already provides what CleverAgents needs. Rejected.
|
|
|
|
|
|
|
|
|
|
**Custom IDE protocol** — Maximum flexibility but zero ecosystem adoption, higher learning curve for contributors, and no editor-side library support. Rejected.
|
|
|
|
|
**Tree-sitter only** — Use Tree-sitter for syntax-level analysis across all languages. Tree-sitter provides fast, incremental parsing and syntax highlighting but lacks semantic understanding (no type checking, no cross-file references, no completion intelligence). Tree-sitter may be used as a complementary lightweight layer for syntax-only tasks, but it cannot replace LSP for semantic intelligence. Rejected as the primary strategy.
|
|
|
|
|
|
|
|
|
|
**LSP as IDE integration (Presentation layer)** — Use LSP as a protocol for IDE plugin integration, mapping editor features to plan operations. This conflates two concerns: LSP is designed for language intelligence, not for application-level UI integration. CleverAgents' Presentation-layer clients (CLI, TUI, Web, IDE plugin) all communicate through ACP. Using LSP for IDE integration would create an unnecessary protocol layer between the IDE and ACP when ACP already provides the needed surface. Rejected — the IDE plugin is an embedded TUI communicating through ACP; LSP serves the Infrastructure layer.
|
|
|
|
|
|
|
|
|
|
**LSP as part of the Skill system** — Package LSP capabilities as skills rather than as a separate registry and binding mechanism. While architecturally possible, this conflates two distinct concerns: skills organize tools into reusable collections, while LSP binding requires workspace-aware server lifecycle management, file synchronization, and language-based routing that skills are not designed to handle. A dedicated LSP Registry and Runtime provide clearer separation of concerns. Rejected.
|
|
|
|
|
|
|
|
|
|
## Compliance
|
|
|
|
|
|
|
|
|
|
- **LSP conformance tests**: Validate request/response message formats and declared capabilities against the LSP specification.
|
|
|
|
|
- **ACP integration tests**: Verify that every LSP feature correctly maps to the corresponding ACP operation and produces the expected editor-visible result.
|
|
|
|
|
- **Diagnostics tests**: Ensure validation results translate to LSP diagnostics with the correct severity mapping and no resource side effects.
|
|
|
|
|
- **Degradation tests**: Verify graceful fallback when workspace roots do not map to registered resources.
|
|
|
|
|
- **LSP conformance tests**: Validate that the LSP Runtime correctly implements the LSP initialize/shutdown lifecycle, file synchronization notifications, and capability negotiation for each registered server.
|
|
|
|
|
- **Tool adapter tests**: Verify that `LSPToolAdapter` generates correct tool definitions from LSP server capabilities and routes tool invocations to the correct server based on file language.
|
|
|
|
|
- **Binding resolution tests**: Test all three binding modes (explicit, language-based, resource-auto) including edge cases: missing servers, ambiguous language detection, multiple servers for the same language.
|
|
|
|
|
- **Lifecycle tests**: Verify server startup/shutdown, shared instance reference counting, crash recovery and re-synchronization, and sandbox workspace mapping.
|
|
|
|
|
- **Context enrichment tests**: Confirm that diagnostic injection respects context budgets, handles empty diagnostics, and correctly annotates files in hot context.
|
|
|
|
|
- **Integration tests**: End-to-end tests where an actor with LSP bindings performs code analysis tasks (find errors, navigate definitions, complete code) and produces correct results using LSP tools.
|
|
|
|
|
|
|
|
|
|
## Related ADRs
|
|
|
|
|
|
|
|
|
|
| ADR | Title | Relationship |
|
|
|
|
|
|-----|-------|-------------|
|
|
|
|
|
| [ADR-001](ADR-001-layered-architecture.md) | Layered Architecture | The LSP server is a Presentation-layer adapter |
|
|
|
|
|
| [ADR-013](ADR-013-validation-abstraction.md) | Validation Abstraction | Validation results are the primary source for LSP diagnostics |
|
|
|
|
|
| [ADR-026](ADR-026-agent-client-protocol.md) | Agent Client Protocol (ACP) | The LSP server communicates with the backend exclusively through ACP |
|
|
|
|
|
| [ADR-001](ADR-001-layered-architecture.md) | Layered Architecture | The LSP Runtime resides in the Infrastructure layer; LSP tools are exposed to actors through the Domain layer's tool abstractions |
|
|
|
|
|
| [ADR-008](ADR-008-resource-system.md) | Resource System | Resources provide the workspace roots for LSP servers and the language discovery inputs for auto-binding |
|
|
|
|
|
| [ADR-010](ADR-010-actor-and-agent-architecture.md) | Actor and Agent Architecture | Actors declare LSP bindings in their YAML configuration; LSP tools participate as capabilities in actor graph nodes |
|
|
|
|
|
| [ADR-011](ADR-011-tool-system.md) | Tool System | LSP capabilities are exposed as tools through the `LSPToolAdapter`, following the same adapter pattern as MCP |
|
|
|
|
|
| [ADR-012](ADR-012-skill-system.md) | Skill System | LSP tools complement skill-provided tools in the actor's merged tool surface |
|
|
|
|
|
| [ADR-014](ADR-014-context-management-acms.md) | Context Management (ACMS) | LSP context enrichment injects diagnostics and type information into the ACMS hot context |
|
|
|
|
|
| [ADR-029](ADR-029-model-context-protocol.md) | Model Context Protocol (MCP) Adoption | MCP and LSP follow analogous adapter patterns; both bridge standard protocol servers into the tool system |
|
|
|
|
|
| [ADR-031](ADR-031-actor-abstraction-definition.md) | Actor Abstraction Definition | Actors acquire language intelligence through LSP bindings alongside tool capabilities through skills |
|
|
|
|
|
| [ADR-032](ADR-032-jinja2-yaml-template-preprocessing.md) | Jinja2 YAML Template Preprocessing | Jinja2 templates enable dynamic LSP binding resolution in actor YAML files |
|
|
|
|
|
|
|
|
|
|
## Acceptance
|
|
|
|
|
|
|
|
|
@@ -107,7 +407,7 @@ The LSP server is a long-lived process launched by the editor. It:
|
|
|
|
|
|
|
|
|
|
| Voter | Comment |
|
|
|
|
|
|-------|---------|
|
|
|
|
|
| Jeffrey Phillips Freeman <Jeffrey.Freeman@CleverThis.com> | LSP integration gives IDE users real-time access to CleverAgents without leaving their editor |
|
|
|
|
|
| Jeffrey Phillips Freeman <Jeffrey.Freeman@CleverThis.com> | Giving actors the same language intelligence that human developers use in IDEs is a force multiplier for code quality — and leveraging the LSP ecosystem avoids reinventing mature tooling |
|
|
|
|
|
|
|
|
|
|
**Total: 1**
|
|
|
|
|
|
|
|
|
|