cclsp

repository·main·Indexed 20 days ago

https://github.com/ktnyt/cclsp

An MCP server that acts as an intelligent adapter between LLM-based coding agents and Language Server Protocol (LSP) servers. It provides robust symbol resolution, navigation, and refactoring tools—such as find_definition, find_references, and rename_symbol—to help AI agents overcome challenges with precise line and column numbering. cclsp supports custom configuration via cclsp.json to map file extensions to specific LSP server commands.

Tokens
13.5K
Snippets
42
Records
51
Agent score
71%

What's inside cclsp

  1. Planned improvements to cclsp architecture

    main

    The project is undergoing a refactoring process divided into two main phases to improve modularity, stability, and observability.

    Phase 1: Structural Refactoring

    Focuses on decomposing the monolithic lsp-client.ts into specialized modules while preserving existing behavior. Key modules being extracted include:

    • src/lsp/types.ts: Centralized LSP and internal types.
    • src/lsp/json-rpc.ts: Message framing and ID correlation.
    • src/lsp/config.ts: Configuration loading and validation.
    • src/lsp/document-manager.ts: Tracking didOpen, didChange, and didClose events.
    • src/lsp/server-manager.ts: Server lifecycle (spawn, restart) and adapter integration.
    • src/lsp/diagnostics.ts: Diagnostics caching.
    • src/lsp/operations.ts: Orchestration of LSP operations.
    • src/tools/registry.ts: Declarative tool definitions.

    Phase 2: Quality Improvements

    Focuses on enhancing the developer and user experience:

    • Logging: Adding a structured logger controlled by the CCLSP_LOG_LEVEL environment variable.
    • Adapter Interface: Cleaning up the adapter interface and implementing customizeInitializeParams.
    • Resilience: Implementing graceful degradation so individual server failures do not crash the entire MCP process.
    • Version Accuracy: Ensuring the MCP server version matches package.json.
  2. How the cclsp architecture is structured

    main

    The cclsp project is organized into several specialized modules to separate concerns between I/O, lifecycle management, and LSP semantics:

    • lsp/json-rpc.ts: Handles the low-level JsonRpcTransport (message framing, ID correlation).
    • lsp/server-manager.ts: Manages the ServerManager (spawning, restarting, and adapter integration).
    • lsp/document-manager.ts: Tracks open document state via DocumentManager.
    • lsp/operations.ts: Orchestrates LspOperations (the logic of getting a server, opening a document, and sending requests).
    • lsp/diagnostics.ts: Provides a DiagnosticsCache to store and query server-pushed diagnostics.
    • lsp/config.ts: Uses ConfigLoader to resolve environment variables (like CCLSP_CONFIG_PATH) and validate cclsp.json.
  3. Integrate cclsp as an MCP Server

    main

    To use cclsp with an MCP client like Claude Code, add it to your mcpServers configuration. You must provide the CCLSP_CONFIG_PATH environment variable pointing to your cclsp.json file.

    Using npm package (after global install)

    {
      "mcpServers": {
        "cclsp": {
          "command": "cclsp",
          "env": {
            "CCLSP_CONFIG_PATH": "/path/to/your/cclsp.json"
          }
        }
      }
    }

    Using local installation

    {
      "mcpServers": {
        "cclsp": {
          "command": "node",
          "args": ["/path/to/cclsp/dist/index.js"],
          "env": {
            "CCLSP_CONFIG_PATH": "/path/to/your/cclsp.json"
          }
        }
      }
    }
    {
      "mcpServers": {
        "cclsp": {
          "command": "cclsp",
          "env": {
            "CCLSP_CONFIG_PATH": "/path/to/your/cclsp.json"
          }
        }
      }
    }
  4. Prerequisites for cclsp

    main

    Before using cclsp, ensure you have the following installed:

    1. Runtime: Node.js 18+ or Bun.
    2. Language Servers: You must have the specific LSP servers for your target languages installed on your system (e.g., gopls for Go, clangd for C/C++). cclsp does not include these; it only acts as an adapter.
  5. Install and Setup cclsp

    main

    cclsp is an MCP server that integrates LLM-based coding agents with LSP servers. You can set it up using an automated interactive wizard or manually.

    Run the interactive wizard to auto-detect languages, install required LSPs, and configure Claude MCP.

    • Project-specific configuration: Creates .claude/cclsp.json in the current directory.
    • User-wide configuration: Creates a global config in ~/.config/claude/cclsp.json using the --user flag.
    # Project-specific setup
    npx cclsp@latest setup
    
    # Global user-wide setup
    npx cclsp@latest setup --user

    Manual Setup

    1. Install cclsp globally: npm install -g cclsp.
    2. Install the required language servers for your target languages (e.g., gopls for Go, typescript-language-server for TS).
    3. Create a configuration file (see Manual Configuration).
    4. Add to Claude MCP:
    claude mcp add cclsp npx cclsp@latest --env CCLSP_CONFIG_PATH=/path/to/cclsp.json
    npx cclsp@latest setup
  6. Refactor code using rename_symbol and rename_symbol_strict

    main

    To rename a symbol across the entire codebase, use rename_symbol. If the tool finds multiple matching symbols and cannot determine which one you want to change, it will return candidate positions. In that case, use rename_symbol_strict by providing the exact line and character (both 1-indexed) of the target symbol.

    Safety Features:

    • Dry Run: Set dry_run: true to preview changes without modifying files.
    • Backups: When dry_run is false, the tool creates backup files with a .bak extension.
    • Scope: Both tools apply changes to all affected files by default.
    // Example: Using rename_symbol_strict after multiple candidates are found
    {
      "name": "rename_symbol_strict",
      "arguments": {
        "file_path": "src/utils/parser.ts",
        "line": 45,
        "character": 10,
        "new_name": "userData"
      }
    }
  7. How LSPClient selects the correct server

    main

    When you request an operation for a specific file, LSPClient automatically determines which LSP server to use based on the file's extension and configuration. The selection logic follows these rules:

    1. Extension Match: It identifies all servers in the configuration that include the file's extension in their extensions array.
    2. Specificity (rootDir): If multiple servers match the extension, the client selects the server whose rootDir is the most specific (the longest matching path) relative to the file's location.
    3. Fallback: If no rootDir matches the file's location, it falls back to the first matching server in the configuration.

    This allows you to have different language servers for different sub-projects within a single workspace by defining specific rootDir values in your configuration.

  8. Configure automatic Python LSP restarts

    main

    The Python Language Server (pylsp) may experience performance degradation or become unresponsive after extended use. To prevent this, you can configure an automatic restart interval in your server configuration. This ensures the server remains responsive during long coding sessions.

    {
      "servers": [
        {
          "extensions": ["py", "pyi"],
          "command": ["pylsp"],
          "restartInterval": 5
        }
      ]
    }
  9. Understand the ServerState interface

    main

    The ServerState interface represents the internal state of a running LSP server process. It manages the underlying ChildProcess, the communication transport, the documentManager for file lifecycle, and a diagnosticsCache for managing errors and warnings.

    Note: This is an internal interface used by the core engine to track server health, initialization status, and document versions. It is not intended for direct user extension.

    export interface ServerState {
      process: ChildProcess;
      transport: {
        sendRequest(method: string, params: unknown, timeout?: number): Promise<unknown>;
        sendMessage(message: LSPMessage): void;
        sendNotification(method: string, params: unknown): void;
        rejectAllPending(reason: string): void;
      };
      documentManager: {
        ensureOpen(filePath: string): Promise<boolean>;
        sendChange(filePath: string, text: string): void;
        isOpen(filePath: string): boolean;
        getVersion(filePath: string): number;
      };
      initialized: boolean;
      initializationPromise: Promise<void>;
      startTime: number;
      config: LSPServerConfig;
      restartTimer?: NodeJS.Timeout;
      initializationResolve?: () => void;
      diagnosticsCache: {
        update(uri: string, items: Diagnostic[], version?: number): void;
        get(uri: string): Diagnostic[] | undefined;
        waitForIdle(
          uri: string,
          options?: {
            maxWaitTime?: number;
            idleTime?: number;
            checkInterval?: number;
          }
        ): Promise<void>;
      };
      adapter?: ServerAdapter;
    }
  10. Configure cclsp via cclsp.json

    main

    cclsp uses a JSON configuration file to map file extensions to specific LSP server commands. You can generate this file using the interactive setup command or create it manually.

    Configuration Schema

    Each object in the servers array supports:

    • extensions: (Array) File extensions this server handles (e.g., ["py", "pyi"]).
    • command: (Array) The command and arguments used to spawn the LSP server.
    • rootDir: (String, optional) Working directory for the LSP server. Defaults to ".".
    • restartInterval: (Number, optional) Auto-restart interval in minutes.
    • initializationOptions: (Object, optional) LSP server initialization options, used for passing specific settings (like pylsp plugins) to the underlying server.

    Example: Python (pylsp) with plugins

    {
      "servers": [
        {
          "extensions": ["py", "pyi"],
          "command": ["uvx", "--from", "python-lsp-server", "pylsp"],
          "rootDir": ".",
          "initializationOptions": {
            "settings": {
              "pylsp": {
                "plugins": {
                  "jedi_completion": { "enabled": true },
                  "pylint": { "enabled": false }
                }
              }
            }
          }
        }
      ]
    }
    {
      "servers": [
        {
          "extensions": ["js", "ts", "jsx", "tsx"],
          "command": ["npx", "--", "typescript-language-server", "--stdio"],
          "rootDir": "."
        }
      ]
    }
  11. Understand the ServerAdapter interface

    main

    The ServerAdapter is an internal mechanism used to handle LSP servers that deviate from the standard protocol or require special handling.

    Warning: This is an internal interface. Currently, no user-facing extension mechanism is supported for creating custom adapters. The system uses built-in adapters that are auto-detected via the matches(config: LSPServerConfig) method.

    export interface ServerAdapter {
      readonly name: string;
      matches(config: LSPServerConfig): boolean;
      customizeInitializeParams?(params: InitializeParams): InitializeParams;
      handleNotification?(method: string, params: unknown, state: ServerState): boolean;
      handleRequest?(method: string, params: unknown, state: ServerState): Promise<unknown>;
      getTimeout?(method: string): number | undefined;
    }