Skip to main content
Glama

VSCode Language Server MCP Extension

by aaddrick
languageServerTools.ts9.73 kB
import * as vscode from 'vscode'; import { Logger } from './logger'; export class LanguageServerTools { constructor(private logger: Logger) {} async getSymbols(uriString: string) { try { const uri = vscode.Uri.parse(uriString); this.logger.debug(`Getting symbols for ${uri.fsPath}`); const symbols = await vscode.commands.executeCommand<vscode.DocumentSymbol[]>( 'vscode.executeDocumentSymbolProvider', uri ); if (!symbols) { return { symbols: [] }; } // Convert to serializable format const serializableSymbols = symbols.map(sym => this.serializeSymbol(sym)); this.logger.debug(`Found ${symbols.length} top-level symbols`); return { symbols: serializableSymbols }; } catch (error) { this.logger.error('Error getting symbols:', error); throw new Error(`Failed to get symbols: ${error}`); } } async findReferences(uriString: string, line: number, character: number) { try { const uri = vscode.Uri.parse(uriString); const position = new vscode.Position(line, character); this.logger.debug(`Finding references at ${uri.fsPath}:${line}:${character}`); const locations = await vscode.commands.executeCommand<vscode.Location[]>( 'vscode.executeReferenceProvider', uri, position ); if (!locations) { return { references: [] }; } const references = locations.map(loc => ({ uri: loc.uri.toString(), range: { start: { line: loc.range.start.line, character: loc.range.start.character }, end: { line: loc.range.end.line, character: loc.range.end.character } } })); this.logger.debug(`Found ${references.length} references`); return { references }; } catch (error) { this.logger.error('Error finding references:', error); throw new Error(`Failed to find references: ${error}`); } } async rename(uriString: string, line: number, character: number, newName: string) { try { const uri = vscode.Uri.parse(uriString); const position = new vscode.Position(line, character); this.logger.debug(`Renaming symbol at ${uri.fsPath}:${line}:${character} to ${newName}`); const workspaceEdit = await vscode.commands.executeCommand<vscode.WorkspaceEdit>( 'vscode.executeDocumentRenameProvider', uri, position, newName ); if (!workspaceEdit) { return { success: false, message: 'No workspace edit returned' }; } // Apply the edit const success = await vscode.workspace.applyEdit(workspaceEdit); const changesCount = workspaceEdit.size; const changes: any[] = []; // Collect all changes for reporting workspaceEdit.entries().forEach(([uri, edits]) => { edits.forEach(edit => { changes.push({ uri: uri.toString(), range: { start: { line: edit.range.start.line, character: edit.range.start.character }, end: { line: edit.range.end.line, character: edit.range.end.character } }, oldText: '', // Not available from WorkspaceEdit newText: edit.newText }); }); }); this.logger.info(`Renamed ${changesCount} occurrences to ${newName}`); return { success, changesCount, changes }; } catch (error) { this.logger.error('Error renaming symbol:', error); throw new Error(`Failed to rename symbol: ${error}`); } } async getDefinition(uriString: string, line: number, character: number) { try { const uri = vscode.Uri.parse(uriString); const position = new vscode.Position(line, character); this.logger.debug(`Getting definition at ${uri.fsPath}:${line}:${character}`); const locations = await vscode.commands.executeCommand< vscode.Location[] | vscode.LocationLink[] >( 'vscode.executeDefinitionProvider', uri, position ); if (!locations || locations.length === 0) { return { definitions: [] }; } const definitions = locations.map(loc => { if ('targetUri' in loc) { // LocationLink return { uri: loc.targetUri.toString(), range: { start: { line: loc.targetRange.start.line, character: loc.targetRange.start.character }, end: { line: loc.targetRange.end.line, character: loc.targetRange.end.character } } }; } else { // Location return { uri: loc.uri.toString(), range: { start: { line: loc.range.start.line, character: loc.range.start.character }, end: { line: loc.range.end.line, character: loc.range.end.character } } }; } }); this.logger.debug(`Found ${definitions.length} definitions`); return { definitions }; } catch (error) { this.logger.error('Error getting definition:', error); throw new Error(`Failed to get definition: ${error}`); } } async getHover(uriString: string, line: number, character: number) { try { const uri = vscode.Uri.parse(uriString); const position = new vscode.Position(line, character); this.logger.debug(`Getting hover info at ${uri.fsPath}:${line}:${character}`); const hovers = await vscode.commands.executeCommand<vscode.Hover[]>( 'vscode.executeHoverProvider', uri, position ); if (!hovers || hovers.length === 0) { return { hover: null }; } // Take first hover const hover = hovers[0]; const contents = hover.contents.map(content => { if (typeof content === 'string') { return { kind: 'plaintext', value: content }; } else { return { kind: 'markdown', value: content.value }; } }); this.logger.debug('Found hover information'); return { hover: { contents, range: hover.range ? { start: { line: hover.range.start.line, character: hover.range.start.character }, end: { line: hover.range.end.line, character: hover.range.end.character } } : null } }; } catch (error) { this.logger.error('Error getting hover:', error); throw new Error(`Failed to get hover info: ${error}`); } } private serializeSymbol(symbol: vscode.DocumentSymbol): any { return { name: symbol.name, detail: symbol.detail, kind: vscode.SymbolKind[symbol.kind], range: { start: { line: symbol.range.start.line, character: symbol.range.start.character }, end: { line: symbol.range.end.line, character: symbol.range.end.character } }, selectionRange: { start: { line: symbol.selectionRange.start.line, character: symbol.selectionRange.start.character }, end: { line: symbol.selectionRange.end.line, character: symbol.selectionRange.end.character } }, children: symbol.children?.map(child => this.serializeSymbol(child)) || [] }; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aaddrick/vscode-mcp-language-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server