skills-templates/language-server-protocol/SKILL.md
Language Server Protocol (LSP) - Microsoft's open standard for IDE-language server communication. Use for building language servers, implementing LSP clients, understanding protocol architecture, and integrating code intelligence features.
npx skillsauth add enuno/claude-command-and-control language-server-protocolInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The Language Server Protocol (LSP) is an open standard that defines the protocol used between an editor or IDE and a language server that provides language features like auto-complete, go to definition, find all references, and more. Think of LSP as "USB-C for code intelligence" - a standardized interface that allows any language server to work with any compatible editor.
Core Value Proposition: Build once, integrate everywhere. A single language server implementation works across VS Code, Neovim, Emacs, Sublime Text, and dozens of other editors without modification.
This skill should be triggered when:
Before LSP:
After LSP:
Just as USB-C provides a universal connector:
┌─────────────────────────────────────────────────────────────┐
│ LSP ARCHITECTURE │
└─────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ EDITOR / IDE (Client) │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ LSP CLIENT │ │
│ │ • Sends document events (open, change, save) │ │
│ │ • Requests language features (completion, definition) │ │
│ │ • Displays diagnostics and suggestions │ │
│ └───────────┬────────────────────────────────────────────┘ │
└──────────────┼───────────────────────────────────────────────┘
│ JSON-RPC (stdio / TCP / WebSocket)
▼
┌──────────────────────────────────────────────────────────────┐
│ LANGUAGE SERVER │
│ │
│ • Parses and analyzes source code │
│ • Maintains project model and symbol tables │
│ • Responds to feature requests │
│ • Publishes diagnostics (errors, warnings) │
└──────────────────────────────────────────────────────────────┘
LSP uses JSON-RPC 2.0 with a specific message format:
Content-Length: 123\r\n
Content-Type: application/vscode-jsonrpc; charset=utf-8\r\n
\r\n
{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{...}}
Message Types:
| Type | Has ID | Expects Response | Example |
|------|--------|------------------|---------|
| Request | Yes | Yes | textDocument/completion |
| Response | Yes (matches request) | N/A | Result or error |
| Notification | No | No | textDocument/didOpen |
┌─────────────────────────────────────────────────────────────┐
│ LSP LIFECYCLE │
└─────────────────────────────────────────────────────────────┘
1. INITIALIZATION
Client ──initialize──────────► Server
└─ capabilities, rootUri, clientInfo
Client ◄──result────────────── Server
└─ capabilities, serverInfo
Client ──initialized─────────► Server (notification)
2. DOCUMENT SYNCHRONIZATION
Client ──didOpen─────────────► Server (file opened)
Client ──didChange───────────► Server (content changed)
Client ◄──publishDiagnostics── Server (errors/warnings)
Client ──didSave─────────────► Server (file saved)
Client ──didClose────────────► Server (file closed)
3. FEATURE REQUESTS
Client ──completion──────────► Server
Client ◄──completionItems───── Server
Client ──definition──────────► Server
Client ◄──location───────────── Server
4. SHUTDOWN
Client ──shutdown────────────► Server
Client ◄──result (null)─────── Server
Client ──exit────────────────► Server (notification)
| Method | Purpose | Returns |
|--------|---------|---------|
| textDocument/definition | Go to symbol definition | Location(s) |
| textDocument/declaration | Go to symbol declaration | Location(s) |
| textDocument/typeDefinition | Go to type definition | Location(s) |
| textDocument/implementation | Go to implementations | Location(s) |
| textDocument/references | Find all references | Location[] |
Example Request (Go to Definition):
{
"jsonrpc": "2.0",
"id": 1,
"method": "textDocument/definition",
"params": {
"textDocument": {
"uri": "file:///project/src/main.ts"
},
"position": {
"line": 10,
"character": 15
}
}
}
Example Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"uri": "file:///project/src/utils.ts",
"range": {
"start": { "line": 5, "character": 0 },
"end": { "line": 5, "character": 20 }
}
}
}
Request:
{
"jsonrpc": "2.0",
"id": 2,
"method": "textDocument/completion",
"params": {
"textDocument": { "uri": "file:///project/src/main.ts" },
"position": { "line": 12, "character": 8 },
"context": {
"triggerKind": 1,
"triggerCharacter": "."
}
}
}
Response:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"isIncomplete": false,
"items": [
{
"label": "toString",
"kind": 2,
"detail": "(): string",
"documentation": "Returns a string representation",
"insertText": "toString()"
},
{
"label": "valueOf",
"kind": 2,
"detail": "(): number",
"insertText": "valueOf()"
}
]
}
}
Completion Item Kinds: | Kind | Value | Kind | Value | |------|-------|------|-------| | Text | 1 | Method | 2 | | Function | 3 | Constructor | 4 | | Field | 5 | Variable | 6 | | Class | 7 | Interface | 8 | | Module | 9 | Property | 10 | | Snippet | 15 | Keyword | 14 |
Servers push diagnostics via notification:
{
"jsonrpc": "2.0",
"method": "textDocument/publishDiagnostics",
"params": {
"uri": "file:///project/src/main.ts",
"version": 3,
"diagnostics": [
{
"range": {
"start": { "line": 10, "character": 0 },
"end": { "line": 10, "character": 10 }
},
"severity": 1,
"code": "TS2322",
"source": "typescript",
"message": "Type 'string' is not assignable to type 'number'",
"relatedInformation": [
{
"location": {
"uri": "file:///project/src/types.ts",
"range": { "start": { "line": 5, "character": 2 }, "end": { "line": 5, "character": 8 } }
},
"message": "The expected type comes from property 'count'"
}
]
}
]
}
}
Diagnostic Severities: | Value | Severity | |-------|----------| | 1 | Error | | 2 | Warning | | 3 | Information | | 4 | Hint |
// Request
{
"method": "textDocument/hover",
"params": {
"textDocument": { "uri": "file:///project/src/main.ts" },
"position": { "line": 8, "character": 10 }
}
}
// Response
{
"result": {
"contents": {
"kind": "markdown",
"value": "```typescript\nfunction calculateSum(a: number, b: number): number\n```\n\nCalculates the sum of two numbers."
},
"range": {
"start": { "line": 8, "character": 4 },
"end": { "line": 8, "character": 16 }
}
}
}
Request quick fixes and refactorings:
// Request
{
"method": "textDocument/codeAction",
"params": {
"textDocument": { "uri": "file:///project/src/main.ts" },
"range": {
"start": { "line": 10, "character": 0 },
"end": { "line": 10, "character": 20 }
},
"context": {
"diagnostics": [...],
"only": ["quickfix"]
}
}
}
// Response
{
"result": [
{
"title": "Convert to template literal",
"kind": "refactor.rewrite",
"edit": {
"changes": {
"file:///project/src/main.ts": [
{
"range": { "start": { "line": 10, "character": 0 }, "end": { "line": 10, "character": 25 } },
"newText": "`Hello, ${name}!`"
}
]
}
}
}
]
}
// Request
{
"method": "textDocument/documentSymbol",
"params": {
"textDocument": { "uri": "file:///project/src/main.ts" }
}
}
// Response (hierarchical)
{
"result": [
{
"name": "Calculator",
"kind": 5,
"range": { "start": { "line": 0, "character": 0 }, "end": { "line": 20, "character": 1 } },
"selectionRange": { "start": { "line": 0, "character": 6 }, "end": { "line": 0, "character": 16 } },
"children": [
{
"name": "add",
"kind": 6,
"range": { "start": { "line": 2, "character": 2 }, "end": { "line": 4, "character": 3 } },
"selectionRange": { "start": { "line": 2, "character": 2 }, "end": { "line": 2, "character": 5 } }
}
]
}
]
}
Symbol Kinds: | Kind | Value | Kind | Value | |------|-------|------|-------| | File | 1 | Module | 2 | | Namespace | 3 | Package | 4 | | Class | 5 | Method | 6 | | Property | 7 | Field | 8 | | Constructor | 9 | Enum | 10 | | Interface | 11 | Function | 12 | | Variable | 13 | Constant | 14 |
initialize){
"capabilities": {
"textDocument": {
"synchronization": {
"dynamicRegistration": true,
"willSave": true,
"willSaveWaitUntil": true,
"didSave": true
},
"completion": {
"completionItem": {
"snippetSupport": true,
"commitCharactersSupport": true,
"documentationFormat": ["markdown", "plaintext"],
"resolveSupport": {
"properties": ["documentation", "detail"]
}
},
"contextSupport": true
},
"hover": {
"contentFormat": ["markdown", "plaintext"]
},
"definition": {
"linkSupport": true
},
"codeAction": {
"codeActionLiteralSupport": {
"codeActionKind": {
"valueSet": ["quickfix", "refactor", "source"]
}
}
}
},
"workspace": {
"workspaceFolders": true,
"configuration": true,
"didChangeConfiguration": {
"dynamicRegistration": true
}
}
}
}
initialize response){
"capabilities": {
"textDocumentSync": {
"openClose": true,
"change": 2,
"save": { "includeText": false }
},
"completionProvider": {
"triggerCharacters": [".", ":", "<"],
"resolveProvider": true
},
"hoverProvider": true,
"definitionProvider": true,
"referencesProvider": true,
"documentSymbolProvider": true,
"workspaceSymbolProvider": true,
"codeActionProvider": {
"codeActionKinds": ["quickfix", "refactor.extract", "source.organizeImports"]
},
"documentFormattingProvider": true,
"renameProvider": {
"prepareProvider": true
},
"diagnosticProvider": {
"interFileDependencies": true,
"workspaceDiagnostics": true
}
}
}
| Value | Mode | Description | |-------|------|-------------| | 0 | None | No synchronization | | 1 | Full | Full document on every change | | 2 | Incremental | Only send changes (preferred) |
import {
createConnection,
TextDocuments,
ProposedFeatures,
InitializeParams,
TextDocumentSyncKind,
InitializeResult,
CompletionItem,
CompletionItemKind,
TextDocumentPositionParams,
Diagnostic,
DiagnosticSeverity
} from 'vscode-languageserver/node';
import { TextDocument } from 'vscode-languageserver-textdocument';
// Create connection using all proposed features
const connection = createConnection(ProposedFeatures.all);
// Create document manager
const documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
connection.onInitialize((params: InitializeParams): InitializeResult => {
return {
capabilities: {
textDocumentSync: TextDocumentSyncKind.Incremental,
completionProvider: {
resolveProvider: true,
triggerCharacters: ['.']
},
hoverProvider: true,
definitionProvider: true,
referencesProvider: true
}
};
});
// Validate documents on change
documents.onDidChangeContent(change => {
validateTextDocument(change.document);
});
async function validateTextDocument(document: TextDocument): Promise<void> {
const diagnostics: Diagnostic[] = [];
const text = document.getText();
// Example: Find TODO comments
const todoPattern = /\bTODO\b/g;
let match;
while ((match = todoPattern.exec(text))) {
diagnostics.push({
severity: DiagnosticSeverity.Information,
range: {
start: document.positionAt(match.index),
end: document.positionAt(match.index + match[0].length)
},
message: 'TODO comment found',
source: 'my-language-server'
});
}
connection.sendDiagnostics({ uri: document.uri, diagnostics });
}
// Provide completions
connection.onCompletion((params: TextDocumentPositionParams): CompletionItem[] => {
return [
{
label: 'console',
kind: CompletionItemKind.Module,
detail: 'Console object',
documentation: 'The console object provides access to debugging console'
},
{
label: 'console.log',
kind: CompletionItemKind.Function,
detail: '(message: any): void',
insertText: 'console.log($1)',
insertTextFormat: 2 // Snippet
}
];
});
// Provide hover information
connection.onHover((params) => {
const document = documents.get(params.textDocument.uri);
if (!document) return null;
// Get word at position and return hover info
return {
contents: {
kind: 'markdown',
value: '**Symbol Info**\n\nDocumentation here'
}
};
});
// Listen for document events
documents.listen(connection);
// Start the connection
connection.listen();
from pygls.server import LanguageServer
from lsprotocol import types as lsp
server = LanguageServer("my-language-server", "v1.0")
@server.feature(lsp.INITIALIZE)
def initialize(params: lsp.InitializeParams) -> lsp.InitializeResult:
return lsp.InitializeResult(
capabilities=lsp.ServerCapabilities(
text_document_sync=lsp.TextDocumentSyncOptions(
open_close=True,
change=lsp.TextDocumentSyncKind.Incremental,
),
completion_provider=lsp.CompletionOptions(
trigger_characters=["."],
resolve_provider=True,
),
hover_provider=True,
definition_provider=True,
)
)
@server.feature(lsp.TEXT_DOCUMENT_DID_OPEN)
def did_open(params: lsp.DidOpenTextDocumentParams):
"""Handle document open."""
validate_document(params.text_document.uri)
@server.feature(lsp.TEXT_DOCUMENT_DID_CHANGE)
def did_change(params: lsp.DidChangeTextDocumentParams):
"""Handle document changes."""
validate_document(params.text_document.uri)
def validate_document(uri: str):
"""Validate document and publish diagnostics."""
document = server.workspace.get_text_document(uri)
diagnostics = []
# Example: Find syntax issues
for i, line in enumerate(document.lines):
if "TODO" in line:
diagnostics.append(lsp.Diagnostic(
range=lsp.Range(
start=lsp.Position(line=i, character=line.index("TODO")),
end=lsp.Position(line=i, character=line.index("TODO") + 4),
),
message="TODO comment found",
severity=lsp.DiagnosticSeverity.Information,
source="my-language-server",
))
server.publish_diagnostics(uri, diagnostics)
@server.feature(lsp.TEXT_DOCUMENT_COMPLETION)
def completions(params: lsp.CompletionParams) -> lsp.CompletionList:
"""Provide completion items."""
return lsp.CompletionList(
is_incomplete=False,
items=[
lsp.CompletionItem(
label="print",
kind=lsp.CompletionItemKind.Function,
detail="print(*args, **kwargs)",
documentation="Print to stdout",
),
lsp.CompletionItem(
label="len",
kind=lsp.CompletionItemKind.Function,
detail="len(obj) -> int",
documentation="Return the length of an object",
),
],
)
@server.feature(lsp.TEXT_DOCUMENT_HOVER)
def hover(params: lsp.HoverParams) -> lsp.Hover | None:
"""Provide hover information."""
document = server.workspace.get_text_document(params.text_document.uri)
# Get word at position and return info
return lsp.Hover(
contents=lsp.MarkupContent(
kind=lsp.MarkupKind.Markdown,
value="**Symbol Info**\n\nDocumentation here",
)
)
if __name__ == "__main__":
server.start_io()
import * as cp from 'child_process';
import * as rpc from 'vscode-jsonrpc/node';
// Spawn the language server
const serverProcess = cp.spawn('node', ['path/to/server.js']);
// Create JSON-RPC connection
const connection = rpc.createMessageConnection(
new rpc.StreamMessageReader(serverProcess.stdout),
new rpc.StreamMessageWriter(serverProcess.stdin)
);
// Listen for notifications from server
connection.onNotification('textDocument/publishDiagnostics', (params) => {
console.log('Diagnostics:', params.diagnostics);
});
// Start connection
connection.listen();
// Send initialize request
const initResult = await connection.sendRequest('initialize', {
processId: process.pid,
rootUri: 'file:///path/to/workspace',
capabilities: {
textDocument: {
completion: { completionItem: { snippetSupport: true } },
hover: { contentFormat: ['markdown'] }
}
}
});
console.log('Server capabilities:', initResult.capabilities);
// Send initialized notification
connection.sendNotification('initialized', {});
// Open a document
connection.sendNotification('textDocument/didOpen', {
textDocument: {
uri: 'file:///path/to/file.ts',
languageId: 'typescript',
version: 1,
text: 'const x = 1;\nconsole.log(x);'
}
});
// Request completion
const completions = await connection.sendRequest('textDocument/completion', {
textDocument: { uri: 'file:///path/to/file.ts' },
position: { line: 1, character: 8 }
});
console.log('Completions:', completions);
// Shutdown
await connection.sendRequest('shutdown');
connection.sendNotification('exit');
| Language | SDK | Repository | |----------|-----|------------| | TypeScript | vscode-languageserver | microsoft/vscode-languageserver-node | | Python | pygls | openlawlibrary/pygls | | Java | LSP4J | eclipse/lsp4j | | Rust | tower-lsp | tower-rs/tower-lsp | | C# | OmniSharp | OmniSharp/csharp-language-server-protocol | | Go | go-lsp | sourcegraph/go-lsp | | Haskell | lsp | haskell/lsp |
| Language | Server | Notes | |----------|--------|-------| | TypeScript/JavaScript | typescript-language-server | Uses tsserver | | Python | pyright, pylsp | Static typing / general | | Rust | rust-analyzer | Official Rust analyzer | | Go | gopls | Official Go team | | C/C++ | clangd | LLVM-based | | Java | Eclipse JDT LS | Used by VS Code Java | | C# | OmniSharp | .NET ecosystem |
Most clients support logging LSP messages:
VS Code (settings.json):
{
"myExtension.trace.server": "verbose"
}
Neovim (Lua):
vim.lsp.set_log_level("debug")
-- Logs at: ~/.local/state/nvim/lsp.log
# Start server and send messages manually
echo 'Content-Length: 108\r\n\r\n{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"processId":null,"rootUri":null,"capabilities":{}}}' | node server.js
Server doesn't start:
No completions:
completionProvider capability is advertiseddidOpen sent)Diagnostics not showing:
textDocumentSync capabilitypublishDiagnostics notifications are being sent$/cancelRequest$/progress for long operations// Debounce document changes
let validationTimeout: NodeJS.Timeout;
documents.onDidChangeContent(change => {
clearTimeout(validationTimeout);
validationTimeout = setTimeout(() => {
validateDocument(change.document);
}, 500);
});
// Support cancellation
connection.onDefinition(async (params, token) => {
// Check cancellation periodically
if (token.isCancellationRequested) {
return null;
}
const result = await findDefinition(params);
if (token.isCancellationRequested) {
return null;
}
return result;
});
Display inline parameter names, type annotations:
{
"method": "textDocument/inlayHint",
"params": {
"textDocument": { "uri": "file:///project/src/main.ts" },
"range": { "start": { "line": 0, "character": 0 }, "end": { "line": 50, "character": 0 } }
}
}
// Response
{
"result": [
{
"position": { "line": 10, "character": 15 },
"label": ": number",
"kind": 1,
"paddingLeft": true
}
]
}
Navigate type relationships:
// Prepare
{ "method": "textDocument/prepareTypeHierarchy", "params": { "textDocument": {...}, "position": {...} } }
// Get supertypes
{ "method": "typeHierarchy/supertypes", "params": { "item": {...} } }
// Get subtypes
{ "method": "typeHierarchy/subtypes", "params": { "item": {...} } }
Client-initiated diagnostics (alternative to push):
// Request diagnostics for a document
{ "method": "textDocument/diagnostic", "params": { "textDocument": { "uri": "..." } } }
// Request workspace-wide diagnostics
{ "method": "workspace/diagnostic", "params": { "previousResultIds": [...] } }
LSP DevTools is a collection of CLI utilities for inspecting and visualizing language server interactions. Essential for debugging protocol issues.
Installation:
pipx install lsp-devtools
Architecture: The LSP Agent acts as a proxy between client and server, forwarding messages while copying them to a monitoring application.
┌────────────────┐ ┌─────────────┐ ┌─────────────────┐
│ Language Client│◄────►│ LSP Agent │◄────►│ Language Server │
└────────────────┘ └──────┬──────┘ └─────────────────┘
│
▼ TCP (localhost:8765)
┌──────────────┐
│ lsp-devtools │
│ record / │
│ inspect │
└──────────────┘
Wrap your language server to enable inspection:
# Basic usage - wrap server command
lsp-devtools agent -- <server-cmd>
# Custom host/port
lsp-devtools agent --host 127.0.0.1 --port 1234 -- python -m my_server
# Example: wrap esbonio server
lsp-devtools agent -- esbonio
Neovim Configuration:
-- In nvim-lspconfig setup
require('lspconfig').esbonio.setup {
cmd = { "lsp-devtools", "agent", "--", "esbonio" }
}
VS Code Configuration:
{
"myLanguage.server.path": "lsp-devtools",
"myLanguage.server.args": ["agent", "--", "my-server"]
}
Capture LSP sessions to various destinations:
# Record to console (pretty-printed JSON)
lsp-devtools record
# Record to file (JSON-RPC, one message per line)
lsp-devtools record --to-file session.jsonl
# Record to SQLite database (for analysis)
lsp-devtools record --to-sqlite session.db
# Save console output as HTML/SVG
lsp-devtools record --save-output session.html
Filtering Options:
# Filter by source
lsp-devtools record --message-source client
lsp-devtools record --message-source server
# Filter by message type
lsp-devtools record --include-message-type request
lsp-devtools record --include-message-type notification
# Filter by method
lsp-devtools record --include-method textDocument/completion
lsp-devtools record --exclude-method textDocument/didChange
# Custom format
lsp-devtools record -f "{message.method}: {message.params.position.line}"
Interactive TUI for real-time LSP message inspection:
# Launch inspector
lsp-devtools inspect
# Custom port
lsp-devtools inspect --port 1234
Features:
End-to-end testing framework for language servers, built on pygls.
Installation:
pip install pytest-lsp
Key Features:
import pytest
import pytest_lsp
from pytest_lsp import ClientServerConfig, LanguageClient
from lsprotocol.types import (
InitializeParams,
CompletionParams,
TextDocumentIdentifier,
Position,
)
@pytest_lsp.fixture(
config=ClientServerConfig(
server_command=["python", "-m", "my_server"],
),
)
async def client(lsp_client: LanguageClient):
# Setup: Initialize the LSP session
await lsp_client.initialize_session(
InitializeParams(
capabilities={},
root_uri="file:///workspace",
)
)
yield
# Teardown: Shutdown gracefully
await lsp_client.shutdown_session()
@pytest.mark.asyncio
async def test_completions(client: LanguageClient):
"""Test that completion returns expected items."""
result = await client.text_document_completion_async(
CompletionParams(
text_document=TextDocumentIdentifier(uri="file:///test.py"),
position=Position(line=0, character=0),
)
)
labels = [item.label for item in result.items]
assert "hello" in labels
assert "world" in labels
Test against multiple client configurations:
@pytest.fixture(params=["neovim", "vscode", "emacs"])
def client_name(request):
return request.param
@pytest_lsp.fixture(
config=ClientServerConfig(
server_command=["python", "-m", "my_server"],
),
)
async def client(lsp_client: LanguageClient, client_name: str):
# Get capabilities for specific client
capabilities = client_capabilities(client_name)
await lsp_client.initialize_session(
InitializeParams(
capabilities=capabilities,
client_info={"name": client_name},
)
)
yield
await lsp_client.shutdown_session()
@pytest.mark.asyncio
async def test_diagnostics(client: LanguageClient):
"""Test diagnostic publishing."""
# Open a document with errors
client.text_document_did_open(
TextDocumentItem(
uri="file:///test.py",
language_id="python",
version=1,
text="def foo(\n invalid syntax",
)
)
# Wait for diagnostics
await client.wait_for_notification("textDocument/publishDiagnostics")
# Check diagnostics were received
diagnostics = client.diagnostics["file:///test.py"]
assert len(diagnostics) > 0
assert diagnostics[0].severity == DiagnosticSeverity.Error
Servers must explicitly start I/O handling:
# In your server's __main__.py
if __name__ == "__main__":
server = MyLanguageServer()
server.start_io() # Don't forget this!
When building browser-based LSP clients with Monaco Editor, use monaco-languageserver-types for bidirectional type conversion.
Installation:
npm install monaco-languageserver-types
Key Concept: Monaco Editor and LSP use different type representations. This library provides from* and to* functions for seamless conversion:
from* - Convert Monaco types → LSP typesto* - Convert LSP types → Monaco typesPosition & Range:
import { fromRange, toRange, fromPosition, toPosition } from 'monaco-languageserver-types';
// Monaco uses 1-based lines, LSP uses 0-based
const monacoRange = {
startLineNumber: 1,
startColumn: 2,
endLineNumber: 3,
endColumn: 4
};
// Convert to LSP format
const lspRange = fromRange(monacoRange);
// { start: { line: 0, character: 1 }, end: { line: 2, character: 3 } }
// Convert back to Monaco format
const backToMonaco = toRange(lspRange);
// { startLineNumber: 1, startColumn: 2, endLineNumber: 3, endColumn: 4 }
Diagnostics:
import { fromMarkerData, toMarkerData, fromMarkerSeverity } from 'monaco-languageserver-types';
// Convert Monaco marker to LSP diagnostic
const monacoMarker = {
severity: monaco.MarkerSeverity.Error,
message: "Unexpected token",
startLineNumber: 5,
startColumn: 10,
endLineNumber: 5,
endColumn: 15
};
const lspDiagnostic = fromMarkerData(monacoMarker);
// Ready to send to language server
// Convert LSP diagnostic to Monaco marker
const marker = toMarkerData(lspDiagnostic);
monaco.editor.setModelMarkers(model, 'lsp', [marker]);
Completion Items:
import { fromCompletionItem, toCompletionItem, toCompletionList } from 'monaco-languageserver-types';
// Handle LSP completion response for Monaco
connection.onCompletion(async (params) => {
const lspCompletions = await languageServer.getCompletions(params);
return lspCompletions;
});
// In Monaco provider
const monacoProvider: monaco.languages.CompletionItemProvider = {
provideCompletionItems: async (model, position) => {
const lspPosition = fromPosition(position);
const lspResult = await client.sendRequest('textDocument/completion', {
textDocument: { uri: model.uri.toString() },
position: lspPosition
});
return toCompletionList(lspResult);
}
};
Hover:
import { fromPosition, toHover } from 'monaco-languageserver-types';
const hoverProvider: monaco.languages.HoverProvider = {
provideHover: async (model, position) => {
const lspHover = await client.sendRequest('textDocument/hover', {
textDocument: { uri: model.uri.toString() },
position: fromPosition(position)
});
return lspHover ? toHover(lspHover) : null;
}
};
| Category | Functions |
|----------|-----------|
| Structural | fromPosition, toPosition, fromRange, toRange, fromLocation, toLocation |
| Diagnostics | fromMarkerData, toMarkerData, fromMarkerSeverity, toMarkerSeverity |
| Completion | fromCompletionItem, toCompletionItem, toCompletionList, fromCompletionItemKind |
| Code Actions | fromCodeAction, toCodeAction, fromCodeActionContext |
| Navigation | fromDefinition, toDefinition, fromDocumentHighlight, toDocumentHighlight |
| Symbols | fromDocumentSymbol, toDocumentSymbol, fromSymbolKind, toSymbolKind |
| Formatting | fromTextEdit, toTextEdit, fromFormattingOptions |
| Semantic | fromSemanticTokens, toSemanticTokens, fromInlayHint, toInlayHint |
| Workspace | fromWorkspaceEdit, toWorkspaceEdit |
import * as monaco from 'monaco-editor';
import {
fromPosition, fromRange, toCompletionList, toHover,
toMarkerData, toDocumentHighlight, toCodeAction
} from 'monaco-languageserver-types';
import { createLanguageClient } from './lsp-client';
// Create LSP client connection
const client = createLanguageClient('ws://localhost:3000/lsp');
// Register Monaco providers that bridge to LSP
monaco.languages.registerCompletionItemProvider('typescript', {
triggerCharacters: ['.', '"', "'", '/'],
provideCompletionItems: async (model, position) => {
const result = await client.textDocumentCompletion({
textDocument: { uri: model.uri.toString() },
position: fromPosition(position)
});
return toCompletionList(result);
}
});
monaco.languages.registerHoverProvider('typescript', {
provideHover: async (model, position) => {
const result = await client.textDocumentHover({
textDocument: { uri: model.uri.toString() },
position: fromPosition(position)
});
return result ? toHover(result) : null;
}
});
// Handle diagnostics from server
client.onNotification('textDocument/publishDiagnostics', (params) => {
const model = monaco.editor.getModel(monaco.Uri.parse(params.uri));
if (model) {
const markers = params.diagnostics.map(toMarkerData);
monaco.editor.setModelMarkers(model, 'lsp', markers);
}
});
1.2.0 (2026-01-11): Added Monaco Editor integration
1.1.0 (2026-01-11): Added Development Tools section
1.0.0 (2026-01-10): Initial skill release
tools
MemPalace local-first AI memory system. Use when setting up persistent memory for Claude Code sessions, mining project files or conversation transcripts, querying past context, configuring MCP tools, managing the knowledge graph, or troubleshooting palace operations.
tools
LangSmith Python SDK — trace, evaluate, and monitor LLM applications. Covers @traceable decorator, trace context manager, Client API, evaluate() / aevaluate(), comparative evaluation, custom evaluators, dataset management, prompt caching, ASGI middleware, and pytest plugin.
development
LangGraph (Python) — build stateful, controllable agent graphs with checkpointing, streaming, persistence, interrupts, fault tolerance, and durable execution. Covers both Graph API (StateGraph) and Functional API (@entrypoint/@task).
development
LangGraph Graph API (Python) — build explicit DAG agent workflows with StateGraph, typed state, nodes, edges, Command routing, Send fan-out, checkpointers, interrupts, and streaming. Use when you need explicit control flow and graph topology.