-
Notifications
You must be signed in to change notification settings - Fork 16
Issues/1546 #127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Sakrafux
wants to merge
26
commits into
eclipse-glsp:issues/1546
Choose a base branch
from
Sakrafux:issues/1546
base: issues/1546
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Issues/1546 #127
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
d0e330a
Restructured MCP resources and added PNG resource
Sakrafux c6fed18
Restructured MCP tools
Sakrafux e41d24f
Reorganized tool and resource registration
Sakrafux c6ec0f8
Added info and modifying tools
Sakrafux 5737dad
Switched IDs from UUIDs to semantic IDs
Sakrafux 6b8b49f
Moved new ID generation from generic framework code to actual impleme…
Sakrafux 14dd726
Added workflow specific implementations
Sakrafux 87f4ce0
Further refined model creation and modification, specifically for wor…
Sakrafux e67d7da
Make node and edge creation multi-input handlers
Sakrafux 3051037
Aligned logic across handlers and added more documentation
Sakrafux 25ebb63
Added undo/redo tools
Sakrafux 832028e
Added modify-edges tool
Sakrafux 6472a41
Added request-layout tool
Sakrafux 80a42a8
Added get-selection tool and refined diagram-elements
Sakrafux eeeabbe
Restructured MCP png components
Sakrafux 6407fca
Added viewport changes via change-view
Sakrafux 7aa6f98
Added export
Sakrafux bbaae34
reverted custom ID generation
Sakrafux bb0c276
Added JSON/Markdown switch
Sakrafux b2bf157
Added ID alias feature with flag
Sakrafux bbe418c
Cleanup and agent guidance
Sakrafux 3cfa276
Removed JSON flag
Sakrafux b6303f9
Cleanup of feature flags
Sakrafux 6026bbc
Added README
Sakrafux 8d5c119
Cleanup server across connections
Sakrafux 58d34dd
Fixed buggy or unstable behavior
Sakrafux File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
examples/workflow-server/src/common/mcp/workflow-create-nodes-handler.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| /******************************************************************************** | ||
| * 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 { GLabel, GModelElement } from '@eclipse-glsp/server'; | ||
| import { CreateNodesMcpToolHandler } from '@eclipse-glsp/server-mcp'; | ||
| import { injectable } from 'inversify'; | ||
| import { ModelTypes } from '../util/model-types'; | ||
|
|
||
| @injectable() | ||
| export class WorkflowCreateNodesMcpToolHandler extends CreateNodesMcpToolHandler { | ||
| override getCorrespondingLabelId(element: GModelElement): string | undefined { | ||
| // Category labels are nested in a header component | ||
| if (element.type === ModelTypes.CATEGORY) { | ||
| return element.children.find(child => child.type === ModelTypes.COMP_HEADER)?.children.find(child => child instanceof GLabel) | ||
| ?.id; | ||
| } | ||
|
|
||
| // Assume that generally, labelled nodes have those labels as direct children | ||
| return element.children.find(child => child instanceof GLabel)?.id; | ||
| } | ||
| } |
155 changes: 155 additions & 0 deletions
155
examples/workflow-server/src/common/mcp/workflow-element-types-handler.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| /******************************************************************************** | ||
| * 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 { DefaultTypes } from '@eclipse-glsp/server'; | ||
| import { | ||
| createResourceToolResult, | ||
| ElementTypesMcpResourceHandler, | ||
| GLSPMcpServer, | ||
| objectArrayToMarkdownTable, | ||
| ResourceHandlerResult | ||
| } from '@eclipse-glsp/server-mcp'; | ||
| import { injectable } from 'inversify'; | ||
| import { ModelTypes } from '../util/model-types'; | ||
| import * as z from 'zod/v4'; | ||
|
|
||
| interface ElementType { | ||
| id: string; | ||
| label: string; | ||
| description: string; | ||
| hasLabel: boolean; | ||
| } | ||
|
|
||
| const WORKFLOW_NODE_ELEMENT_TYPES: ElementType[] = [ | ||
| { | ||
| id: ModelTypes.AUTOMATED_TASK, | ||
| label: 'Automated Task', | ||
| description: 'Task without human input', | ||
| hasLabel: true | ||
| }, | ||
| { | ||
| id: ModelTypes.MANUAL_TASK, | ||
| label: 'Manual Task', | ||
| description: 'Task done by a human', | ||
| hasLabel: true | ||
| }, | ||
| { | ||
| id: ModelTypes.JOIN_NODE, | ||
| label: 'Join Node', | ||
| description: 'Gateway that merges parallel flows', | ||
| hasLabel: false | ||
| }, | ||
| { | ||
| id: ModelTypes.FORK_NODE, | ||
| label: 'Fork Node', | ||
| description: 'Gateway that splits into parallel flows', | ||
| hasLabel: false | ||
| }, | ||
| { | ||
| id: ModelTypes.MERGE_NODE, | ||
| label: 'Merge Node', | ||
| description: 'Gateway that merges alternative flows', | ||
| hasLabel: false | ||
| }, | ||
| { | ||
| id: ModelTypes.DECISION_NODE, | ||
| label: 'Decision Node', | ||
| description: 'Gateway that splits into alternative flows', | ||
| hasLabel: false | ||
| }, | ||
| { | ||
| id: ModelTypes.CATEGORY, | ||
| label: 'Category', | ||
| description: 'Container node that groups other elements', | ||
| hasLabel: true | ||
| } | ||
| ]; | ||
| const WORKFLOW_EDGE_ELEMENT_TYPES: ElementType[] = [ | ||
| { | ||
| id: DefaultTypes.EDGE, | ||
| label: 'Edge', | ||
| description: 'Standard control flow edge', | ||
| hasLabel: false | ||
| }, | ||
| { | ||
| id: ModelTypes.WEIGHTED_EDGE, | ||
| label: 'Weighted Edge', | ||
| description: 'Edge that indicates a weighted probability. Typically used with a Decision Node.', | ||
| hasLabel: false | ||
| } | ||
| ]; | ||
|
|
||
| const WORKFLOW_ELEMENT_TYPES_STRING = [ | ||
| '# Creatable element types for diagram type "workflow-diagram"', | ||
| '## Node Types', | ||
| objectArrayToMarkdownTable(WORKFLOW_NODE_ELEMENT_TYPES), | ||
| '## Edge Types', | ||
| objectArrayToMarkdownTable(WORKFLOW_EDGE_ELEMENT_TYPES) | ||
| ].join('\n'); | ||
|
|
||
| /** | ||
| * The default {@link ElementTypesMcpResourceHandler} extracts a list of operations generically from | ||
| * the `OperationHandlerRegistry`, because it can't know the details of a specific GLSP implementation. | ||
| * This is naturally quite limited in expression and relies on semantically meaningful model types to be | ||
| * able to inform an MCP client reliably. | ||
| * | ||
| * However, when overriding this for a specific implementation, we don't have those limitations. Rather, | ||
| * since the available element types do not change dynamically, we can simply provide a statically generated | ||
| * string. | ||
| */ | ||
| @injectable() | ||
| export class WorkflowElementTypesMcpResourceHandler extends ElementTypesMcpResourceHandler { | ||
| override registerTool(server: GLSPMcpServer): void { | ||
| server.registerTool( | ||
| 'element-types', | ||
| { | ||
| title: 'Creatable Element Types', | ||
| description: | ||
| 'List all element types (nodes and edges) that can be created for a specific diagram type. ' + | ||
| 'Use this to discover valid elementTypeId values for creation tools.', | ||
| inputSchema: { | ||
| diagramType: z.string().describe('Diagram type whose elements should be discovered') | ||
| } | ||
| }, | ||
| async params => createResourceToolResult(await this.handle(params)) | ||
| ); | ||
| } | ||
|
|
||
| override async handle({ diagramType }: { diagramType?: string }): Promise<ResourceHandlerResult> { | ||
| this.logger.info(`'element-types' invoked for diagram type '${diagramType}'`); | ||
|
|
||
| // In this specifc GLSP implementation, only 'workflow-diagram' is valid | ||
| if (diagramType !== 'workflow-diagram') { | ||
| return { | ||
| content: { | ||
| uri: `glsp://types/${diagramType}/elements`, | ||
| mimeType: 'text/plain', | ||
| text: 'Invalid diagram type.' | ||
| }, | ||
| isError: true | ||
| }; | ||
| } | ||
|
|
||
| return { | ||
| content: { | ||
| uri: `glsp://types/${diagramType}/elements`, | ||
| mimeType: 'text/markdown', | ||
| text: WORKFLOW_ELEMENT_TYPES_STRING | ||
| }, | ||
| isError: false | ||
| }; | ||
| } | ||
| } |
153 changes: 153 additions & 0 deletions
153
examples/workflow-server/src/common/mcp/workflow-mcp-model-serializer.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| /******************************************************************************** | ||
| * 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 { GModelElement } from '@eclipse-glsp/graph'; | ||
| import { DefaultTypes } from '@eclipse-glsp/server'; | ||
| import { DefaultMcpModelSerializer } from '@eclipse-glsp/server-mcp'; | ||
| import { injectable } from 'inversify'; | ||
| import { ModelTypes } from '../util/model-types'; | ||
|
|
||
| /** | ||
| * As compared to the {@link DefaultMcpModelSerializer}, this is a specific implementation and we | ||
| * know not only the structure of our graph but also each relevant attribute. This enables us to | ||
| * order them semantically so the produced serialization makes more sense if read with semantics | ||
| * mind. As LLMs (i.e., the MCP clients) work semantically, this is superior to a random ordering. | ||
| * Furthermore, including only the relevant information without redundancies decreases context size. | ||
| */ | ||
| @injectable() | ||
| export class WorkflowMcpModelSerializer extends DefaultMcpModelSerializer { | ||
| override prepareElement(element: GModelElement): Record<string, Record<string, any>[]> { | ||
| const elements = this.flattenStructure(element); | ||
|
|
||
| // Define the order of keys | ||
| const result: Record<string, Record<string, any>[]> = { | ||
| [DefaultTypes.GRAPH]: [], | ||
| [ModelTypes.CATEGORY]: [], | ||
| [ModelTypes.AUTOMATED_TASK]: [], | ||
| [ModelTypes.MANUAL_TASK]: [], | ||
| [ModelTypes.FORK_NODE]: [], | ||
| [ModelTypes.JOIN_NODE]: [], | ||
| [ModelTypes.DECISION_NODE]: [], | ||
| [ModelTypes.MERGE_NODE]: [], | ||
| [DefaultTypes.EDGE]: [], | ||
| [ModelTypes.WEIGHTED_EDGE]: [] | ||
| }; | ||
| elements.forEach(element => { | ||
| this.combinePositionAndSize(element); | ||
|
|
||
| const adjustedElement = this.adjustElement(element); | ||
| if (!adjustedElement) { | ||
| return; | ||
| } | ||
|
|
||
| result[element.type].push(adjustedElement); | ||
| }); | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| private adjustElement(element: Record<string, any>): Record<string, any> | undefined { | ||
| switch (element.type) { | ||
| case ModelTypes.AUTOMATED_TASK: | ||
| case ModelTypes.MANUAL_TASK: { | ||
| const label = element.children.find((child: { type: string }) => child.type === ModelTypes.LABEL_HEADING); | ||
|
|
||
| // For tasks, the only content with impact on element size is the label | ||
| // Therefore, all other factors get integrated into the label size for the AI to do proper resizing operations | ||
| const labelSize = { | ||
| // 10px padding right, 31px padding left (incl. icon) | ||
| width: Math.trunc(label.size.width + 10 + 31), | ||
| // 7px padding top and bottom each | ||
| height: Math.trunc(label.size.height + 14) | ||
| }; | ||
|
|
||
| return { | ||
| id: element.id, | ||
| position: element.position, | ||
| size: element.size, | ||
| bounds: element.bounds, | ||
| label: label.text, | ||
| labelSize: labelSize, | ||
| parentId: element.parent.type === ModelTypes.STRUCTURE ? element.parent.parent.id : element.parentId | ||
| }; | ||
| } | ||
| case ModelTypes.CATEGORY: { | ||
| const label = element.children | ||
| .find((child: { type: string }) => child.type === ModelTypes.COMP_HEADER) | ||
| ?.children.find((child: { type: string }) => child.type === ModelTypes.LABEL_HEADING); | ||
|
|
||
| const labelSize = { | ||
| width: Math.trunc(label.size.width + 20), | ||
| height: Math.trunc(label.size.height + 20) | ||
| }; | ||
|
|
||
| const usableSpaceSize = { | ||
| width: Math.trunc(Math.max(0, element.size.width - 10)), | ||
| height: Math.trunc(Math.max(0, element.size.height - labelSize.height - 10)) | ||
| }; | ||
|
|
||
| return { | ||
| id: element.id, | ||
| isContainer: true, | ||
| position: element.position, | ||
| size: element.size, | ||
| bounds: element.bounds, | ||
| label: label.text, | ||
| labelSize: labelSize, | ||
| usableSpaceSize: usableSpaceSize, | ||
| parentId: element.parentId | ||
| }; | ||
| } | ||
| case ModelTypes.JOIN_NODE: | ||
| case ModelTypes.MERGE_NODE: | ||
| case ModelTypes.DECISION_NODE: | ||
| case ModelTypes.FORK_NODE: { | ||
| return { | ||
| id: element.id, | ||
| position: element.position, | ||
| size: element.size, | ||
| bounds: element.bounds, | ||
| parentId: element.parentId | ||
| }; | ||
| } | ||
| case DefaultTypes.EDGE: { | ||
| return { | ||
| id: element.id, | ||
| sourceId: element.sourceId, | ||
| targetId: element.targetId, | ||
| parentId: element.parentId | ||
| }; | ||
| } | ||
| case ModelTypes.WEIGHTED_EDGE: { | ||
| return { | ||
| id: element.id, | ||
| sourceId: element.sourceId, | ||
| targetId: element.targetId, | ||
| probability: element.probability, | ||
| parentId: element.parentId | ||
| }; | ||
| } | ||
| case DefaultTypes.GRAPH: { | ||
| return { | ||
| id: element.id, | ||
| isContainer: true | ||
| }; | ||
| } | ||
| default: | ||
| return undefined; | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is necessary to hard-code the size here if it was not necessary before? (same in the merge-node-handler)