Skip to main content
Glama
nrwl

Nx MCP Server

Official
by nrwl
ide-communication.controller.ts6.4 kB
import { ContextProvider } from '@lit-labs/context'; import { FormValues, GenerateUiConfiguration, GenerateUiInputMessage, GenerateUiOutputMessage, GenerateUiRequestValidationOutputMessage, GenerateUiStyles, GeneratorContext, GeneratorSchema, ValidationResults, } from '@nx-console/shared-generate-ui-types'; import { ReactiveController, ReactiveElement } from 'lit'; import type { WebviewApi } from 'vscode-webview'; import { editorContext } from '@nx-console/shared-ui-components'; import { generatorContextContext } from './contexts/generator-context-context'; export class IdeCommunicationController implements ReactiveController { editor: 'vscode' | 'intellij'; generatorSchema: GeneratorSchema | undefined; configuration: GenerateUiConfiguration | undefined; banner: | { message: string; type: 'info' | 'warning' | 'error'; } | undefined; private postToIde: (message: unknown) => void; private generatorContextContextProvider: ContextProvider<{ __context__: GeneratorContext | undefined; }>; constructor(private host: ReactiveElement) { let vscode: WebviewApi<undefined> | undefined; try { vscode = acquireVsCodeApi(); } catch (e) { // noop } this.editor = vscode ? 'vscode' : 'intellij'; console.log('initializing ide communication for', this.editor); new ContextProvider(host, { context: editorContext, initialValue: this.editor, }); this.generatorContextContextProvider = new ContextProvider(host, { context: generatorContextContext, initialValue: undefined, }); if (vscode) { this.setupVscodeCommunication(vscode); } else { this.setupIntellijCommunication(); } this.postMessageToIde({ payloadType: 'output-init', }); } hostConnected(): void { // noop } postMessageToIde(message: GenerateUiOutputMessage) { // we filter these messages out because they're noisy if (message.payloadType !== 'request-validation') { console.log('sending message to ide', message); } this.postToIde(message); } private pendingPluginValidationQueue: (( results: ValidationResults, ) => void)[] = []; async getValidationResults( formValues: FormValues, schema: GeneratorSchema, ): Promise<ValidationResults> { // send request and wait until handleInputMessage resolves the promise const promise = new Promise<ValidationResults>((resolve) => { this.pendingPluginValidationQueue.push(resolve); }); this.postMessageToIde( new GenerateUiRequestValidationOutputMessage({ formValues, schema }), ); return await promise; } private setupVscodeCommunication(vscode: WebviewApi<undefined>) { window.addEventListener( 'message', (event: MessageEvent<GenerateUiInputMessage>) => { const data = event.data; if (!data) { return; } // we filter these messages out because they're noisy if (data.payloadType !== 'validation-results') { console.log('received message from vscode', data); } this.handleInputMessage(data); }, ); this.postToIde = (message) => vscode.postMessage(message); } private setupIntellijCommunication() { window.intellijApi?.registerPostToWebviewCallback( (message: GenerateUiInputMessage) => { if (message.payloadType === 'styles') { this.setIntellijStyles(message.payload); return; } this.handleInputMessage(message); }, ); this.postToIde = (message) => { const stringified = JSON.stringify(message); window.intellijApi?.postToIde(stringified); }; } private handleInputMessage(message: GenerateUiInputMessage) { switch (message.payloadType) { case 'generator': { this.generatorSchema = message.payload; this.generatorContextContextProvider.setValue( this.generatorSchema.context, ); this.host.requestUpdate(); break; } case 'config': { this.configuration = message.payload; this.host.requestUpdate(); break; } case 'banner': { this.banner = message.payload; this.host.requestUpdate(); break; } case 'validation-results': { // get most recent listener from queue and resolve it if (this.pendingPluginValidationQueue.length > 0) { const resolve = this.pendingPluginValidationQueue.shift(); if (!resolve) { break; } resolve(message.payload); } break; } case 'update-form-values': { // Get the Root element cast to access its formValuesService const root = this.host as unknown as { formValuesService: { updateFormValuesFromIde: (values: FormValues) => void; }; }; if (root.formValuesService) { root.formValuesService.updateFormValuesFromIde(message.payload); } this.host.requestUpdate(); break; } } } private setIntellijStyles(styles: GenerateUiStyles) { const styleSheet = new CSSStyleSheet(); styleSheet.replaceSync(` :root { --foreground-color: ${styles.foregroundColor}; --muted-foreground-color: ${styles.mutedForegroundColor}; --background-color: ${styles.backgroundColor}; --primary-color: ${styles.primaryColor}; --error-color: ${styles.errorColor}; --field-background-color: ${styles.fieldBackgroundColor}; --field-border-color: ${styles.fieldBorderColor}; --select-field-background-color: ${styles.selectFieldBackgroundColor}; --active-selection-background-color: ${styles.activeSelectionBackgroundColor}; --focus-border-color: ${styles.focusBorderColor}; --banner-warning-color: ${styles.bannerWarningBackgroundColor}; --banner-text-color: ${styles.bannerTextColor}; --badge-background-color: ${styles.badgeBackgroundColor}; --separator-color: ${styles.separatorColor}; --field-nav-hover-color: ${styles.fieldNavHoverColor}; --scrollbar-thumb-color: ${styles.scrollbarThumbColor}; font-family: ${styles.fontFamily}; font-size: ${styles.fontSize}; } `); document.adoptedStyleSheets = [styleSheet]; } }

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