Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion example/workflow/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@
"id": "workflow.editor.title",
"label": "Diagram"
}
],
"mcpServerDefinitionProviders": [
{
"id": "glsp-workflow",
"label": "GLSP Workflow"
}
]
},
"activationEvents": [
Expand All @@ -209,6 +215,6 @@
"workflow-glsp-webview": "2.7.0-next"
},
"engines": {
"vscode": "^1.54.0"
"vscode": "^1.99.0"
}
}
3 changes: 2 additions & 1 deletion example/workflow/extension/src/workflow-editor-provider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2021-2023 EclipseSource and others.
* Copyright (c) 2021-2026 EclipseSource and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand Down Expand Up @@ -48,6 +48,7 @@ export default class WorkflowEditorProvider extends GlspEditorProvider {
<meta name="viewport" content="width=device-width, height=device-height">
<meta http-equiv="Content-Security-Policy" content="
default-src http://*.fontawesome.com ${webview.cspSource} 'unsafe-inline' 'unsafe-eval';
img-src ${webview.cspSource} blob:;
">

</head>
Expand Down
60 changes: 52 additions & 8 deletions example/workflow/extension/src/workflow-extension.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2021-2024 EclipseSource and others.
* Copyright (c) 2021-2026 EclipseSource and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand All @@ -15,10 +15,17 @@
********************************************************************************/
import 'reflect-metadata';

import { WorkflowDiagramModule, WorkflowLayoutConfigurator, WorkflowServerModule } from '@eclipse-glsp-examples/workflow-server/node';
import { configureELKLayoutModule } from '@eclipse-glsp/layout-elk';
import {
WorkflowDiagramModule,
WorkflowLayoutConfigurator,
WorkflowMcpDiagramModule,
WorkflowMcpServerModule,
WorkflowServerModule
} from '@eclipse-glsp-examples/workflow-server/node';
import { ElkLayoutModule } from '@eclipse-glsp/layout-elk';
import { GModelStorage, LogLevel, createAppModule } from '@eclipse-glsp/server/node';
import {
GlspMcpServerProvider,
GlspSocketServerLauncher,
GlspVscodeConnector,
NavigateAction,
Expand Down Expand Up @@ -58,21 +65,44 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
context.subscriptions.push(serverProcess);
await serverProcess.start();
}
// Presence of `mcpServer` (even an empty `{}`) opts the GLSP server into starting an
// embedded MCP HTTP server. The custom `name` groups multiple GLSP-based MCP servers
// in the IDE's MCP server list and matches the `mcpServerDefinitionProviders` id below.
const mcpServer = { name: 'glsp-workflow' };
// Wrap server with quickstart component
const workflowServer = useIntegratedServer
? new NodeGlspVscodeServer({
clientId: 'glsp.workflow',
clientName: 'workflow',
serverModules: createServerModules()
serverModules: createServerModules(),
mcpServer
})
: new SocketGlspVscodeServer({
clientId: 'glsp.workflow',
clientName: 'workflow',
connectionOptions: {
port: serverProcess?.getPort() || JSON.parse(process.env.GLSP_SERVER_PORT || DEFAULT_SERVER_PORT),
path: process.env.GLSP_WEBSOCKET_PATH
}
},
mcpServer
});

// Bridge the embedded MCP server's announced URL into VS Code's built-in MCP host. The
// `glsp-workflow` provider id matches the `mcpServerDefinitionProviders` contribution in
// this extension's `package.json` and the `mcpServer.name` above.
const mcpProvider = new GlspMcpServerProvider();
context.subscriptions.push(mcpProvider, vscode.lm.registerMcpServerDefinitionProvider(mcpServer.name, mcpProvider));
workflowServer.initializeResult.then(
result => {
const server = mcpProvider.addServer(result);
if (server) {
notifyMcpConnected(server.name, server.url);
}
},
() => {
/* GLSP startup error already surfaced by the connector; no MCP server to register. */
}
);
// Initialize GLSP-VSCode connector with server wrapper
const glspVscodeConnector = new GlspVscodeConnector({
server: workflowServer,
Expand Down Expand Up @@ -106,9 +136,23 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
);
}

/** Info notification with a `Copy URL` action; stays until the user dismisses it. */
function notifyMcpConnected(name: string, url: string): void {
const COPY_URL_ACTION = 'Copy URL';
vscode.window.showInformationMessage(`MCP server '${name}' auto-registered at ${url}`, COPY_URL_ACTION).then(action => {
if (action === COPY_URL_ACTION) {
vscode.env.clipboard.writeText(url);
}
});
}

function createServerModules(): ContainerModule[] {
const appModule = createAppModule({ logLevel: LogLevel.info, logDir: LOG_DIR, fileLog: true, consoleLog: false });
const elkLayoutModule = configureELKLayoutModule({ algorithms: ['layered'], layoutConfigurator: WorkflowLayoutConfigurator });
const mainModule = new WorkflowServerModule().configureDiagramModule(new WorkflowDiagramModule(() => GModelStorage), elkLayoutModule);
return [appModule, mainModule];
const elkLayoutModule = new ElkLayoutModule({ algorithms: ['layered'], layoutConfigurator: WorkflowLayoutConfigurator });
const mainModule = new WorkflowServerModule().configureDiagramModule(
new WorkflowDiagramModule(() => GModelStorage),
elkLayoutModule,
new WorkflowMcpDiagramModule()
);
return [appModule, mainModule, new WorkflowMcpServerModule()];
}
2 changes: 1 addition & 1 deletion example/workflow/web-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"bundle": "webpack",
"bundle:prod": "webpack --config ./webpack.prod.js",
"clean": "rimraf lib tsconfig.tsbuildinfo ",
"compile": "tsc-b",
"compile": "tsc -b",
"lint": "eslint ./src",
"package": "vsce package --yarn",
"watch": "tsc -w",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2023 EclipseSource and others.
* Copyright (c) 2023-2026 EclipseSource and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand Down Expand Up @@ -48,6 +48,7 @@ export default class WorkflowEditorProvider extends GlspEditorProvider {
<meta name="viewport" content="width=device-width, height=device-height">
<meta http-equiv="Content-Security-Policy" content="
default-src http://*.fontawesome.com ${webview.cspSource} 'unsafe-inline' 'unsafe-eval';
img-src ${webview.cspSource} blob:;
">

</head>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"devDependencies": {
"@eclipse-glsp/dev": "next",
"@types/node": "22.x",
"@types/vscode": "^1.54.0",
"@types/vscode": "^1.99.0",
"concurrently": "^8.2.2",
"lerna": "^9.0.0",
"mocha-ctrf-json-reporter": "0.0.9",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,9 @@ export class WebviewGlspClient implements GLSPClient, Disposable {
return this.messenger.sendRequest(DisposeClientSessionRequest, HOST_EXTENSION, params);
}

shutdownServer(): void {
this.messenger.sendNotification(ShutdownServerNotification, HOST_EXTENSION);
async shutdownServer(): Promise<void> {
// Await the send so callers can dispose without racing the wire flush.
await this.messenger.sendNotification(ShutdownServerNotification, HOST_EXTENSION);
}

async stop(): Promise<void> {
Expand Down
4 changes: 2 additions & 2 deletions packages/vscode-integration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@
"ws": "^8.13.0"
},
"devDependencies": {
"@types/vscode": "^1.54.0",
"@types/vscode": "^1.99.0",
"@types/ws": "^8.5.4"
},
"engines": {
"vscode": "^1.54.0"
"vscode": "^1.99.0"
},
"publishConfig": {
"access": "public"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2023 EclipseSource and others.
* Copyright (c) 2023-2026 EclipseSource and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand All @@ -21,7 +21,8 @@ import {
GLSPClient,
InitializeParameters,
InitializeResult,
MaybePromise
MaybePromise,
McpServerConfiguration
} from '@eclipse-glsp/protocol';
import * as vscode from 'vscode';
import { GlspVscodeServer } from '../types';
Expand All @@ -31,6 +32,8 @@ export interface GlspVscodeServerOptions {
readonly clientId: string;
/** Name to register the client with on the server. */
readonly clientName: string;
/** MCP-server configuration sent on the GLSP `initialize` request; presence opts in. */
readonly mcpServer?: McpServerConfiguration;
}

/**
Expand Down Expand Up @@ -85,7 +88,8 @@ export abstract class BaseGlspVscodeServer<C extends GLSPClient = GLSPClient> im
protected async createInitializeParameters(): Promise<InitializeParameters> {
return {
applicationId: ApplicationIdProvider.get(),
protocolVersion: GLSPClient.protocolVersion
protocolVersion: GLSPClient.protocolVersion,
...(this.options.mcpServer ? { mcpServer: this.options.mcpServer } : {})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use

mcpServer: this.options.mcpServer ?? {} ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we do not want to set mcpServer if it is not configured. Just setting the property is the opt-in for us to start the MCP server with default configuration.

};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export class WebviewEndpoint implements Disposable {
this.messenger.onRequest(DisposeClientSessionRequest, params => glspClient.disposeClientSession(params), {
sender: this.messageParticipant
}),
this.messenger.onRequest(ShutdownServerNotification, () => glspClient.shutdownServer(), {
this.messenger.onRequest(ShutdownServerNotification, () => Promise.resolve(glspClient.shutdownServer()), {
sender: this.messageParticipant
}),
this.messenger.onRequest(StopRequest, () => glspClient.stop(), {
Expand Down
83 changes: 83 additions & 0 deletions packages/vscode-integration/src/node/glsp-mcp-server-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/********************************************************************************
* Copyright (c) 2026 EclipseSource and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { InitializeResult, McpInitializeResult, McpServerResult } from '@eclipse-glsp/protocol';
import * as vscode from 'vscode';

/**
* Bridges the embedded GLSP MCP server's announced URL into VS Code's built-in MCP host
* via {@link vscode.lm.registerMcpServerDefinitionProvider}. Adopters create one provider
* per extension and feed it the {@link InitializeResult} they get back from the GLSP server's
* `initialize` call (or an explicit {@link McpServerResult}); each entry then surfaces as a
* dynamic MCP server definition in VS Code.
*
* Companion contribution point in the consuming extension's `package.json`:
* ```json
* "contributes": {
* "mcpServerDefinitionProviders": [
* { "id": "glsp", "label": "GLSP" }
* ]
* }
* ```
*
* @example
* ```ts
* const provider = new GlspMcpServerProvider();
* context.subscriptions.push(vscode.lm.registerMcpServerDefinitionProvider('glsp', provider));
* const initializeResult = await glspVscodeServer.initializeResult;
* provider.addServer(initializeResult);
* ```
*/
export class GlspMcpServerProvider implements vscode.McpServerDefinitionProvider, vscode.Disposable {
protected readonly servers = new Map<string, McpServerResult>();
protected readonly didChangeEmitter = new vscode.EventEmitter<void>();

readonly onDidChangeMcpServerDefinitions: vscode.Event<void> = this.didChangeEmitter.event;

/**
* Adds (or replaces by `name`) an MCP server entry. Accepts either the raw
* {@link InitializeResult} from a GLSP `initialize` call (a no-op if the response carries no
* `mcpServer` field) or an explicit {@link McpServerResult}. Returns the entry that was
* registered, or `undefined` if nothing was added.
*/
addServer(serverOrResult: InitializeResult | McpServerResult): McpServerResult | undefined {
const server = McpServerResult.is(serverOrResult) ? serverOrResult : McpInitializeResult.getServer(serverOrResult);
if (!server) {
return undefined;
}
this.servers.set(server.name, server);
this.didChangeEmitter.fire();
return server;
}

/** Removes a previously-added entry by `name`. No-op if the entry is unknown. */
removeServer(name: string): void {
if (this.servers.delete(name)) {
this.didChangeEmitter.fire();
}
}

provideMcpServerDefinitions(): vscode.McpServerDefinition[] {
return [...this.servers.values()].map(
server => new vscode.McpHttpServerDefinition(server.name, vscode.Uri.parse(server.url), server.headers)
);
}

dispose(): void {
this.servers.clear();
this.didChangeEmitter.dispose();
}
}

1 change: 1 addition & 0 deletions packages/vscode-integration/src/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
export * from './glsp-mcp-server-provider';
export * from './quickstart-components/glsp-socket-server-launcher';
export * from './quickstart-components/node-glsp-vscode-server';
export * from './quickstart-components/socket-glsp-vscode-server';
Expand Down
Loading