Skip to content

Add support for Model Context Protocol#120

Merged
martin-fleck-at merged 3 commits into
mainfrom
issues/1546
May 12, 2026
Merged

Add support for Model Context Protocol#120
martin-fleck-at merged 3 commits into
mainfrom
issues/1546

Conversation

@martin-fleck-at
Copy link
Copy Markdown
Contributor

@martin-fleck-at martin-fleck-at commented Dec 18, 2025

What it does

Exposes each GLSP server as an MCP server over Streamable HTTP, so LLM
clients can query and mutate diagrams via MCP resources, tools, and
prompts.

Architecture

  • One MCP endpoint per GLSP server process; under per-window
    deployment, one endpoint per application instance.
  • Auto-discovers diagram-scoped contributions from all GLSP client
    sessions and merges them into a shared surface with cross-session
    ID aliasing.
  • Pluggable model-serializer, label-provider, and element-types
    extension points for diagram-specific overrides.
  • Opt-in: started only when configured on initialize.
  • Random port by default; the announced URL is surfaced for adopter
    discovery.
  • No built-in auth; loopback-only unless explicitly acknowledged.
  • Targets MCP spec 2025-06-18 via the official SDK.

Adopters

  • Diagram module: register language-specific tools, prompts,
    resources, and override the diagram extension points.
  • Server module: configure server-wide options (port, route, auth
    acknowledgement).
  • The workflow server example wires both end-to-end.

Server framework

  • Adds an initialize-contribution extension point on the default
    GLSP server, used by MCP to surface configuration on initialize.
    The previous handleInitializeArgs hook is deprecated — adopters
    subclassing the default server should migrate.

Documentation

  • Adopter README plus a separate architecture document covering the
    feature matrix, lifecycle, extension points, and rationale.

Misc

  • Small fix to a base type-guard on edges in the graph package.
  • Disposes the server instance when the JSON-RPC client connection
    closes uncleanly (browser/IDE force-close).
  • Action dispatcher drops stale response actions arriving without a
    registered handler instead of throwing.

Part of eclipse-glsp/glsp#1546
Requires eclipse-glsp/glsp-client#456

How to test

  • Start the node server
  • Connect with a client that sets the mcpServer configuration
  • Read the actual url of the server out of the log, e.g., [McpServerManager] MCP server 'glspMcpServer' is ready to accept new client requests on: http://127.0.0.1:64577/glsp-mcp
  • Connect to the server using the Theia Integration (caution: no support for MCP Resources) or Claude Code or the MCP Inspector.
  • Test querying resources and tools

Follow-ups

Changelog

  • This PR should be mentioned in the changelog
  • This PR introduces a breaking change (if yes, provide more details below for the changelog and the migration guide)

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds Model Context Protocol (MCP) support to the GLSP server, enabling AI assistants and other MCP clients to interact with GLSP diagrams through a standardized protocol. The implementation provides HTTP-based MCP server functionality with default tools for diagram manipulation and resources for querying diagram state.

Key changes:

  • Introduces new @eclipse-glsp/server-mcp package with MCP server infrastructure, HTTP transport layer, and default tools/resources for diagram interaction
  • Extends GLSP server initialization to support contributions via GLSPServerInitContribution interface
  • Updates workflow example to demonstrate MCP integration

Reviewed changes

Copilot reviewed 19 out of 21 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/server/src/common/session/client-session-manager.ts Adds getSessions() method to retrieve all active client sessions
packages/server/src/common/protocol/glsp-server.ts Introduces GLSPServerInitContribution mechanism and deprecates handleInitializeArgs
packages/server/src/common/di/server-module.ts Adds binding configuration for GLSPServerInitContribution
packages/server-mcp/tsconfig.json TypeScript configuration for new MCP package
packages/server-mcp/src/mcp-util.ts Utility functions for MCP parameter extraction and result formatting
packages/server-mcp/src/mcp-server-manager.ts Core MCP server lifecycle management and HTTP server initialization
packages/server-mcp/src/mcp-server-contribution.ts Interface for extending MCP server with custom tools and resources
packages/server-mcp/src/index.ts Package exports
packages/server-mcp/src/http-server-with-sessions.ts HTTP server implementation with MCP session management
packages/server-mcp/src/di.config.ts Dependency injection configuration for MCP module
packages/server-mcp/src/default-mcp-tool-contribution.ts Default MCP tools for diagram validation, element creation, undo/redo, and save operations
packages/server-mcp/src/default-mcp-resource-contribution.ts Default MCP resources for querying sessions, diagram types, element types, and models
packages/server-mcp/package.json Package metadata and dependencies for MCP package
packages/server-mcp/.eslintrc.js ESLint configuration ignoring MCP SDK imports
examples/workflow-server/tsconfig.json Adds reference to server-mcp package
examples/workflow-server/src/node/app.ts Integrates MCP module into workflow server
examples/workflow-server/src/common/workflow-glsp-server.ts Migrates from deprecated handleInitializeArgs to GLSPServerInitContribution
examples/workflow-server/src/common/workflow-diagram-module.ts Updates binding to use new init contribution pattern
examples/workflow-server/package.json Adds dependency on server-mcp package

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/server-mcp/src/http-server-with-sessions.ts Outdated
Exposes each GLSP server as an MCP server over Streamable HTTP, so LLM
clients can query and mutate diagrams via MCP resources, tools, and
prompts.

Architecture
- One MCP endpoint per GLSP server process; under per-window
  deployment, one endpoint per application instance.
- Auto-discovers diagram-scoped contributions from all GLSP client
  sessions and merges them into a shared surface with cross-session
  ID aliasing.
- Pluggable model-serializer, label-provider, and element-types
  extension points for diagram-specific overrides.
- Opt-in: started only when configured on initialize.
- Random port by default; the announced URL is surfaced for adopter
  discovery.
- No built-in auth; loopback-only unless explicitly acknowledged.
- Targets MCP spec 2025-06-18 via the official SDK.

Adopters
- Diagram module: register language-specific tools, prompts,
  resources, and override the diagram extension points.
- Server module: configure server-wide options (port, route, auth
  acknowledgement).
- The workflow server example wires both end-to-end.

Server framework
- Adds an initialize-contribution extension point on the default
  GLSP server, used by MCP to surface configuration on initialize.
  The previous `handleInitializeArgs` hook is deprecated — adopters
  subclassing the default server should migrate.

Documentation
- Adopter README plus a separate architecture document covering the
  feature matrix, lifecycle, extension points, and rationale.

Misc
- Small fix to a base type-guard on edges in the graph package.
- Disposes the server instance when the JSON-RPC client connection
  closes uncleanly (browser/IDE force-close).
- Action dispatcher drops stale response actions arriving without a
  registered handler instead of throwing.

Part of eclipse-glsp/glsp#1546

Co-authored-by: Andreas Hell <44035624+Sakrafux@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@tortmayr tortmayr left a comment

Choose a reason for hiding this comment

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

Thanks for the rework Martin.
In general the code looks good to me.
I have a couple of inline comments.

In addition, we should probably create follow-up issues for the following tasks

  • Java alignment/implementation. Not planned in yet from our side, but we should open the issue nevertheless for tracking. (tag with help wanted, looking for sponsor)

  • Once this is merged we should update the protocol doc on the website

  • It seems like we have a couple of concepts in place (initializers, factories) which sole purpose is to bypass inversify circular dependencies for contribution points. We should consider an alignmnet here and use the LazyInjector approach consistently (same as in client)


@inject(GLSPMcpServerFactory) protected glspMcpServerFactory: GLSPMcpServerFactory;

@inject(McpDiagramHandlerDispatcher) protected dispatcher: DefaultMcpDiagramHandlerDispatcher;
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.

Weird mix of symbol injection and direct class typing.

Should probably create an interface for the McpDiagramHandlerDispatcher and use it here.

Alternatively, if you dont want to interface this consider renaming to McpDiagramHandlerDispatcher and directly using the class as service identifier

() =>
this.ping().catch(err => {
if (!(err instanceof McpError) || err.code !== ErrorCode.RequestTimeout) {
console.debug('MCP keep-alive ping failed:', err);
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.

Consider injecting and using the logger here instead of plain console.log

return new GLabelBuilder(GLabel)
.type(ModelTypes.LABEL_HEADING)
.id(this.proxy.id + '_classname')
.id(this.proxy.id + '_label')
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.

Question: Should we update the example1.wf file on the client side to reflect this change?
Or is is purely cosmetically?

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.

The file is already correct, just the server was inconsistent. We can leave it I believe.

/** Best-effort fan-out — failures on individual MCP sessions (e.g. transport mid-close) are swallowed. */
protected broadcastResourceListChanged(): void {
for (const glspMcpServer of this.sessionServers.values()) {
glspMcpServer
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.

SHould we directly expose this on the GLSPMcpServer TopLevel ?
Or is this a one time use, and we don't expect adopters to need the same functionality?

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.

I wouldn't expect adopters to overwrite this tbh. we are the only caller and then we would also need to expose other similar methods. If you agree, I think wee should leave it as is.

@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (c) 2022-2024 STMicroelectronics and others.
* Copyright (c) 2022-2026 STMicroelectronics and others.
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.

Single header change with out additional file changes, is that correct?

* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import {
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.

Double check comments, trim fluff.

* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import * as http from 'http';
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.

Should be in a .spec.ts file. Otherwise it gets shipped as part of the source code

* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { ApplyLabelEditOperation, CreateNodeOperation } from '@eclipse-glsp/server';
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.

Should this file be tested as well?

}
}

export namespace CreateNodeOperationHandler {
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.

Place the namespace alongside the corresponding interface

try {
result = await initializer.initializeServer(this, params, result);
} catch (error: unknown) {
this.logger.error(`Error during server initialization from ${initializer.constructor.name}:`, error);
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.

Catch without rethrow breaks promise resolution.
We catch all errors -> promise returned is never rejected

@martin-fleck-at
Copy link
Copy Markdown
Contributor Author

Thanks for the rework Martin. In general the code looks good to me. I have a couple of inline comments.

Thank you very much, I'll address them shortly!

  • It seems like we have a couple of concepts in place (initializers, factories) which sole purpose is to bypass inversify circular dependencies for contribution points. We should consider an alignmnet here and use the LazyInjector approach consistently (same as in client)

Do you have any particular places in mind that we introduced? I think we only have the registry initializers, similar to the ActionHandlerRegistryInitializer and OperationHandlerRegistryInitializer that already exist. And then we have a few factories similar to OperationHandlerFactory and ActionHandlerFactory. Or is that a more general comment to align long-term?

@tortmayr
Copy link
Copy Markdown
Contributor

Do you have any particular places in mind that we introduced? I think we only have the registry initializers, similar to the ActionHandlerRegistryInitializer and OperationHandlerRegistryInitializer that already exist. And then we have a few factories similar to OperationHandlerFactory and ActionHandlerFactory. Or is that a more general comment to align long-term?

That was more a general comment to align long term. These concepts were introduced before the LazyInjector API was available. Now, it think it makes sense to align this and use one consistent approach for initializing contributions.

- McpHttpTransport.dispose: catch per-session close() rejections
- McpDiagramHandlerDispatcher: extract interface
- harvest: factor out getConstructorList helper + clarify JSDoc
- dispatchStaticDiagramRead: clarify "pick first session"
- DefaultGLSPMcpServer: inject Logger, drop console.debug
- GShapeElement.is: drop; callers use `instanceof`
- GEdge.is: deprecate; callers use `instanceof`
- Move prompt-handler tests to resolveActiveSessionId spec
- Add ElkLayoutModule (subclassable); keep factory wrapper
- workflow-server: switch to new ElkLayoutModule
@martin-fleck-at
Copy link
Copy Markdown
Contributor Author

I pushed a commit that hopefully addresses all your concerns.

- glsp-server: rethrow initializer error so initialize() rejects
- glsp-mcp-server: redeclare picked methods; @Unmanaged ctor params
- create-operation-handler: place namespaces alongside interfaces
- ElementTypeEntry: trim header doc + add @experimental
- mcp-model-serializer: drop stale alias-function note
- mcp-resource-handler: trim header doc
- mcp-server-launcher: trim dense JSDoc/inline comments
- Rename dual-emit.spec → mcp-tool-handler.spec
- Rename raw-http.test-util → raw-http.spec (drop tsconfig exclude)
- Add create-nodes-mcp-tool-handler.spec (4 cases)
- Revert incidental year bump on create-merge/decision-node-handler
@tortmayr tortmayr self-requested a review May 12, 2026 15:51
Copy link
Copy Markdown
Contributor

@tortmayr tortmayr left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks for the fast update.
👍🏼

@martin-fleck-at martin-fleck-at merged commit a58ea45 into main May 12, 2026
5 checks passed
@martin-fleck-at martin-fleck-at deleted the issues/1546 branch May 12, 2026 22:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants