Skip to content

Commit 222656d

Browse files
feat(vscode-integration): add Model Context Protocol integration
Bridges the GLSP-announced MCP server into the IDE's built-in MCP definition provider API, surfacing the URL in the IDE's MCP server list. MCP integration - Node-side provider registers the announced endpoint on startup and deregisters on extension dispose. - Plumbs an opt-in MCP server configuration through the connector base classes. - Raises the minimum supported IDE version to the release shipping the MCP definition provider API. Adopters - Enable MCP by setting the configuration on the connector. - The workflow example demonstrates the end-to-end wiring with a transient connect notification and a copy-URL action. Misc - Webview content-security policy allows blob URLs as image sources, enabling client-side PNG export inside the webview. - Adapts the webview shutdown bridge to the widened asynchronous shutdown contract. Part of eclipse-glsp/glsp#1546
1 parent 57bf2bb commit 222656d

13 files changed

Lines changed: 736 additions & 123 deletions

File tree

example/workflow/extension/package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,12 @@
191191
"id": "workflow.editor.title",
192192
"label": "Diagram"
193193
}
194+
],
195+
"mcpServerDefinitionProviders": [
196+
{
197+
"id": "glsp-workflow",
198+
"label": "GLSP Workflow"
199+
}
194200
]
195201
},
196202
"activationEvents": [
@@ -209,6 +215,6 @@
209215
"workflow-glsp-webview": "2.7.0-next"
210216
},
211217
"engines": {
212-
"vscode": "^1.54.0"
218+
"vscode": "^1.99.0"
213219
}
214220
}

example/workflow/extension/src/workflow-editor-provider.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/********************************************************************************
2-
* Copyright (c) 2021-2023 EclipseSource and others.
2+
* Copyright (c) 2021-2026 EclipseSource and others.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -48,6 +48,7 @@ export default class WorkflowEditorProvider extends GlspEditorProvider {
4848
<meta name="viewport" content="width=device-width, height=device-height">
4949
<meta http-equiv="Content-Security-Policy" content="
5050
default-src http://*.fontawesome.com ${webview.cspSource} 'unsafe-inline' 'unsafe-eval';
51+
img-src ${webview.cspSource} blob:;
5152
">
5253
5354
</head>

example/workflow/extension/src/workflow-extension.ts

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/********************************************************************************
2-
* Copyright (c) 2021-2024 EclipseSource and others.
2+
* Copyright (c) 2021-2026 EclipseSource and others.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -15,10 +15,17 @@
1515
********************************************************************************/
1616
import 'reflect-metadata';
1717

18-
import { WorkflowDiagramModule, WorkflowLayoutConfigurator, WorkflowServerModule } from '@eclipse-glsp-examples/workflow-server/node';
19-
import { configureELKLayoutModule } from '@eclipse-glsp/layout-elk';
18+
import {
19+
WorkflowDiagramModule,
20+
WorkflowLayoutConfigurator,
21+
WorkflowMcpDiagramModule,
22+
WorkflowMcpServerModule,
23+
WorkflowServerModule
24+
} from '@eclipse-glsp-examples/workflow-server/node';
25+
import { ElkLayoutModule } from '@eclipse-glsp/layout-elk';
2026
import { GModelStorage, LogLevel, createAppModule } from '@eclipse-glsp/server/node';
2127
import {
28+
GlspMcpServerProvider,
2229
GlspSocketServerLauncher,
2330
GlspVscodeConnector,
2431
NavigateAction,
@@ -58,21 +65,44 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
5865
context.subscriptions.push(serverProcess);
5966
await serverProcess.start();
6067
}
68+
// Presence of `mcpServer` (even an empty `{}`) opts the GLSP server into starting an
69+
// embedded MCP HTTP server. The custom `name` groups multiple GLSP-based MCP servers
70+
// in the IDE's MCP server list and matches the `mcpServerDefinitionProviders` id below.
71+
const mcpServer = { name: 'glsp-workflow' };
6172
// Wrap server with quickstart component
6273
const workflowServer = useIntegratedServer
6374
? new NodeGlspVscodeServer({
6475
clientId: 'glsp.workflow',
6576
clientName: 'workflow',
66-
serverModules: createServerModules()
77+
serverModules: createServerModules(),
78+
mcpServer
6779
})
6880
: new SocketGlspVscodeServer({
6981
clientId: 'glsp.workflow',
7082
clientName: 'workflow',
7183
connectionOptions: {
7284
port: serverProcess?.getPort() || JSON.parse(process.env.GLSP_SERVER_PORT || DEFAULT_SERVER_PORT),
7385
path: process.env.GLSP_WEBSOCKET_PATH
74-
}
86+
},
87+
mcpServer
7588
});
89+
90+
// Bridge the embedded MCP server's announced URL into VS Code's built-in MCP host. The
91+
// `glsp-workflow` provider id matches the `mcpServerDefinitionProviders` contribution in
92+
// this extension's `package.json` and the `mcpServer.name` above.
93+
const mcpProvider = new GlspMcpServerProvider();
94+
context.subscriptions.push(mcpProvider, vscode.lm.registerMcpServerDefinitionProvider(mcpServer.name, mcpProvider));
95+
workflowServer.initializeResult.then(
96+
result => {
97+
const server = mcpProvider.addServer(result);
98+
if (server) {
99+
notifyMcpConnected(server.name, server.url);
100+
}
101+
},
102+
() => {
103+
/* GLSP startup error already surfaced by the connector; no MCP server to register. */
104+
}
105+
);
76106
// Initialize GLSP-VSCode connector with server wrapper
77107
const glspVscodeConnector = new GlspVscodeConnector({
78108
server: workflowServer,
@@ -106,9 +136,23 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
106136
);
107137
}
108138

139+
/** Info notification with a `Copy URL` action; stays until the user dismisses it. */
140+
function notifyMcpConnected(name: string, url: string): void {
141+
const COPY_URL_ACTION = 'Copy URL';
142+
vscode.window.showInformationMessage(`MCP server '${name}' auto-registered at ${url}`, COPY_URL_ACTION).then(action => {
143+
if (action === COPY_URL_ACTION) {
144+
vscode.env.clipboard.writeText(url);
145+
}
146+
});
147+
}
148+
109149
function createServerModules(): ContainerModule[] {
110150
const appModule = createAppModule({ logLevel: LogLevel.info, logDir: LOG_DIR, fileLog: true, consoleLog: false });
111-
const elkLayoutModule = configureELKLayoutModule({ algorithms: ['layered'], layoutConfigurator: WorkflowLayoutConfigurator });
112-
const mainModule = new WorkflowServerModule().configureDiagramModule(new WorkflowDiagramModule(() => GModelStorage), elkLayoutModule);
113-
return [appModule, mainModule];
151+
const elkLayoutModule = new ElkLayoutModule({ algorithms: ['layered'], layoutConfigurator: WorkflowLayoutConfigurator });
152+
const mainModule = new WorkflowServerModule().configureDiagramModule(
153+
new WorkflowDiagramModule(() => GModelStorage),
154+
elkLayoutModule,
155+
new WorkflowMcpDiagramModule()
156+
);
157+
return [appModule, mainModule, new WorkflowMcpServerModule()];
114158
}

example/workflow/web-extension/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"bundle": "webpack",
3535
"bundle:prod": "webpack --config ./webpack.prod.js",
3636
"clean": "rimraf lib tsconfig.tsbuildinfo ",
37-
"compile": "tsc-b",
37+
"compile": "tsc -b",
3838
"lint": "eslint ./src",
3939
"package": "vsce package --yarn",
4040
"watch": "tsc -w",

example/workflow/web-extension/src/workflow-editor-provider.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/********************************************************************************
2-
* Copyright (c) 2023 EclipseSource and others.
2+
* Copyright (c) 2023-2026 EclipseSource and others.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -48,6 +48,7 @@ export default class WorkflowEditorProvider extends GlspEditorProvider {
4848
<meta name="viewport" content="width=device-width, height=device-height">
4949
<meta http-equiv="Content-Security-Policy" content="
5050
default-src http://*.fontawesome.com ${webview.cspSource} 'unsafe-inline' 'unsafe-eval';
51+
img-src ${webview.cspSource} blob:;
5152
">
5253
5354
</head>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"devDependencies": {
3333
"@eclipse-glsp/dev": "next",
3434
"@types/node": "22.x",
35-
"@types/vscode": "^1.54.0",
35+
"@types/vscode": "^1.99.0",
3636
"concurrently": "^8.2.2",
3737
"lerna": "^9.0.0",
3838
"mocha-ctrf-json-reporter": "0.0.9",

packages/vscode-integration-webview/src/webview-glsp-client.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,9 @@ export class WebviewGlspClient implements GLSPClient, Disposable {
118118
return this.messenger.sendRequest(DisposeClientSessionRequest, HOST_EXTENSION, params);
119119
}
120120

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

125126
async stop(): Promise<void> {

packages/vscode-integration/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@
5252
"ws": "^8.13.0"
5353
},
5454
"devDependencies": {
55-
"@types/vscode": "^1.54.0",
55+
"@types/vscode": "^1.99.0",
5656
"@types/ws": "^8.5.4"
5757
},
5858
"engines": {
59-
"vscode": "^1.54.0"
59+
"vscode": "^1.99.0"
6060
},
6161
"publishConfig": {
6262
"access": "public"

packages/vscode-integration/src/common/quickstart-components/base-glsp-vscode-server.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/********************************************************************************
2-
* Copyright (c) 2023 EclipseSource and others.
2+
* Copyright (c) 2023-2026 EclipseSource and others.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0 which is available at
@@ -21,7 +21,8 @@ import {
2121
GLSPClient,
2222
InitializeParameters,
2323
InitializeResult,
24-
MaybePromise
24+
MaybePromise,
25+
McpServerConfiguration
2526
} from '@eclipse-glsp/protocol';
2627
import * as vscode from 'vscode';
2728
import { GlspVscodeServer } from '../types';
@@ -31,6 +32,8 @@ export interface GlspVscodeServerOptions {
3132
readonly clientId: string;
3233
/** Name to register the client with on the server. */
3334
readonly clientName: string;
35+
/** MCP-server configuration sent on the GLSP `initialize` request; presence opts in. */
36+
readonly mcpServer?: McpServerConfiguration;
3437
}
3538

3639
/**
@@ -85,7 +88,8 @@ export abstract class BaseGlspVscodeServer<C extends GLSPClient = GLSPClient> im
8588
protected async createInitializeParameters(): Promise<InitializeParameters> {
8689
return {
8790
applicationId: ApplicationIdProvider.get(),
88-
protocolVersion: GLSPClient.protocolVersion
91+
protocolVersion: GLSPClient.protocolVersion,
92+
...(this.options.mcpServer ? { mcpServer: this.options.mcpServer } : {})
8993
};
9094
}
9195

packages/vscode-integration/src/common/quickstart-components/webview-endpoint.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ export class WebviewEndpoint implements Disposable {
157157
this.messenger.onRequest(DisposeClientSessionRequest, params => glspClient.disposeClientSession(params), {
158158
sender: this.messageParticipant
159159
}),
160-
this.messenger.onRequest(ShutdownServerNotification, () => glspClient.shutdownServer(), {
160+
this.messenger.onRequest(ShutdownServerNotification, () => Promise.resolve(glspClient.shutdownServer()), {
161161
sender: this.messageParticipant
162162
}),
163163
this.messenger.onRequest(StopRequest, () => glspClient.stop(), {

0 commit comments

Comments
 (0)