lsmcp

repository·main·Indexed 19 days ago

https://github.com/mizchi/lsmcp

A unified Model Context Protocol (MCP) server that integrates with Language Server Protocols (LSP) to provide AI agents with semantic code analysis, symbol searching, and code manipulation. It supports multiple programming languages including TypeScript (via tsgo), Rust (via rust-analyzer), Python (via pyright), F# (via fsautocomplete), and Moonbit. Key features include project overviews, symbol indexing, and a suite of LSP tools for hover information, diagnostics, and refactoring.

Tokens
89.5K
Snippets
270
Records
369
Agent score
62%

What's inside @mizchi/lsmcp

  1. What is LSIF (Language Server Index Format)

    main

    LSIF is an interchange format designed to enable rich code navigation (such as Hover, Go to Definition, and Find All References) in tools or web UIs without requiring a local repository clone.

    Unlike traditional Language Server Protocol (LSP) implementations that assume local files and in-memory analysis, LSIF allows language servers to precompute and emit knowledge about a workspace. This persisted data can then be consumed by services to answer LSP-equivalent queries without launching a full language server at runtime, making it ideal for PR reviews or browser-based code browsing.

  2. What is the LSMCP Memory Report System?

    main

    The Memory Report System provides a way to create comprehensive snapshots of your project's state at specific git commits. These Reports act as temporal investigation records that combine mechanical metrics (file statistics, symbol analysis, dependencies) with optional AI-generated insights (architecture assessment, technical debt, code quality).

    Reports are stored locally in a SQLite database (.lsmcp/cache/memory.db) and are intended for later review, trend analysis, and decision-making rather than being part of the permanent codebase.

  3. Understand WorkspaceEdit Failure Handling Strategies

    main

    When a WorkspaceEdit fails to apply, the client's behavior is determined by its FailureHandlingKind. The available strategies are:

    • abort: The operation is stopped immediately. Changes applied before the failure remain.
    • transactional: All operations are executed as a single transaction; either all succeed or none are applied.
    • textOnlyTransactional: If the edit only contains text changes, they are executed transactionally. If it contains resource operations (create/rename/delete), the strategy reverts to abort.
    • undo: The client attempts to undo operations already executed, though success is not guaranteed.
    export type FailureHandlingKind = 'abort' | 'transactional' | 'undo' | 'textOnlyTransactional';
  4. Understand Semantic Tokens in LSP

    main

    Semantic tokens are used by the client to apply language-specific color information (syntax highlighting) to a file based on semantic information provided by the server. Instead of simple regex-based highlighting, semantic tokens allow for more accurate coloring of elements like classes, functions, and variables.

    Tokens are composed of a token type (e.g., class, function) and zero or more token modifiers (e.g., static, async).

    Predefined Token Types

    Common types include:

    • namespace, type, class, enum, interface, struct, typeParameter, parameter, variable, property, enumMember, event, function, method, macro, keyword, modifier, comment, string, number, regexp, operator, and decorator (since 3.17.0).

    Predefined Token Modifiers

    Common modifiers include:

    • declaration, definition, readonly, static, deprecated, abstract, async, modification, documentation, and defaultLibrary.
    export enum SemanticTokenTypes {
    	namespace = 'namespace',
    	type = 'type',
    	class = 'class',
    	enum = 'enum',
    	interface = 'interface',
    	struct = 'struct',
    	typeParameter = 'typeParameter',
    	parameter = 'parameter',
    	variable = 'variable',
    	property = 'property',
    	enumMember = 'enumMember',
    	event = 'event',
    	function = 'function',
    	method = 'method',
    	macro = 'macro',
    	keyword = 'keyword',
    	modifier = 'modifier',
    	comment = 'comment',
    	string = 'string',
    	number = 'number',
    	regexp = 'regexp',
    	operator = 'operator',
    	decorator = 'decorator' // since 3.17.0
    }
    
    export enum SemanticTokenModifiers {
    	declaration = 'declaration',
    	definition = 'definition',
    	readonly = 'readonly',
    	static = 'static',
    	deprecated = 'deprecated',
    	abstract = 'abstract',
    	async = 'async',
    	modification = 'modification',
    	documentation = 'documentation',
    	defaultLibrary = 'defaultLibrary'
    }
  5. How Type Hierarchy works (Prepare, Supertypes, and Subtypes)

    main

    Type hierarchy requests (introduced in LSP 3.17.0) allow clients to explore the inheritance or implementation structure of a type. The process follows a two-step workflow:

    1. Prepare: The client calls textDocument/prepareTypeHierarchy at a specific position. The server returns a TypeHierarchyItem[]. This item may include a data field used to preserve state between steps.
    2. Resolve: Using the TypeHierarchyItem obtained from the prepare step, the client calls either typeHierarchy/supertypes or typeHierarchy/subtypes to traverse the hierarchy.

    Key Types:

    • TypeHierarchyItem: Contains the name, kind (SymbolKind), uri, and range of the type. It also includes a selectionRange for UI highlighting and an optional data field for server-side performance optimization.
    // Step 1: Prepare
    // method: 'textDocument/prepareTypeHierarchy'
    // params: TypeHierarchyPrepareParams
    
    // Step 2: Resolve (either)
    // method: 'typeHierarchy/supertypes'
    // method: 'typeHierarchy/subtypes'
    // params: { item: TypeHierarchyItem }
  6. Use `CompletionList` for incomplete completions

    main

    If a server cannot provide all possible completions at once, it should return a CompletionList with isIncomplete: true.

    Item Defaults (v3.17.0+): To reduce payload size, a CompletionList can define itemDefaults which apply to all items in the list unless the item explicitly overrides them. This requires the client to support the completionList.itemDefaults capability.

    Supported defaults:

    • commitCharacters: A default set of characters that commit the completion.
    • editRange: A default range for text edits.
    • insertTextFormat: A default format (Plain Text or Snippet).
    • insertTextMode: A default indentation handling mode.
    • data: A default data value.
    export interface CompletionList {
      isIncomplete: boolean;
      itemDefaults?: {
        commitCharacters?: string[];
        editRange?: Range | { insert: Range; replace: Range; };
        insertTextFormat?: InsertTextFormat;
        insertTextMode?: InsertTextMode;
        data?: LSPAny;
      };
      items: CompletionItem[];
    }
  7. Understand the Language Server Protocol (LSP) message exchange

    main

    The Language Server Protocol (LSP) uses JSON-RPC to exchange requests, responses, and notifications between a client and a server.

    Key behaviors:

    • Request/Response: A client sends a request (e.g., textDocument/hover) and the server returns a response. A null response value indicates no result and does not trigger a retry.
    • Ordering: Responses should generally follow the order of requests. However, servers may use parallel execution and return responses out of order if it does not affect correctness (e.g., reordering textDocument/completion and textDocument/signatureHelp is allowed, but reordering textDocument/definition and textDocument/rename is not).
    • Capabilities: Since not all servers support all features, capabilities are exchanged during the initialize request. Clients and servers use these to announce supported features (e.g., a server announcing it supports textDocument/hover).
    • Parameter Types: While the protocol uses JSON-RPC, parameters for requests/notifications are expected to be of object type (though Array is permitted for custom messages).
  8. Handle LSP Enumerations

    main

    LSP supports both integer-based and string-based enumerations.

    Best Practices for Evolution: To ensure forward compatibility, the side using the enumeration (client or server) must not fail when encountering an unknown value. Instead, it should ignore the value and attempt to preserve it during round trips.

    Example: If a client announces a new CompletionItemKind value that an older server does not recognize, the server should treat it as an unknown item kind rather than throwing an error.

  9. Handle Position Encoding Agreement

    main

    Clients and servers must agree on a positionEncoding to ensure character offsets (like line/column positions) are interpreted identically.

    • Client side: The client provides supported encodings via general.positionEncodings. If omitted, it defaults to ['utf-16'].
    • Server side: The server selects one encoding from the client's list via positionEncoding. If the client provides no encodings, the server MUST use 'utf-16'. If omitted, it defaults to 'utf-16'.
  10. Use Request, Response, and Notification Messages

    main

    LSP communication relies on three message types:

    1. RequestMessage: Sent by a client or server to invoke a method. Every request must receive a response.
    2. ResponseMessage: The result of a request. If the request is successful but has no result, result should be null. If the request fails, use the error property.
    3. NotificationMessage: An event-like message that does not require a response.

    Note on $/ methods: Methods starting with $/ are implementation-dependent. If a server receives a $/ request it doesn't support, it must return MethodNotFound (-32601).

    interface RequestMessage extends Message {
    	id: integer | string;
    	method: string;
    	params?: array | object;
    }
    
    interface ResponseMessage extends Message {
    	id: integer | string | null;
    	result?: LSPAny;
    	error?: ResponseError;
    }
    
    interface NotificationMessage extends Message {
    	method: string;
    	params?: array | object;
    }
  11. Handle the initialize request lifecycle

    main

    The initialize request is the first request sent by a client. It establishes the server's capabilities and the client's environment.

    Lifecycle Rules:

    • Pre-initialization: If a server receives a request before initialize, it must return an error with code: -32002. Notifications (except exit) should be dropped.
    • During initialization: The client must not send further requests until the server responds with InitializeResult. The server is only allowed to send window/showMessage, window/logMessage, telemetry/event, and window/showMessageRequest during this phase.
    • Post-initialization: Normal request/response flow begins.
  12. Understand the `lsmcp_detect_dead_code` tool design

    main

    The lsmcp_detect_dead_code tool is designed to detect unused exports, imports, and local declarations in TypeScript projects. It uses static analysis and dependency graph traversal via a 'Mark and Sweep' algorithm.

    Core Algorithm

    1. Build Module Graph: Parses TypeScript files to create a graph of modules, exports, imports, and re-exports.
    2. Mark Phase: Starting from defined entry points, the tool marks all reachable exports.
    3. Sweep Phase: Any exports or imports left unmarked are identified as dead code.

    Key Capabilities

    • Entry Point Detection: Supports multiple entry points and regex patterns (e.g., 'src/main\.ts$') to define the roots of the dependency graph. It also supports auto-detection of common files like index.ts or main.ts.
    • Comprehensive Analysis: Handles named exports, default exports, re-exports (export * from ...), and type exports. It also analyzes various import styles (named, default, namespace, and side-effect imports).
    • Special Case Handling: Automatically marks dynamic imports and global augmentations as 'used' to prevent false positives.