Skip to main content
Glama
extension.ts12.1 kB
import * as vscode from 'vscode'; import { MCPServer, ToolConfiguration } from './server'; import { listWorkspaceFiles } from './tools/file-tools'; import { logger } from './utils/logger'; // Re-export for testing purposes export { MCPServer }; let mcpServer: MCPServer | undefined; let statusBarItem: vscode.StatusBarItem | undefined; let sharedTerminal: vscode.Terminal | undefined; // Server state - disabled by default let serverEnabled: boolean = false; // Terminal name constant const TERMINAL_NAME = 'MCP Shell Commands'; /** * Gets the tool configuration from VS Code settings * @returns ToolConfiguration object with all tool enablement settings */ function getToolConfiguration(): ToolConfiguration { const config = vscode.workspace.getConfiguration('vscode-mcp-server'); const enabledTools = config.get<any>('enabledTools') || {}; return { file: enabledTools.file ?? true, edit: enabledTools.edit ?? true, shell: enabledTools.shell ?? true, diagnostics: enabledTools.diagnostics ?? true, symbol: enabledTools.symbol ?? true }; } /** * Gets or creates the shared terminal for the extension * @param context The extension context * @returns The shared terminal instance */ export function getExtensionTerminal(context: vscode.ExtensionContext): vscode.Terminal { // Check if a terminal with our name already exists const existingTerminal = vscode.window.terminals.find(t => t.name === TERMINAL_NAME); if (existingTerminal && existingTerminal.exitStatus === undefined) { // Reuse the existing terminal if it's still open logger.info('[getExtensionTerminal] Reusing existing terminal for shell commands'); return existingTerminal; } // Create a new terminal if it doesn't exist or if it has exited sharedTerminal = vscode.window.createTerminal(TERMINAL_NAME); logger.info('[getExtensionTerminal] Created new terminal for shell commands'); context.subscriptions.push(sharedTerminal); return sharedTerminal; } // Function to update status bar function updateStatusBar(port: number) { if (!statusBarItem) { return; } if (serverEnabled) { statusBarItem.text = `$(server) MCP Server: ${port}`; statusBarItem.tooltip = `MCP Server running at localhost:${port} (Click to toggle)`; statusBarItem.backgroundColor = undefined; } else { statusBarItem.text = `$(server) MCP Server: Off`; statusBarItem.tooltip = `MCP Server is disabled (Click to toggle)`; // Use a subtle color to indicate disabled state statusBarItem.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground'); } statusBarItem.show(); } // Function to toggle server state async function toggleServerState(context: vscode.ExtensionContext): Promise<void> { logger.info(`[toggleServerState] Starting toggle operation - changing from ${serverEnabled} to ${!serverEnabled}`); serverEnabled = !serverEnabled; // Store state for persistence context.globalState.update('mcpServerEnabled', serverEnabled); const config = vscode.workspace.getConfiguration('vscode-mcp-server'); const port = config.get<number>('port') || 3000; const host = config.get<string>('host') || '127.0.0.1'; // Update status bar immediately to provide feedback updateStatusBar(port); if (serverEnabled) { // Start the server if it was disabled if (!mcpServer) { logger.info(`[toggleServerState] Creating MCP server instance`); const terminal = getExtensionTerminal(context); const toolConfig = getToolConfiguration(); mcpServer = new MCPServer(port, host, terminal, toolConfig); mcpServer.setFileListingCallback(async (path: string, recursive: boolean) => { try { return await listWorkspaceFiles(path, recursive); } catch (error) { logger.error(`[toggleServerState] Error listing files: ${error instanceof Error ? error.message : String(error)}`); throw error; } }); mcpServer.setupTools(); logger.info(`[toggleServerState] Starting server at ${new Date().toISOString()}`); const startTime = Date.now(); await mcpServer.start(); const duration = Date.now() - startTime; logger.info(`[toggleServerState] Server started successfully at ${new Date().toISOString()} (took ${duration}ms)`); vscode.window.showInformationMessage(`MCP Server enabled and running at http://localhost:${port}/mcp`); } } else { // Stop the server if it was enabled if (mcpServer) { // Show progress indicator await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: 'Stopping MCP Server', cancellable: false }, async (progress) => { logger.info(`[toggleServerState] Stopping server at ${new Date().toISOString()}`); progress.report({ message: 'Closing connections...' }); const stopTime = Date.now(); if (mcpServer) { await mcpServer.stop(); } const duration = Date.now() - stopTime; logger.info(`[toggleServerState] Server stopped successfully at ${new Date().toISOString()} (took ${duration}ms)`); mcpServer = undefined; }); vscode.window.showInformationMessage('MCP Server has been disabled'); } } logger.info(`[toggleServerState] Toggle operation completed`); } export async function activate(context: vscode.ExtensionContext) { logger.info('Activating vscode-mcp-server extension'); try { // Get configuration const config = vscode.workspace.getConfiguration('vscode-mcp-server'); const defaultEnabled = config.get<boolean>('defaultEnabled') ?? false; const port = config.get<number>('port') || 3000; const host = config.get<string>('host') || '127.0.0.1'; // Load saved state or use configured default serverEnabled = context.globalState.get('mcpServerEnabled', defaultEnabled); logger.info(`[activate] Using port ${port} from configuration`); logger.info(`[activate] Server enabled: ${serverEnabled}`); // Create status bar item statusBarItem = vscode.window.createStatusBarItem( vscode.StatusBarAlignment.Right, 100 ); statusBarItem.command = 'vscode-mcp-server.toggleServer'; // Only start the server if enabled if (serverEnabled) { // Create the shared terminal const terminal = getExtensionTerminal(context); // Initialize MCP server with the configured port, terminal, and tool configuration const toolConfig = getToolConfiguration(); mcpServer = new MCPServer(port, host, terminal, toolConfig); // Set up file listing callback mcpServer.setFileListingCallback(async (path: string, recursive: boolean) => { try { return await listWorkspaceFiles(path, recursive); } catch (error) { logger.error(`Error listing files: ${error instanceof Error ? error.message : String(error)}`); throw error; } }); // Call setupTools after setting the callback mcpServer.setupTools(); await mcpServer.start(); logger.info('MCP Server started successfully'); } else { logger.info('MCP Server is disabled by default'); } // Update status bar after server state is determined updateStatusBar(port); // Register commands const toggleServerCommand = vscode.commands.registerCommand( 'vscode-mcp-server.toggleServer', () => toggleServerState(context) ); const showServerInfoCommand = vscode.commands.registerCommand( 'vscode-mcp-server.showServerInfo', () => { if (serverEnabled) { vscode.window.showInformationMessage(`MCP Server is running at http://localhost:${port}/mcp`); } else { vscode.window.showInformationMessage('MCP Server is currently disabled. Click on the status bar item to enable it.'); } } ); // Listen for configuration changes to restart server if needed const configChangeListener = vscode.workspace.onDidChangeConfiguration(async (event) => { if (event.affectsConfiguration('vscode-mcp-server.enabledTools')) { logger.info('[configChangeListener] Tool configuration changed - restarting server if enabled'); if (serverEnabled && mcpServer) { // Stop current server await mcpServer.stop(); mcpServer = undefined; // Start new server with updated configuration const config = vscode.workspace.getConfiguration('vscode-mcp-server'); const port = config.get<number>('port') || 3000; const host = config.get<string>('host') || '127.0.0.1'; const terminal = getExtensionTerminal(context); const toolConfig = getToolConfiguration(); mcpServer = new MCPServer(port, host, terminal, toolConfig); mcpServer.setFileListingCallback(async (path: string, recursive: boolean) => { try { return await listWorkspaceFiles(path, recursive); } catch (error) { logger.error(`[configChangeListener] Error listing files: ${error instanceof Error ? error.message : String(error)}`); throw error; } }); mcpServer.setupTools(); await mcpServer.start(); vscode.window.showInformationMessage('MCP Server restarted with updated tool configuration'); } } }); // Add all disposables to the context subscriptions context.subscriptions.push( statusBarItem, toggleServerCommand, showServerInfoCommand, configChangeListener, { dispose: async () => mcpServer && await mcpServer.stop() } ); } catch (error) { logger.error(`Failed to start MCP Server: ${error instanceof Error ? error.message : 'Unknown error'}`); vscode.window.showErrorMessage(`Failed to start MCP Server: ${error instanceof Error ? error.message : 'Unknown error'}`); } } export async function deactivate() { if (statusBarItem) { statusBarItem.dispose(); statusBarItem = undefined; } // Dispose the shared terminal if (sharedTerminal) { sharedTerminal.dispose(); sharedTerminal = undefined; } if (!mcpServer) { return; } try { logger.info('Stopping MCP Server during extension deactivation'); await mcpServer.stop(); logger.info('MCP Server stopped successfully'); } catch (error) { logger.error(`Error stopping MCP Server: ${error instanceof Error ? error.message : String(error)}`); throw error; // Re-throw to ensure VS Code knows about the failure } finally { mcpServer = undefined; // Dispose the logger logger.dispose(); } }

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/juehang/vscode-mcp-server'

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