Configures and orchestrates Language Server Protocol (LSP) clients, builds semantic code indexes, and enables code intelligence features like go-to-definition, find references, hover documentation, and auto-complete. Use when you need to set up or debug language servers (e.g., typescript-language-server, intelephense, pyright, gopls, rust-analyzer), implement code navigation in an editor or toolchain, build a unified symbol graph across multiple languages, configure LSP server connections and capability negotiation, or create real-time semantic indexes (LSIF, nav.index.jsonl) for large codebases.
90
88%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
You orchestrate Language Server Protocol clients and build unified code intelligence systems — transforming heterogeneous language servers into a cohesive semantic graph that powers code navigation and visualization.
nav.index.jsonl with symbol definitions, references, and hover documentation; support LSIF import/export| Endpoint / Event | Budget |
|---|---|
GET /graph (<10k nodes) | ≤ 100 ms |
GET /nav/:symId cached | ≤ 20 ms |
GET /nav/:symId uncached | ≤ 60 ms |
| WebSocket event latency | < 50 ms |
| Memory (typical project) | < 500 MB |
ServerCapabilities before invoking any feature — never assume supportinitialize → initialized → shutdown → exit// Use these as canonical schema definitions for graphd
interface GraphNode {
id: string; // "file:src/foo.ts" or "sym:foo#method"
kind: 'file' | 'module' | 'class' | 'function' | 'variable' | 'type';
file?: string; // Parent file path
range?: Range; // LSP Range for symbol location
detail?: string; // Type signature or brief description
}
interface GraphEdge {
id: string; // "edge:uuid"
source: string; // Node ID
target: string; // Node ID
type: 'contains' | 'imports' | 'extends' | 'implements' | 'calls' | 'references';
weight?: number; // For importance/frequency
}# Install language servers
npm install -g typescript-language-server typescript
npm install -g intelephense # PHP
npm install -g gopls # Go
npm install -g rust-analyzer # Rust
npm install -g pyright # Python
# Verify a server starts and responds (TypeScript example)
echo '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"capabilities":{},"rootUri":null,"processId":null}}' \
| typescript-language-server --stdio✅ Validation checkpoint: Confirm the initialize response contains a capabilities object. If the response is empty or errors, check PATH, node version, and that --stdio is supported by the installed version.
class LSPOrchestrator {
private clients = new Map<string, LanguageClient>();
private capabilities = new Map<string, ServerCapabilities>();
async initialize(projectRoot: string): Promise<void> {
const servers = [
{ lang: 'typescript', command: 'typescript-language-server', args: ['--stdio'] },
{ lang: 'php', command: 'intelephense', args: ['--stdio'] },
];
await Promise.all(servers.map(({ lang, command, args }) =>
this.initializeClient(lang, new LanguageClient(lang, { command, args, rootPath: projectRoot }))
));
}
private async initializeClient(lang: string, client: LanguageClient): Promise<void> {
const result = await client.sendRequest('initialize', {
processId: process.pid,
rootUri: `file://${client.rootPath}`,
capabilities: {},
});
// ✅ Validation: store capabilities before any further requests
this.capabilities.set(lang, result.capabilities);
this.clients.set(lang, client);
await client.sendNotification('initialized', {});
}
async getDefinition(uri: string, position: Position): Promise<Location[]> {
const lang = this.detectLanguage(uri);
const cap = this.capabilities.get(lang);
if (!cap?.definitionProvider) return []; // capability check before request
return this.clients.get(lang)!.sendRequest('textDocument/definition', {
textDocument: { uri },
position,
});
}
}✅ Validation checkpoint: After initialization, log capabilities for each server. Confirm definitionProvider, referencesProvider, and hoverProvider are present for expected languages before proceeding.
class GraphBuilder {
async buildFromProject(root: string): Promise<Graph> {
const graph = new Graph();
// Phase 1 & 2: Collect files and create file nodes
const files = await glob('**/*.{ts,tsx,js,jsx,php}', { cwd: root });
for (const file of files) {
graph.addNode({ id: `file:${file}`, kind: 'file', path: file });
}
// ✅ Validation: all file nodes must exist before symbol nodes are added
// Phase 3: Extract symbols via LSP (parallel)
await Promise.all(files.map(async (file) => {
const symbols = await this.extractSymbols(file); // calls textDocument/documentSymbol
for (const sym of symbols) {
graph.addNode({ id: `sym:${sym.name}`, kind: sym.kind, file, range: sym.range });
graph.addEdge({ source: `file:${file}`, target: `sym:${sym.name}`, type: 'contains' });
}
}));
// ✅ Validation: assert every symbol node's `file` references a known file node
// Phase 4: Resolve cross-file references and call edges
await this.resolveReferences(graph); // calls textDocument/references per symbol
// ✅ Validation: assert all edge source/target IDs exist in graph.nodes
return graph;
}
}✅ Validation checkpoint: After each phase, run a consistency check:
function assertGraphConsistency(graph: Graph): void {
for (const edge of graph.edges.values()) {
if (!graph.nodes.has(edge.source)) throw new Error(`Missing source node: ${edge.source}`);
if (!graph.nodes.has(edge.target)) throw new Error(`Missing target node: ${edge.target}`);
}
}GET /graph — return full graph snapshot (≤100ms for <10k nodes)GET /nav/:symId — symbol definition + references lookupGET /stats — node/edge counts, LSP client healthGraphDiff events on file change✅ Validation checkpoint: After wiring the watcher, save a test file and confirm a GraphDiff event arrives on the WebSocket within 500ms.
--prof (Node.js) or perf to find bottlenecks before optimizingtextDocument/references requests to minimize round-trip overheadtextDocument/didChange notificationsEach symbol's data is written as separate JSONL lines:
{"symId":"sym:AppController","def":{"uri":"file:///src/controllers/app.php","l":10,"c":6}}
{"symId":"sym:AppController","refs":[{"uri":"file:///src/routes.php","l":5,"c":10},{"uri":"file:///tests/app.test.php","l":15,"c":20}]}
{"symId":"sym:AppController","hover":{"contents":{"kind":"markdown","value":"```php\nclass AppController extends BaseController\n```\nMain application controller"}}}
{"symId":"sym:useState","def":{"uri":"file:///node_modules/react/index.d.ts","l":1234,"c":17}}
{"symId":"sym:useState","refs":[{"uri":"file:///src/App.tsx","l":3,"c":10},{"uri":"file:///src/components/Header.tsx","l":2,"c":10}]}010799b
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.