Back to skills
SkillHub ClubShip Full StackFull StackBackend

lsp-implementation

Use when implementing Language Server Protocol features - covers completions, hover, diagnostics, and navigation

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
3
Hot score
80
Updated
March 20, 2026
Overall rating
C2.0
Composite score
2.0
Best-practice grade
N/A

Install command

npx @skill-hub/cli install mcclowes-lea-lsp-implementation

Repository

mcclowes/lea

Skill path: .claude/skills/lsp-implementation

Use when implementing Language Server Protocol features - covers completions, hover, diagnostics, and navigation

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack, Backend.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: mcclowes.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install lsp-implementation into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/mcclowes/lea before adding lsp-implementation to shared team environments
  • Use lsp-implementation for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: lsp-implementation
# prettier-ignore
description: Use when implementing Language Server Protocol features - covers completions, hover, diagnostics, and navigation
---

# Language Server Protocol Implementation

## Quick Start

```typescript
import {
  createConnection,
  TextDocuments,
  ProposedFeatures,
} from "vscode-languageserver/node";
import { TextDocument } from "vscode-languageserver-textdocument";

const connection = createConnection(ProposedFeatures.all);
const documents = new TextDocuments(TextDocument);

connection.onInitialize(() => ({
  capabilities: {
    textDocumentSync: TextDocumentSyncKind.Incremental,
    completionProvider: { triggerCharacters: [".", "/"] },
    hoverProvider: true,
    definitionProvider: true,
  }
}));

documents.listen(connection);
connection.listen();
```

## Core Features

### Completions

```typescript
connection.onCompletion((params): CompletionItem[] => {
  const document = documents.get(params.textDocument.uri);
  const position = params.position;

  // Get context at cursor
  const line = document?.getText({
    start: { line: position.line, character: 0 },
    end: position
  });

  // Return builtins
  return [
    { label: "map", kind: CompletionItemKind.Function, detail: "(fn) -> List" },
    { label: "filter", kind: CompletionItemKind.Function, detail: "(fn) -> List" },
    { label: "reduce", kind: CompletionItemKind.Function, detail: "(init, fn) -> Value" },
  ];
});
```

### Hover Information

```typescript
connection.onHover((params): Hover | null => {
  const document = documents.get(params.textDocument.uri);
  const word = getWordAtPosition(document, params.position);

  const builtin = BUILTINS[word];
  if (builtin) {
    return {
      contents: {
        kind: "markdown",
        value: `**${word}**\n\n${builtin.description}\n\n\`\`\`lea\n${builtin.signature}\n\`\`\``
      }
    };
  }
  return null;
});
```

### Diagnostics

```typescript
documents.onDidChangeContent((change) => {
  const document = change.document;
  const diagnostics: Diagnostic[] = [];

  try {
    parse(document.getText());
  } catch (error) {
    if (error instanceof ParseError) {
      diagnostics.push({
        severity: DiagnosticSeverity.Error,
        range: error.range,
        message: error.message,
        source: "lea"
      });
    }
  }

  connection.sendDiagnostics({ uri: document.uri, diagnostics });
});
```

### Go to Definition

```typescript
connection.onDefinition((params): Definition | null => {
  const document = documents.get(params.textDocument.uri);
  const word = getWordAtPosition(document, params.position);

  // Find definition in AST
  const definition = findDefinition(document.getText(), word);
  if (definition) {
    return {
      uri: params.textDocument.uri,
      range: definition.range
    };
  }
  return null;
});
```

## Protocol Messages

- `textDocument/completion` - Code completions
- `textDocument/hover` - Hover information
- `textDocument/definition` - Go to definition
- `textDocument/references` - Find all references
- `textDocument/documentSymbol` - Document outline
- `textDocument/formatting` - Code formatting

## Reference Files

- [references/capabilities.md](references/capabilities.md) - Server capabilities
- [references/diagnostics.md](references/diagnostics.md) - Error reporting


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### references/capabilities.md

```markdown
# Server Capabilities

## Initialization Response

```typescript
connection.onInitialize((params: InitializeParams): InitializeResult => {
  return {
    capabilities: {
      // Document sync
      textDocumentSync: TextDocumentSyncKind.Incremental,

      // Completions
      completionProvider: {
        triggerCharacters: [".", "/", "#"],
        resolveProvider: true,
      },

      // Hover
      hoverProvider: true,

      // Signature help
      signatureHelpProvider: {
        triggerCharacters: ["(", ","],
      },

      // Navigation
      definitionProvider: true,
      referencesProvider: true,
      documentHighlightProvider: true,

      // Symbols
      documentSymbolProvider: true,
      workspaceSymbolProvider: true,

      // Formatting
      documentFormattingProvider: true,
      documentRangeFormattingProvider: true,
      documentOnTypeFormattingProvider: {
        firstTriggerCharacter: "}",
        moreTriggerCharacter: [";", "\n"],
      },

      // Code actions
      codeActionProvider: {
        codeActionKinds: [
          CodeActionKind.QuickFix,
          CodeActionKind.Refactor,
        ],
      },

      // Rename
      renameProvider: {
        prepareProvider: true,
      },

      // Folding
      foldingRangeProvider: true,

      // Semantic tokens
      semanticTokensProvider: {
        legend: {
          tokenTypes: ["keyword", "variable", "function"],
          tokenModifiers: ["declaration", "definition"],
        },
        full: true,
        delta: true,
      },
    },
  };
});
```

## Capability Details

### Text Document Sync

```typescript
// Full sync - send entire document on change
textDocumentSync: TextDocumentSyncKind.Full

// Incremental - send only changes
textDocumentSync: TextDocumentSyncKind.Incremental

// Detailed options
textDocumentSync: {
  openClose: true,
  change: TextDocumentSyncKind.Incremental,
  save: { includeText: true },
}
```

### Completion Provider

```typescript
completionProvider: {
  // Characters that trigger completion
  triggerCharacters: [".", "#"],

  // Support resolving additional info
  resolveProvider: true,

  // All commit characters
  allCommitCharacters: ["\t", "\n"],
}
```

### Code Action Provider

```typescript
codeActionProvider: {
  codeActionKinds: [
    CodeActionKind.QuickFix,        // Fix errors
    CodeActionKind.Refactor,        // Refactorings
    CodeActionKind.RefactorExtract, // Extract method/variable
    CodeActionKind.Source,          // Source actions
    CodeActionKind.SourceOrganizeImports,
  ],
  resolveProvider: true,
}
```

## Dynamic Registration

```typescript
// Register capability dynamically
connection.client.register(CompletionRequest.type, {
  documentSelector: [{ language: "lea" }],
  triggerCharacters: ["."],
});
```

```

### references/diagnostics.md

```markdown
# Diagnostic Reporting

## Basic Diagnostics

```typescript
import {
  Diagnostic,
  DiagnosticSeverity,
  Position,
  Range,
} from "vscode-languageserver/node";

function createDiagnostic(
  message: string,
  line: number,
  startChar: number,
  endChar: number,
  severity: DiagnosticSeverity = DiagnosticSeverity.Error
): Diagnostic {
  return {
    severity,
    range: {
      start: { line, character: startChar },
      end: { line, character: endChar },
    },
    message,
    source: "lea",
  };
}
```

## Severity Levels

```typescript
DiagnosticSeverity.Error       // 1 - Red squiggle
DiagnosticSeverity.Warning     // 2 - Yellow squiggle
DiagnosticSeverity.Information // 3 - Blue squiggle
DiagnosticSeverity.Hint        // 4 - Faded text (3 dots)
```

## Publishing Diagnostics

```typescript
documents.onDidChangeContent((change) => {
  validateDocument(change.document);
});

async function validateDocument(document: TextDocument): Promise<void> {
  const diagnostics: Diagnostic[] = [];
  const text = document.getText();

  try {
    parse(text);
  } catch (error) {
    if (error instanceof ParseError) {
      diagnostics.push({
        severity: DiagnosticSeverity.Error,
        range: {
          start: { line: error.line - 1, character: error.column - 1 },
          end: { line: error.line - 1, character: error.column + 10 },
        },
        message: error.message,
        source: "lea",
        code: error.code,
      });
    }
  }

  // Send diagnostics to client
  connection.sendDiagnostics({
    uri: document.uri,
    diagnostics,
  });
}
```

## Diagnostic Tags

```typescript
{
  severity: DiagnosticSeverity.Hint,
  range: unusedVarRange,
  message: "Variable is declared but never used",
  tags: [DiagnosticTag.Unnecessary], // Faded text
}

{
  severity: DiagnosticSeverity.Warning,
  range: deprecatedFnRange,
  message: "Function is deprecated",
  tags: [DiagnosticTag.Deprecated], // Strikethrough
}
```

## Related Information

```typescript
{
  severity: DiagnosticSeverity.Error,
  range: errorRange,
  message: "Type mismatch",
  relatedInformation: [
    {
      location: {
        uri: document.uri,
        range: declarationRange,
      },
      message: "Variable declared as Number here",
    },
  ],
}
```

## Code Actions for Diagnostics

```typescript
connection.onCodeAction((params) => {
  const diagnostics = params.context.diagnostics;
  const actions: CodeAction[] = [];

  for (const diagnostic of diagnostics) {
    if (diagnostic.code === "undeclared-variable") {
      actions.push({
        title: `Declare variable '${diagnostic.data?.name}'`,
        kind: CodeActionKind.QuickFix,
        diagnostics: [diagnostic],
        edit: {
          changes: {
            [params.textDocument.uri]: [
              TextEdit.insert(
                { line: 0, character: 0 },
                `let ${diagnostic.data?.name} = undefined\n`
              ),
            ],
          },
        },
      });
    }
  }

  return actions;
});
```

## Clearing Diagnostics

```typescript
// Clear all diagnostics for a document
connection.sendDiagnostics({
  uri: document.uri,
  diagnostics: [],
});
```

```

lsp-implementation | SkillHub