Skip to main content
Glama
nrwl

Nx MCP Server

Official
by nrwl
json-rpc-client.ts7.45 kB
import { RunningTasksMap } from '@nx-console/shared-running-tasks'; import { ConnectionStatus, IDE_RPC_METHODS, OpenGenerateUiResponse, IIdeJsonRpcClient, FocusProjectRequest, FocusTaskRequest, ShowFullProjectGraphRequest, OpenGenerateUiRequest, GetRunningTasksResponse, } from '@nx-console/shared-types'; import { Logger } from '@nx-console/shared-utils'; import { getNxConsoleSocketPath } from '@nx-console/shared-socket-utils'; import { Socket } from 'net'; import { platform } from 'os'; import * as rpc from 'vscode-jsonrpc/node'; // Define typed request and notification types const focusProjectRequest = new rpc.NotificationType<FocusProjectRequest>( IDE_RPC_METHODS.FOCUS_PROJECT, ); const focusTaskRequest = new rpc.NotificationType<FocusTaskRequest>( IDE_RPC_METHODS.FOCUS_TASK, ); const showFullProjectGraphRequest = new rpc.NotificationType<ShowFullProjectGraphRequest>( IDE_RPC_METHODS.SHOW_FULL_PROJECT_GRAPH, ); const openGenerateUiRequest = new rpc.RequestType< OpenGenerateUiRequest, OpenGenerateUiResponse, void >(IDE_RPC_METHODS.OPEN_GENERATE_UI); const getRunningTasksRequest = new rpc.RequestType< undefined, GetRunningTasksResponse, void >(IDE_RPC_METHODS.GET_RUNNING_TASKS); /** * JSON-RPC client for communicating with the IDE using vscode-jsonrpc */ export class IdeJsonRpcClient implements IIdeJsonRpcClient { private socket: Socket | null = null; private connection: rpc.MessageConnection | null = null; private status: ConnectionStatus = 'disconnected'; private reconnectAttempts = 0; private reconnectTimer: NodeJS.Timeout | null = null; private disconnectionHandler?: (client: IdeJsonRpcClient) => void; constructor( private workspacePath: string, private logger?: Logger, private reconnectInterval = 2000, private maxReconnectAttempts = 5, ) {} /** * Set a handler to be called when the client disconnects */ onDisconnection(handler: (client: IdeJsonRpcClient) => void): void { this.disconnectionHandler = handler; } /** * Connect to the IDE socket */ async connect(): Promise<void> { if (this.status === 'connected' || this.status === 'connecting') { return; } this.status = 'connecting'; try { const socketPath = await getNxConsoleSocketPath(this.workspacePath); this.socket = new Socket(); await new Promise<void>((resolve, reject) => { this.socket!.on('connect', () => { this.status = 'connected'; this.reconnectAttempts = 0; this.logger?.log(`Connected to IDE at ${socketPath}`); resolve(); }); this.socket!.on('error', (error) => { this.logger?.log('Socket connection error:', error); reject(error); }); if (platform() === 'win32') { // On Windows, connect to named pipe this.socket!.connect(socketPath); } else { // On Unix, connect to socket file this.socket!.connect(socketPath); } }); const reader = new rpc.SocketMessageReader(this.socket); const writer = new rpc.SocketMessageWriter(this.socket); this.connection = rpc.createMessageConnection(reader, writer); this.connection.onClose(() => { this.logger?.log('JSON-RPC connection closed'); this.handleDisconnection(); }); this.connection.onError((error) => { this.logger?.log('JSON-RPC connection error:', error); this.handleDisconnection(); }); this.connection.listen(); } catch (error) { this.logger?.log('Failed to connect to IDE:', error); this.handleDisconnection(); throw error; } } /** * Disconnect from the IDE */ disconnect(): void { if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer = null; } if (this.connection) { this.connection.dispose(); this.connection = null; } if (this.socket) { this.socket.destroy(); this.socket = null; } this.status = 'disconnected'; } getStatus(): ConnectionStatus { return this.status; } /** * Handle disconnection and attempt reconnection */ private handleDisconnection(): void { if (this.status === 'disconnected') { return; // Already handling disconnection } this.status = 'error'; if (this.connection) { this.connection.dispose(); this.connection = null; } if (this.socket) { this.socket.destroy(); this.socket = null; } // Attempt reconnection if configured const maxAttempts = this.maxReconnectAttempts || 5; const interval = this.reconnectInterval || 2000; if (this.reconnectAttempts < maxAttempts) { this.reconnectAttempts++; this.logger?.log( `Attempting to reconnect (${this.reconnectAttempts}/${maxAttempts})...`, ); this.reconnectTimer = setTimeout(() => { this.connect().catch((error) => { this.logger?.log('Reconnection failed:', error); if (this.reconnectAttempts >= maxAttempts) { this.status = 'disconnected'; this.logger?.log('Max reconnection attempts reached. Giving up.'); } }); }, interval); } else { this.status = 'disconnected'; this.logger?.log('Max reconnection attempts reached. Connection lost.'); // Notify handler of permanent disconnection if (this.disconnectionHandler) { this.disconnectionHandler(this); } } } async focusProject(projectName: string): Promise<void> { if (!this.connection || this.status !== 'connected') { throw new Error('Not connected to IDE'); } await this.connection.sendNotification(focusProjectRequest, { projectName, }); } async focusTask(projectName: string, taskName: string): Promise<void> { if (!this.connection || this.status !== 'connected') { throw new Error('Not connected to IDE'); } await this.connection.sendNotification(focusTaskRequest, { projectName, taskName, }); } async showFullProjectGraph(): Promise<void> { if (!this.connection || this.status !== 'connected') { throw new Error('Not connected to IDE'); } await this.connection.sendNotification(showFullProjectGraphRequest); } async openGenerateUi( generatorName: string, options: Record<string, unknown>, cwd?: string, ): Promise<string> { if (!this.connection || this.status !== 'connected') { throw new Error('Not connected to IDE'); } const response = await this.connection.sendRequest(openGenerateUiRequest, { generatorName, options, cwd, }); return response.logFileName; } async getRunningTasks(): Promise<RunningTasksMap> { if (!this.connection || this.status !== 'connected') { throw new Error('Not connected to IDE'); } const response = await this.connection.sendRequest( getRunningTasksRequest, undefined, ); return response.runningTasks; } async sendNotification(method: string, params?: unknown): Promise<void> { if (!this.connection || this.status !== 'connected') { throw new Error('Not connected to IDE'); } const notificationType = new rpc.NotificationType<unknown>(method); this.connection.sendNotification(notificationType, params); } }

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/nrwl/nx-console'

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