@eclipse-glsp/server-mcp 2.7.0-next.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +642 -0
- package/README.md +57 -0
- package/lib/index.d.ts +23 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +41 -0
- package/lib/index.js.map +1 -0
- package/lib/prompts/handlers/describe-diagram-mcp-prompt-handler.d.ts +43 -0
- package/lib/prompts/handlers/describe-diagram-mcp-prompt-handler.d.ts.map +1 -0
- package/lib/prompts/handlers/describe-diagram-mcp-prompt-handler.js +96 -0
- package/lib/prompts/handlers/describe-diagram-mcp-prompt-handler.js.map +1 -0
- package/lib/prompts/handlers/suggest-improvements-mcp-prompt-handler.d.ts +43 -0
- package/lib/prompts/handlers/suggest-improvements-mcp-prompt-handler.d.ts.map +1 -0
- package/lib/prompts/handlers/suggest-improvements-mcp-prompt-handler.js +95 -0
- package/lib/prompts/handlers/suggest-improvements-mcp-prompt-handler.js.map +1 -0
- package/lib/prompts/index.d.ts +18 -0
- package/lib/prompts/index.d.ts.map +1 -0
- package/lib/prompts/index.js +34 -0
- package/lib/prompts/index.js.map +1 -0
- package/lib/resources/handlers/diagram-png-mcp-resource-handler.d.ts +81 -0
- package/lib/resources/handlers/diagram-png-mcp-resource-handler.d.ts.map +1 -0
- package/lib/resources/handlers/diagram-png-mcp-resource-handler.js +174 -0
- package/lib/resources/handlers/diagram-png-mcp-resource-handler.js.map +1 -0
- package/lib/resources/handlers/diagram-svg-mcp-resource-handler.d.ts +52 -0
- package/lib/resources/handlers/diagram-svg-mcp-resource-handler.d.ts.map +1 -0
- package/lib/resources/handlers/diagram-svg-mcp-resource-handler.js +96 -0
- package/lib/resources/handlers/diagram-svg-mcp-resource-handler.js.map +1 -0
- package/lib/resources/index.d.ts +20 -0
- package/lib/resources/index.d.ts.map +1 -0
- package/lib/resources/index.js +36 -0
- package/lib/resources/index.js.map +1 -0
- package/lib/resources/services/element-types-provider.d.ts +65 -0
- package/lib/resources/services/element-types-provider.d.ts.map +1 -0
- package/lib/resources/services/element-types-provider.js +81 -0
- package/lib/resources/services/element-types-provider.js.map +1 -0
- package/lib/resources/services/mcp-model-serializer.d.ts +78 -0
- package/lib/resources/services/mcp-model-serializer.d.ts.map +1 -0
- package/lib/resources/services/mcp-model-serializer.js +188 -0
- package/lib/resources/services/mcp-model-serializer.js.map +1 -0
- package/lib/server/glsp-mcp-server.d.ts +82 -0
- package/lib/server/glsp-mcp-server.d.ts.map +1 -0
- package/lib/server/glsp-mcp-server.js +140 -0
- package/lib/server/glsp-mcp-server.js.map +1 -0
- package/lib/server/index.d.ts +37 -0
- package/lib/server/index.d.ts.map +1 -0
- package/lib/server/index.js +57 -0
- package/lib/server/index.js.map +1 -0
- package/lib/server/lru-event-store.d.ts +53 -0
- package/lib/server/lru-event-store.d.ts.map +1 -0
- package/lib/server/lru-event-store.js +100 -0
- package/lib/server/lru-event-store.js.map +1 -0
- package/lib/server/mcp-diagram-handler-dispatcher.d.ts +144 -0
- package/lib/server/mcp-diagram-handler-dispatcher.d.ts.map +1 -0
- package/lib/server/mcp-diagram-handler-dispatcher.js +382 -0
- package/lib/server/mcp-diagram-handler-dispatcher.js.map +1 -0
- package/lib/server/mcp-diagram-module.d.ts +123 -0
- package/lib/server/mcp-diagram-module.d.ts.map +1 -0
- package/lib/server/mcp-diagram-module.js +186 -0
- package/lib/server/mcp-diagram-module.js.map +1 -0
- package/lib/server/mcp-diagram-prompt-handler-registry.d.ts +33 -0
- package/lib/server/mcp-diagram-prompt-handler-registry.d.ts.map +1 -0
- package/lib/server/mcp-diagram-prompt-handler-registry.js +76 -0
- package/lib/server/mcp-diagram-prompt-handler-registry.js.map +1 -0
- package/lib/server/mcp-diagram-resource-handler-registry.d.ts +35 -0
- package/lib/server/mcp-diagram-resource-handler-registry.d.ts.map +1 -0
- package/lib/server/mcp-diagram-resource-handler-registry.js +94 -0
- package/lib/server/mcp-diagram-resource-handler-registry.js.map +1 -0
- package/lib/server/mcp-diagram-tool-handler-registry.d.ts +57 -0
- package/lib/server/mcp-diagram-tool-handler-registry.d.ts.map +1 -0
- package/lib/server/mcp-diagram-tool-handler-registry.js +111 -0
- package/lib/server/mcp-diagram-tool-handler-registry.js.map +1 -0
- package/lib/server/mcp-handler-shared.d.ts +142 -0
- package/lib/server/mcp-handler-shared.d.ts.map +1 -0
- package/lib/server/mcp-handler-shared.js +199 -0
- package/lib/server/mcp-handler-shared.js.map +1 -0
- package/lib/server/mcp-http-transport.d.ts +93 -0
- package/lib/server/mcp-http-transport.d.ts.map +1 -0
- package/lib/server/mcp-http-transport.js +350 -0
- package/lib/server/mcp-http-transport.js.map +1 -0
- package/lib/server/mcp-id-alias-service.d.ts +70 -0
- package/lib/server/mcp-id-alias-service.d.ts.map +1 -0
- package/lib/server/mcp-id-alias-service.js +85 -0
- package/lib/server/mcp-id-alias-service.js.map +1 -0
- package/lib/server/mcp-input-schemas.d.ts +73 -0
- package/lib/server/mcp-input-schemas.d.ts.map +1 -0
- package/lib/server/mcp-input-schemas.js +67 -0
- package/lib/server/mcp-input-schemas.js.map +1 -0
- package/lib/server/mcp-label-provider.d.ts +45 -0
- package/lib/server/mcp-label-provider.d.ts.map +1 -0
- package/lib/server/mcp-label-provider.js +42 -0
- package/lib/server/mcp-label-provider.js.map +1 -0
- package/lib/server/mcp-log-level-registry.d.ts +54 -0
- package/lib/server/mcp-log-level-registry.d.ts.map +1 -0
- package/lib/server/mcp-log-level-registry.js +80 -0
- package/lib/server/mcp-log-level-registry.js.map +1 -0
- package/lib/server/mcp-logger.d.ts +59 -0
- package/lib/server/mcp-logger.d.ts.map +1 -0
- package/lib/server/mcp-logger.js +104 -0
- package/lib/server/mcp-logger.js.map +1 -0
- package/lib/server/mcp-mime-types.d.ts +28 -0
- package/lib/server/mcp-mime-types.d.ts.map +1 -0
- package/lib/server/mcp-mime-types.js +18 -0
- package/lib/server/mcp-mime-types.js.map +1 -0
- package/lib/server/mcp-options.d.ts +39 -0
- package/lib/server/mcp-options.d.ts.map +1 -0
- package/lib/server/mcp-options.js +53 -0
- package/lib/server/mcp-options.js.map +1 -0
- package/lib/server/mcp-progress-reporter.d.ts +48 -0
- package/lib/server/mcp-progress-reporter.d.ts.map +1 -0
- package/lib/server/mcp-progress-reporter.js +66 -0
- package/lib/server/mcp-progress-reporter.js.map +1 -0
- package/lib/server/mcp-prompt-handler.d.ts +120 -0
- package/lib/server/mcp-prompt-handler.d.ts.map +1 -0
- package/lib/server/mcp-prompt-handler.js +131 -0
- package/lib/server/mcp-prompt-handler.js.map +1 -0
- package/lib/server/mcp-request-context.d.ts +37 -0
- package/lib/server/mcp-request-context.d.ts.map +1 -0
- package/lib/server/mcp-request-context.js +37 -0
- package/lib/server/mcp-request-context.js.map +1 -0
- package/lib/server/mcp-resource-handler.d.ts +212 -0
- package/lib/server/mcp-resource-handler.d.ts.map +1 -0
- package/lib/server/mcp-resource-handler.js +298 -0
- package/lib/server/mcp-resource-handler.js.map +1 -0
- package/lib/server/mcp-server-launcher.d.ts +143 -0
- package/lib/server/mcp-server-launcher.d.ts.map +1 -0
- package/lib/server/mcp-server-launcher.js +355 -0
- package/lib/server/mcp-server-launcher.js.map +1 -0
- package/lib/server/mcp-server-module.d.ts +143 -0
- package/lib/server/mcp-server-module.d.ts.map +1 -0
- package/lib/server/mcp-server-module.js +249 -0
- package/lib/server/mcp-server-module.js.map +1 -0
- package/lib/server/mcp-session.d.ts +44 -0
- package/lib/server/mcp-session.d.ts.map +1 -0
- package/lib/server/mcp-session.js +18 -0
- package/lib/server/mcp-session.js.map +1 -0
- package/lib/server/mcp-tool-handler.d.ts +259 -0
- package/lib/server/mcp-tool-handler.d.ts.map +1 -0
- package/lib/server/mcp-tool-handler.js +355 -0
- package/lib/server/mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/count-elements-mcp-tool-handler.d.ts +46 -0
- package/lib/tools/handlers/count-elements-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/count-elements-mcp-tool-handler.js +76 -0
- package/lib/tools/handlers/count-elements-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/create-edges-mcp-tool-handler.d.ts +112 -0
- package/lib/tools/handlers/create-edges-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/create-edges-mcp-tool-handler.js +190 -0
- package/lib/tools/handlers/create-edges-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/create-nodes-mcp-tool-handler.d.ts +81 -0
- package/lib/tools/handlers/create-nodes-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/create-nodes-mcp-tool-handler.js +123 -0
- package/lib/tools/handlers/create-nodes-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/delete-elements-mcp-tool-handler.d.ts +52 -0
- package/lib/tools/handlers/delete-elements-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/delete-elements-mcp-tool-handler.js +73 -0
- package/lib/tools/handlers/delete-elements-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/diagram-model-mcp-tool-handler.d.ts +59 -0
- package/lib/tools/handlers/diagram-model-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/diagram-model-mcp-tool-handler.js +78 -0
- package/lib/tools/handlers/diagram-model-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/element-types-mcp-tool-handler.d.ts +97 -0
- package/lib/tools/handlers/element-types-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/element-types-mcp-tool-handler.js +155 -0
- package/lib/tools/handlers/element-types-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/get-selection-mcp-tool-handler.d.ts +43 -0
- package/lib/tools/handlers/get-selection-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/get-selection-mcp-tool-handler.js +68 -0
- package/lib/tools/handlers/get-selection-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/layout-mcp-tool-handler.d.ts +43 -0
- package/lib/tools/handlers/layout-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/layout-mcp-tool-handler.js +71 -0
- package/lib/tools/handlers/layout-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/modify-edges-mcp-tool-handler.d.ts +78 -0
- package/lib/tools/handlers/modify-edges-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/modify-edges-mcp-tool-handler.js +136 -0
- package/lib/tools/handlers/modify-edges-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/modify-nodes-mcp-tool-handler.d.ts +92 -0
- package/lib/tools/handlers/modify-nodes-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/modify-nodes-mcp-tool-handler.js +125 -0
- package/lib/tools/handlers/modify-nodes-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/query-elements-mcp-tool-handler.d.ts +102 -0
- package/lib/tools/handlers/query-elements-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/query-elements-mcp-tool-handler.js +158 -0
- package/lib/tools/handlers/query-elements-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/redo-mcp-tool-handler.d.ts +45 -0
- package/lib/tools/handlers/redo-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/redo-mcp-tool-handler.js +73 -0
- package/lib/tools/handlers/redo-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/save-model-mcp-tool-handler.d.ts +55 -0
- package/lib/tools/handlers/save-model-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/save-model-mcp-tool-handler.js +91 -0
- package/lib/tools/handlers/save-model-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/session-info-mcp-tool-handler.d.ts +65 -0
- package/lib/tools/handlers/session-info-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/session-info-mcp-tool-handler.js +108 -0
- package/lib/tools/handlers/session-info-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/set-selection-mcp-tool-handler.d.ts +60 -0
- package/lib/tools/handlers/set-selection-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/set-selection-mcp-tool-handler.js +103 -0
- package/lib/tools/handlers/set-selection-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/set-view-mcp-tool-handler.d.ts +110 -0
- package/lib/tools/handlers/set-view-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/set-view-mcp-tool-handler.js +142 -0
- package/lib/tools/handlers/set-view-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/undo-mcp-tool-handler.d.ts +45 -0
- package/lib/tools/handlers/undo-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/undo-mcp-tool-handler.js +74 -0
- package/lib/tools/handlers/undo-mcp-tool-handler.js.map +1 -0
- package/lib/tools/handlers/validate-diagram-mcp-tool-handler.d.ts +66 -0
- package/lib/tools/handlers/validate-diagram-mcp-tool-handler.d.ts.map +1 -0
- package/lib/tools/handlers/validate-diagram-mcp-tool-handler.js +0 -0
- package/lib/tools/handlers/validate-diagram-mcp-tool-handler.js.map +1 -0
- package/lib/tools/index.d.ts +34 -0
- package/lib/tools/index.d.ts.map +1 -0
- package/lib/tools/index.js +50 -0
- package/lib/tools/index.js.map +1 -0
- package/lib/util/index.d.ts +18 -0
- package/lib/util/index.d.ts.map +1 -0
- package/lib/util/index.js +34 -0
- package/lib/util/index.js.map +1 -0
- package/lib/util/markdown-util.d.ts +20 -0
- package/lib/util/markdown-util.d.ts.map +1 -0
- package/lib/util/markdown-util.js +45 -0
- package/lib/util/markdown-util.js.map +1 -0
- package/lib/util/mcp-util.d.ts +22 -0
- package/lib/util/mcp-util.d.ts.map +1 -0
- package/lib/util/mcp-util.js +29 -0
- package/lib/util/mcp-util.js.map +1 -0
- package/package.json +63 -0
- package/src/index.ts +24 -0
- package/src/prompts/handlers/describe-diagram-mcp-prompt-handler.ts +89 -0
- package/src/prompts/handlers/suggest-improvements-mcp-prompt-handler.ts +86 -0
- package/src/prompts/index.ts +18 -0
- package/src/resources/handlers/diagram-png-mcp-resource-handler.ts +181 -0
- package/src/resources/handlers/diagram-svg-mcp-resource-handler.ts +89 -0
- package/src/resources/index.ts +20 -0
- package/src/resources/services/element-types-provider.ts +105 -0
- package/src/resources/services/mcp-model-serializer.ts +211 -0
- package/src/server/glsp-mcp-server.spec.ts +73 -0
- package/src/server/glsp-mcp-server.ts +196 -0
- package/src/server/index.ts +42 -0
- package/src/server/lru-event-store.spec.ts +121 -0
- package/src/server/lru-event-store.ts +112 -0
- package/src/server/mcp-diagram-handler-dispatcher.spec.ts +231 -0
- package/src/server/mcp-diagram-handler-dispatcher.ts +459 -0
- package/src/server/mcp-diagram-module.ts +248 -0
- package/src/server/mcp-diagram-prompt-handler-registry.ts +59 -0
- package/src/server/mcp-diagram-resource-handler-registry.ts +73 -0
- package/src/server/mcp-diagram-tool-handler-registry.ts +97 -0
- package/src/server/mcp-handler-shared.spec.ts +53 -0
- package/src/server/mcp-handler-shared.ts +247 -0
- package/src/server/mcp-http-transport-e2e.spec.ts +151 -0
- package/src/server/mcp-http-transport.spec.ts +385 -0
- package/src/server/mcp-http-transport.ts +368 -0
- package/src/server/mcp-id-alias-service.spec.ts +106 -0
- package/src/server/mcp-id-alias-service.ts +104 -0
- package/src/server/mcp-input-schemas.ts +82 -0
- package/src/server/mcp-label-provider.ts +52 -0
- package/src/server/mcp-log-level-registry.spec.ts +75 -0
- package/src/server/mcp-log-level-registry.ts +90 -0
- package/src/server/mcp-logger.spec.ts +227 -0
- package/src/server/mcp-logger.ts +91 -0
- package/src/server/mcp-mime-types.ts +31 -0
- package/src/server/mcp-options.ts +43 -0
- package/src/server/mcp-progress-reporter.spec.ts +93 -0
- package/src/server/mcp-progress-reporter.ts +67 -0
- package/src/server/mcp-prompt-handler.ts +157 -0
- package/src/server/mcp-request-context.ts +39 -0
- package/src/server/mcp-resource-handler.ts +389 -0
- package/src/server/mcp-server-launcher.spec.ts +173 -0
- package/src/server/mcp-server-launcher.ts +369 -0
- package/src/server/mcp-server-module.ts +287 -0
- package/src/server/mcp-session.ts +45 -0
- package/src/server/mcp-tool-handler.spec.ts +182 -0
- package/src/server/mcp-tool-handler.ts +431 -0
- package/src/server/raw-http.spec.ts +59 -0
- package/src/tools/handlers/count-elements-mcp-tool-handler.spec.ts +99 -0
- package/src/tools/handlers/count-elements-mcp-tool-handler.ts +66 -0
- package/src/tools/handlers/create-edges-mcp-tool-handler.spec.ts +196 -0
- package/src/tools/handlers/create-edges-mcp-tool-handler.ts +205 -0
- package/src/tools/handlers/create-nodes-mcp-tool-handler.spec.ts +197 -0
- package/src/tools/handlers/create-nodes-mcp-tool-handler.ts +131 -0
- package/src/tools/handlers/delete-elements-mcp-tool-handler.ts +73 -0
- package/src/tools/handlers/diagram-model-mcp-tool-handler.ts +66 -0
- package/src/tools/handlers/element-types-mcp-tool-handler.ts +151 -0
- package/src/tools/handlers/get-selection-mcp-tool-handler.ts +54 -0
- package/src/tools/handlers/layout-mcp-tool-handler.ts +56 -0
- package/src/tools/handlers/modify-edges-mcp-tool-handler.ts +148 -0
- package/src/tools/handlers/modify-nodes-mcp-tool-handler.ts +140 -0
- package/src/tools/handlers/query-elements-mcp-tool-handler.spec.ts +210 -0
- package/src/tools/handlers/query-elements-mcp-tool-handler.ts +161 -0
- package/src/tools/handlers/redo-mcp-tool-handler.ts +62 -0
- package/src/tools/handlers/save-model-mcp-tool-handler.ts +71 -0
- package/src/tools/handlers/session-info-mcp-tool-handler.spec.ts +152 -0
- package/src/tools/handlers/session-info-mcp-tool-handler.ts +97 -0
- package/src/tools/handlers/set-selection-mcp-tool-handler.spec.ts +118 -0
- package/src/tools/handlers/set-selection-mcp-tool-handler.ts +90 -0
- package/src/tools/handlers/set-view-mcp-tool-handler.ts +162 -0
- package/src/tools/handlers/undo-mcp-tool-handler.ts +61 -0
- package/src/tools/handlers/validate-diagram-mcp-tool-handler.ts +0 -0
- package/src/tools/index.ts +34 -0
- package/src/tools/tool-annotations.spec.ts +141 -0
- package/src/util/index.ts +18 -0
- package/src/util/markdown-util.ts +44 -0
- package/src/util/mcp-util.ts +25 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
* http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
*
|
|
8
|
+
* This Source Code may also be made available under the following Secondary
|
|
9
|
+
* Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
* with the GNU Classpath Exception which is available at
|
|
12
|
+
* https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
*
|
|
14
|
+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
|
+
********************************************************************************/
|
|
16
|
+
|
|
17
|
+
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* **Note on terminology** — the word "session" is overloaded in this codebase. Two
|
|
21
|
+
* independent concepts exist and must not be conflated:
|
|
22
|
+
*
|
|
23
|
+
* 1. **MCP session** (this file): one MCP client connection to the MCP HTTP endpoint, tracked by
|
|
24
|
+
* {@link McpHttpTransport.sessions} keyed by the SDK-issued `mcp-session-id` HTTP header.
|
|
25
|
+
* Created in `onsessioninitialized`, supports resumability across reconnects via
|
|
26
|
+
* `Last-Event-ID`. Each {@link McpSession} corresponds to one underlying SDK `McpServer`
|
|
27
|
+
* instance — multiple LLM clients can connect simultaneously, each with its own MCP session
|
|
28
|
+
* and its own SDK server.
|
|
29
|
+
*
|
|
30
|
+
* 2. **GLSP client session** (core): one open diagram, tracked by core's `ClientSessionManager`
|
|
31
|
+
* and represented by `ClientSession`. Tools/resources/prompts that target a specific diagram
|
|
32
|
+
* receive the GLSP session id via `params.sessionId` in their input schema.
|
|
33
|
+
*
|
|
34
|
+
* The two are independent in lifetime and cardinality: a single MCP session sees ALL open GLSP
|
|
35
|
+
* sessions; a single GLSP session is visible to every connected MCP session. Names in this
|
|
36
|
+
* package distinguish them: anything with the `Mcp` prefix in {@link mcp-session.ts} or
|
|
37
|
+
* `mcp-http-transport.ts` is MCP-side; anything talking about diagrams (`AbstractMcpDiagram*`,
|
|
38
|
+
* `requireGlspSession`, `GlspSession`) is GLSP-side.
|
|
39
|
+
*/
|
|
40
|
+
export type McpSessionId = string;
|
|
41
|
+
|
|
42
|
+
export type WithSessionId<T> = T & { get sessionId(): McpSessionId };
|
|
43
|
+
|
|
44
|
+
/** SDK {@link Transport} widened with the session-id accessor every concrete MCP transport carries post-handshake. */
|
|
45
|
+
export type McpSession = WithSessionId<Transport>;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
* http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
*
|
|
8
|
+
* This Source Code may also be made available under the following Secondary
|
|
9
|
+
* Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
* with the GNU Classpath Exception which is available at
|
|
12
|
+
* https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
*
|
|
14
|
+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
|
+
********************************************************************************/
|
|
16
|
+
|
|
17
|
+
import { expect } from 'chai';
|
|
18
|
+
import * as z from 'zod/v4';
|
|
19
|
+
import { McpToolResult } from './mcp-handler-shared';
|
|
20
|
+
import { CreateEdgesMcpToolHandler, CreateEdgesOutputSchema } from '../tools/handlers/create-edges-mcp-tool-handler';
|
|
21
|
+
import { CreateNodesMcpToolHandler, CreateNodesOutputSchema } from '../tools/handlers/create-nodes-mcp-tool-handler';
|
|
22
|
+
import { DeleteElementsMcpToolHandler, DeleteElementsOutputSchema } from '../tools/handlers/delete-elements-mcp-tool-handler';
|
|
23
|
+
import { GetSelectionMcpToolHandler, GetSelectionOutputSchema } from '../tools/handlers/get-selection-mcp-tool-handler';
|
|
24
|
+
import { LayoutMcpToolHandler, LayoutOutputSchema } from '../tools/handlers/layout-mcp-tool-handler';
|
|
25
|
+
import { ModifyEdgesMcpToolHandler, ModifyEdgesOutputSchema } from '../tools/handlers/modify-edges-mcp-tool-handler';
|
|
26
|
+
import { ModifyNodesMcpToolHandler, ModifyNodesOutputSchema } from '../tools/handlers/modify-nodes-mcp-tool-handler';
|
|
27
|
+
import { QueryElementsMcpToolHandler, QueryElementsOutputSchema } from '../tools/handlers/query-elements-mcp-tool-handler';
|
|
28
|
+
import { RedoMcpToolHandler, RedoOutputSchema } from '../tools/handlers/redo-mcp-tool-handler';
|
|
29
|
+
import { SaveModelMcpToolHandler, SaveModelOutputSchema } from '../tools/handlers/save-model-mcp-tool-handler';
|
|
30
|
+
import { SetViewMcpToolHandler, SetViewOutputSchema } from '../tools/handlers/set-view-mcp-tool-handler';
|
|
31
|
+
import { UndoMcpToolHandler, UndoOutputSchema } from '../tools/handlers/undo-mcp-tool-handler';
|
|
32
|
+
import { ValidateDiagramMcpToolHandler, ValidateDiagramOutputSchema } from '../tools/handlers/validate-diagram-mcp-tool-handler';
|
|
33
|
+
import { AbstractMcpToolHandler } from './mcp-tool-handler';
|
|
34
|
+
|
|
35
|
+
// ─── Framework: BaseMcpToolHandler.success(...) and toRegistrationConfig() ────────────────────
|
|
36
|
+
|
|
37
|
+
class WithoutOutputSchemaHandler extends AbstractMcpToolHandler {
|
|
38
|
+
readonly name = 'no-schema';
|
|
39
|
+
readonly description = 'no-schema';
|
|
40
|
+
readonly inputSchema = z.object({});
|
|
41
|
+
|
|
42
|
+
protected createResult(): McpToolResult {
|
|
43
|
+
return this.success('plain');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Test exposure of the protected helper. `BaseMcpToolHandler` doesn't read `logger` here. */
|
|
47
|
+
public testSuccess(message: string, structured?: Record<string, unknown>): McpToolResult {
|
|
48
|
+
return this.success(message, structured);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
class WithOutputSchemaHandler extends AbstractMcpToolHandler {
|
|
53
|
+
readonly name = 'with-schema';
|
|
54
|
+
readonly description = 'with-schema';
|
|
55
|
+
readonly inputSchema = z.object({});
|
|
56
|
+
override readonly outputSchema = z.object({ ok: z.boolean() });
|
|
57
|
+
|
|
58
|
+
protected createResult(): McpToolResult {
|
|
59
|
+
return this.success('with', { ok: true });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
describe('Dual-emit framework', () => {
|
|
64
|
+
it('success(message) without structured payload omits structuredContent', () => {
|
|
65
|
+
const result = new WithoutOutputSchemaHandler().testSuccess('hi');
|
|
66
|
+
expect(result.content).to.deep.equal([{ type: 'text', text: 'hi' }]);
|
|
67
|
+
expect(result.structuredContent).to.equal(undefined);
|
|
68
|
+
expect(result.isError).to.equal(false);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('success(message, structured) emits structuredContent', () => {
|
|
72
|
+
const result = new WithoutOutputSchemaHandler().testSuccess('hi', { count: 3, ids: ['a'] });
|
|
73
|
+
expect(result.content).to.deep.equal([{ type: 'text', text: 'hi' }]);
|
|
74
|
+
expect(result.structuredContent).to.deep.equal({ count: 3, ids: ['a'] });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('toRegistrationConfig() omits outputSchema when not declared', () => {
|
|
78
|
+
const config = new WithoutOutputSchemaHandler().toRegistrationConfig();
|
|
79
|
+
expect(config.outputSchema).to.equal(undefined);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('toRegistrationConfig() forwards outputSchema when declared', () => {
|
|
83
|
+
const config = new WithOutputSchemaHandler().toRegistrationConfig();
|
|
84
|
+
expect(config.outputSchema).to.not.equal(undefined);
|
|
85
|
+
// The SDK accepts a full `ZodObject` (or its raw shape); we pass the wrapped object so
|
|
86
|
+
// strict-mode rejection of unknown keys can extend uniformly to outputs.
|
|
87
|
+
expect(Object.keys(config.outputSchema!.shape)).to.deep.equal(['ok']);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// ─── Per-handler schema matrix ────────────────────────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Each entry binds a handler constructor to its declared `OutputSchema` + a representative
|
|
95
|
+
* structured payload. The expectations:
|
|
96
|
+
* - Constructor's `outputSchema` field matches the exported schema (same reference).
|
|
97
|
+
* - The schema accepts the representative payload (zod parse passes).
|
|
98
|
+
*/
|
|
99
|
+
const matrix: Array<{
|
|
100
|
+
name: string;
|
|
101
|
+
Constructor: new () => { outputSchema?: unknown };
|
|
102
|
+
schema: z.ZodObject;
|
|
103
|
+
sample: Record<string, unknown>;
|
|
104
|
+
}> = [
|
|
105
|
+
{
|
|
106
|
+
name: 'create-nodes',
|
|
107
|
+
Constructor: CreateNodesMcpToolHandler,
|
|
108
|
+
schema: CreateNodesOutputSchema,
|
|
109
|
+
sample: { createdNodes: [{ id: 'n1', elementTypeId: 'node:foo' }], errors: [], warnings: [] }
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'create-edges',
|
|
113
|
+
Constructor: CreateEdgesMcpToolHandler,
|
|
114
|
+
schema: CreateEdgesOutputSchema,
|
|
115
|
+
sample: { createdEdges: [{ id: 'e1', elementTypeId: 'edge' }], errors: [] }
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: 'modify-nodes',
|
|
119
|
+
Constructor: ModifyNodesMcpToolHandler,
|
|
120
|
+
schema: ModifyNodesOutputSchema,
|
|
121
|
+
sample: { modifiedNodes: [{ id: 'n1', elementTypeId: 'node:foo' }], dispatchedCommands: 1, warnings: [] }
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: 'modify-edges',
|
|
125
|
+
Constructor: ModifyEdgesMcpToolHandler,
|
|
126
|
+
schema: ModifyEdgesOutputSchema,
|
|
127
|
+
sample: { modifiedEdges: [{ id: 'e1', elementTypeId: 'edge' }], dispatchedCommands: 1, errors: [] }
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'delete-elements',
|
|
131
|
+
Constructor: DeleteElementsMcpToolHandler,
|
|
132
|
+
schema: DeleteElementsOutputSchema,
|
|
133
|
+
sample: { deletedElements: [{ id: 'n1', elementTypeId: 'node:foo' }], deletedCount: 3 }
|
|
134
|
+
},
|
|
135
|
+
{ name: 'undo', Constructor: UndoMcpToolHandler, schema: UndoOutputSchema, sample: { commandsUndone: 2 } },
|
|
136
|
+
{ name: 'redo', Constructor: RedoMcpToolHandler, schema: RedoOutputSchema, sample: { commandsRedone: 2 } },
|
|
137
|
+
{ name: 'layout', Constructor: LayoutMcpToolHandler, schema: LayoutOutputSchema, sample: { applied: true } },
|
|
138
|
+
{
|
|
139
|
+
name: 'validate-diagram',
|
|
140
|
+
Constructor: ValidateDiagramMcpToolHandler,
|
|
141
|
+
schema: ValidateDiagramOutputSchema,
|
|
142
|
+
sample: { markers: [{ kind: 'error', label: 'bad', description: 'bad bad', elementId: 'n1' }] }
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'get-selection',
|
|
146
|
+
Constructor: GetSelectionMcpToolHandler,
|
|
147
|
+
schema: GetSelectionOutputSchema,
|
|
148
|
+
sample: { selectedElementIds: ['n1', 'n2'] }
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
name: 'query-elements',
|
|
152
|
+
Constructor: QueryElementsMcpToolHandler,
|
|
153
|
+
schema: QueryElementsOutputSchema,
|
|
154
|
+
sample: { mode: 'inspect', elements: [{ id: 'n1', type: 'node:foo', position: { x: 0, y: 0 } }] }
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: 'save-model',
|
|
158
|
+
Constructor: SaveModelMcpToolHandler,
|
|
159
|
+
schema: SaveModelOutputSchema,
|
|
160
|
+
sample: { saved: true, fileUri: 'file:///a' }
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: 'set-view',
|
|
164
|
+
Constructor: SetViewMcpToolHandler,
|
|
165
|
+
schema: SetViewOutputSchema,
|
|
166
|
+
sample: { action: 'fit-to-screen', targetIds: ['n1'] }
|
|
167
|
+
}
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
describe('Tool output schemas · per-handler matrix', () => {
|
|
171
|
+
matrix.forEach(({ name, Constructor, schema, sample }) => {
|
|
172
|
+
describe(name, () => {
|
|
173
|
+
it('declares outputSchema referencing the exported schema constant', () => {
|
|
174
|
+
expect(new Constructor().outputSchema).to.equal(schema);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('schema accepts a representative structured payload', () => {
|
|
178
|
+
expect(() => schema.parse(sample)).to.not.throw();
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
});
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2025-2026 EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
* http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
*
|
|
8
|
+
* This Source Code may also be made available under the following Secondary
|
|
9
|
+
* Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
* with the GNU Classpath Exception which is available at
|
|
12
|
+
* https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
*
|
|
14
|
+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
|
|
15
|
+
********************************************************************************/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
ActionDispatcher,
|
|
19
|
+
ClientId,
|
|
20
|
+
GModelElement,
|
|
21
|
+
Logger,
|
|
22
|
+
MaybePromise,
|
|
23
|
+
ModelState,
|
|
24
|
+
RequestAction,
|
|
25
|
+
ResponseAction
|
|
26
|
+
} from '@eclipse-glsp/server';
|
|
27
|
+
import { ToolAnnotations } from '@modelcontextprotocol/sdk/types';
|
|
28
|
+
import { inject, injectable, interfaces } from 'inversify';
|
|
29
|
+
import { ZodObject, ZodRawShape } from 'zod/v4';
|
|
30
|
+
import { GLSPMcpServer } from './glsp-mcp-server';
|
|
31
|
+
import {
|
|
32
|
+
McpElementsNotFoundError,
|
|
33
|
+
McpReadOnlyError,
|
|
34
|
+
McpStructuredContent,
|
|
35
|
+
McpToolError,
|
|
36
|
+
McpToolErrorCode,
|
|
37
|
+
McpToolErrorCodes,
|
|
38
|
+
McpToolResult,
|
|
39
|
+
errorCodeFor,
|
|
40
|
+
extractErrorMessage,
|
|
41
|
+
requestActionOrFail,
|
|
42
|
+
toolErrorResult
|
|
43
|
+
} from './mcp-handler-shared';
|
|
44
|
+
import { McpIdAliasService } from './mcp-id-alias-service';
|
|
45
|
+
import { ElementIdentity, McpDiagramScopedInput } from './mcp-input-schemas';
|
|
46
|
+
import { McpLabelProvider } from './mcp-label-provider';
|
|
47
|
+
import { mcpRequestContext } from './mcp-request-context';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Multi-binding key for **server-scope** tool handlers — singletons that don't target a
|
|
51
|
+
* specific GLSP client session. Adopters extend {@link AbstractMcpToolHandler} and bind their
|
|
52
|
+
* subclass against this symbol; the launcher invokes `registerTool(server)` on each at
|
|
53
|
+
* MCP-session-init.
|
|
54
|
+
*
|
|
55
|
+
* Diagram-scope tools (one instance per open diagram) use the separate
|
|
56
|
+
* {@link McpDiagramToolHandlerConstructor} multi-binding instead — see `mcp-session.ts` for
|
|
57
|
+
* the MCP-session vs GLSP-session terminology.
|
|
58
|
+
*
|
|
59
|
+
* @experimental
|
|
60
|
+
*/
|
|
61
|
+
export interface McpToolHandler {
|
|
62
|
+
registerTool(server: GLSPMcpServer): void;
|
|
63
|
+
}
|
|
64
|
+
export const McpToolHandler = Symbol('McpToolHandler');
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Shared infrastructure for both server- and diagram-scope tool handlers — exported so adopters
|
|
68
|
+
* who want to factor common helpers across server-scope and diagram-scope tools can extend a
|
|
69
|
+
* single ancestor instead of duplicating logic. Most adopters extend the more specific siblings
|
|
70
|
+
* ({@link AbstractMcpToolHandler}, {@link AbstractMcpDiagramToolHandler},
|
|
71
|
+
* {@link OperationMcpDiagramToolHandler}); reach for this base only when a helper is genuinely
|
|
72
|
+
* scope-agnostic.
|
|
73
|
+
*
|
|
74
|
+
* @experimental
|
|
75
|
+
*/
|
|
76
|
+
@injectable()
|
|
77
|
+
export abstract class BaseMcpToolHandler {
|
|
78
|
+
@inject(Logger) protected logger: Logger;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Tool identifier exposed to the MCP client. Also used to reference this tool from other
|
|
82
|
+
* handlers' prompt or description text — wire via `static readonly NAME = '…'` and
|
|
83
|
+
* `readonly name = ClassName.NAME` so those cross-references survive renames.
|
|
84
|
+
*/
|
|
85
|
+
abstract readonly name: string;
|
|
86
|
+
/** LLM-facing explanation surfaced in the tool catalog — clients pass this verbatim to the model. Keep concise and behavioral. */
|
|
87
|
+
abstract readonly description: string;
|
|
88
|
+
/** Adopter writes `z.object({ ... })`; the base passes `.shape` to the SDK. */
|
|
89
|
+
abstract readonly inputSchema: ZodObject<ZodRawShape>;
|
|
90
|
+
/**
|
|
91
|
+
* Optional dual-emit schema. When set, pass the matching `structured` payload to
|
|
92
|
+
* {@link success} so the framework forwards it as `structuredContent` alongside the
|
|
93
|
+
* human-readable text. The MCP spec says clients SHOULD validate `structuredContent`
|
|
94
|
+
* against the declared schema, so the two MUST stay in sync.
|
|
95
|
+
*/
|
|
96
|
+
readonly outputSchema?: ZodObject<ZodRawShape>;
|
|
97
|
+
/** Optional human-friendly display name for UIs that render a friendlier label than `name`. */
|
|
98
|
+
readonly title?: string;
|
|
99
|
+
|
|
100
|
+
// ─── Tool annotations (MCP spec: server/tools) ───────────────────────────────
|
|
101
|
+
// Surfaced as flat fields rather than a nested `annotations` object so adopters can
|
|
102
|
+
// override one hint with a one-line `override readonly destructiveHint = true;` instead of
|
|
103
|
+
// re-declaring the whole quartet. **Untrusted** unless from a trusted server — clients
|
|
104
|
+
// MUST treat these as advisory.
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Tool does not modify its environment. Defaults to `true` here on the common base;
|
|
108
|
+
* overridden to `false` on {@link OperationMcpDiagramToolHandler} since write-style tools
|
|
109
|
+
* dispatch model-mutating operations.
|
|
110
|
+
*/
|
|
111
|
+
readonly readOnlyHint: boolean = true;
|
|
112
|
+
/**
|
|
113
|
+
* Tool may perform destructive *updates* (irreversible deletion, data loss). Only meaningful
|
|
114
|
+
* when `readOnlyHint: false`. Set explicitly on the concrete handler when it applies (e.g.
|
|
115
|
+
* `delete-elements`).
|
|
116
|
+
*/
|
|
117
|
+
readonly destructiveHint?: boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Repeated calls with the same arguments have no additional effect. Only meaningful when
|
|
120
|
+
* `readOnlyHint: false`.
|
|
121
|
+
*/
|
|
122
|
+
readonly idempotentHint?: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* Tool interacts with an "open world" of external entities (web search, external APIs).
|
|
125
|
+
* Default in this codebase: `false` — diagram ops are bounded to the GLSP client. Set
|
|
126
|
+
* `true` for tools that reach off-process.
|
|
127
|
+
*/
|
|
128
|
+
readonly openWorldHint: boolean = false;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* SDK-facing registration config; consumed by both the server-scope `registerTool` flow and
|
|
132
|
+
* the launcher's diagram-scope dispatcher. Assembles the {@link ToolAnnotations} object from
|
|
133
|
+
* the flat-field surface so adopters compose annotations via field overrides, not by
|
|
134
|
+
* redeclaring the whole annotations literal.
|
|
135
|
+
*/
|
|
136
|
+
toRegistrationConfig(): {
|
|
137
|
+
title?: string;
|
|
138
|
+
description: string;
|
|
139
|
+
inputSchema: ZodObject<ZodRawShape>;
|
|
140
|
+
outputSchema?: ZodObject<ZodRawShape>;
|
|
141
|
+
annotations: ToolAnnotations;
|
|
142
|
+
} {
|
|
143
|
+
// `.strict()` rejects unknown keys at the SDK boundary instead of silently stripping them
|
|
144
|
+
// — turns an LLM mis-typed field name into a self-correctable JSON-RPC validation error
|
|
145
|
+
// rather than a misleading "no-op success".
|
|
146
|
+
//
|
|
147
|
+
// The `as unknown as ZodObject<ZodRawShape>` bridge is needed because `.strict()` returns
|
|
148
|
+
// a `ZodObject<…, $strict>` whose `$strict` marker generic is not assignable to the
|
|
149
|
+
// shape-only `ZodObject<ZodRawShape>` expected here. The SDK accepts both shapes
|
|
150
|
+
// structurally, so the cast is type-only — no runtime mismatch.
|
|
151
|
+
return {
|
|
152
|
+
title: this.title,
|
|
153
|
+
description: this.description,
|
|
154
|
+
inputSchema: this.inputSchema.strict() as unknown as ZodObject<ZodRawShape>,
|
|
155
|
+
outputSchema: this.outputSchema as ZodObject<ZodRawShape> | undefined,
|
|
156
|
+
annotations: {
|
|
157
|
+
readOnlyHint: this.readOnlyHint,
|
|
158
|
+
destructiveHint: this.destructiveHint,
|
|
159
|
+
idempotentHint: this.idempotentHint,
|
|
160
|
+
openWorldHint: this.openWorldHint
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
protected error(message: string, code?: McpToolErrorCode): McpToolResult {
|
|
166
|
+
return toolErrorResult(message, code);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Convention: `message` is a short, referenceable summary (ids, types, counts); `structured`
|
|
171
|
+
* carries the full payload. The two complement rather than duplicate — balancing token usage
|
|
172
|
+
* across the heterogeneous client landscape.
|
|
173
|
+
*
|
|
174
|
+
* The MCP spec (2025-06-18) recommends mirroring `structuredContent` into a TextContent
|
|
175
|
+
* block, but in-flight discussion is softening that — `content` and `structuredContent` are
|
|
176
|
+
* increasingly model-oriented vs. machine-oriented surfaces that should be semantically
|
|
177
|
+
* equivalent, not byte-identical.
|
|
178
|
+
*
|
|
179
|
+
* Client behavior is uneven, so summary-in-content + payload-in-structured is a deliberate
|
|
180
|
+
* hedge: some clients only forward `structuredContent`, some only `content`, some forward
|
|
181
|
+
* both verbatim and double the per-call context budget.
|
|
182
|
+
*
|
|
183
|
+
* Pass `structured` whenever {@link outputSchema} is declared (the spec says clients SHOULD
|
|
184
|
+
* validate against the declared shape). Omit for plain text-only responses.
|
|
185
|
+
*/
|
|
186
|
+
protected success(message: string, structured?: McpStructuredContent): McpToolResult {
|
|
187
|
+
return { isError: false, content: [{ type: 'text', text: message }], structuredContent: structured };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Catches `McpToolError` (→ `isError: true` result) and unexpected errors; tags known errors via {@link errorCodeFor}. */
|
|
191
|
+
protected async execute(producer: () => MaybePromise<McpToolResult>): Promise<McpToolResult> {
|
|
192
|
+
try {
|
|
193
|
+
return await producer();
|
|
194
|
+
} catch (err: unknown) {
|
|
195
|
+
const code = errorCodeFor(err);
|
|
196
|
+
if (err instanceof McpToolError) {
|
|
197
|
+
return this.error(err.message, code);
|
|
198
|
+
}
|
|
199
|
+
const message = extractErrorMessage(err);
|
|
200
|
+
// Session-disposed races aren't programming errors — log at warn so they don't drown the error feed.
|
|
201
|
+
if (code === McpToolErrorCodes.SessionDisposed) {
|
|
202
|
+
this.logger.warn(`Session disposed mid-call in tool '${this.name}': ${message}`);
|
|
203
|
+
} else {
|
|
204
|
+
this.logger.error(`Unexpected error in tool '${this.name}': ${message}`, err);
|
|
205
|
+
}
|
|
206
|
+
return this.error(message, code);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Server-scope tool base — for tools that don't target a specific GLSP client session
|
|
213
|
+
* (e.g., listing all sessions). Bound under {@link McpToolHandler} as a server-scope singleton;
|
|
214
|
+
* the launcher invokes `registerTool(server)` once per MCP session.
|
|
215
|
+
*
|
|
216
|
+
* @experimental
|
|
217
|
+
*/
|
|
218
|
+
@injectable()
|
|
219
|
+
export abstract class AbstractMcpToolHandler<T = Record<string, unknown>> extends BaseMcpToolHandler implements McpToolHandler {
|
|
220
|
+
/** Throw {@link McpToolError} for expected, user-facing errors; the base wraps. */
|
|
221
|
+
protected abstract createResult(params: T): MaybePromise<McpToolResult>;
|
|
222
|
+
|
|
223
|
+
registerTool(server: GLSPMcpServer): void {
|
|
224
|
+
server.registerTool(this.name, this.toRegistrationConfig(), async (params, extra) =>
|
|
225
|
+
mcpRequestContext.run(extra, () => this.execute(() => this.createResult(params as T)))
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Shared per-session implementation for diagram-scope tool handlers. Adopters don't extend this
|
|
232
|
+
* directly — extend {@link AbstractMcpDiagramToolHandler} (read) or {@link OperationMcpDiagramToolHandler}
|
|
233
|
+
* (write). Exported only as the upper bound for the {@link McpDiagramToolHandlerConstructor}
|
|
234
|
+
* multi-binding.
|
|
235
|
+
*
|
|
236
|
+
* `@inject(...)` fields resolve against the live `ClientSession.container` because instances are
|
|
237
|
+
* created at GLSP-session-open by `McpDiagramToolHandlerRegistryInitializer`.
|
|
238
|
+
*
|
|
239
|
+
* Read-side handlers access `modelState.index` directly. Synchronous reads are atomic, but a
|
|
240
|
+
* concurrent tool that awaits `actionDispatcher.dispatch(...)` may expose a half-committed
|
|
241
|
+
* model — write-then-read tool sequences must serialize the write before the read.
|
|
242
|
+
*
|
|
243
|
+
* @experimental
|
|
244
|
+
*/
|
|
245
|
+
@injectable()
|
|
246
|
+
export abstract class BaseMcpDiagramToolHandler<T extends McpDiagramScopedInput = McpDiagramScopedInput> extends BaseMcpToolHandler {
|
|
247
|
+
@inject(ClientId) protected clientId: string;
|
|
248
|
+
@inject(ModelState) protected modelState: ModelState;
|
|
249
|
+
@inject(McpIdAliasService) protected aliasService: McpIdAliasService;
|
|
250
|
+
@inject(McpLabelProvider) protected labelProvider: McpLabelProvider;
|
|
251
|
+
|
|
252
|
+
/** Throw {@link McpToolError} for expected errors; the base wraps. */
|
|
253
|
+
protected abstract createResult(params: T): MaybePromise<McpToolResult>;
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Public dispatch entry point invoked by {@link McpServerLauncher}'s registered SDK
|
|
257
|
+
* callback. Each sibling sets its own policy — {@link AbstractMcpDiagramToolHandler}
|
|
258
|
+
* passes through; {@link OperationMcpDiagramToolHandler} enforces the readonly gate.
|
|
259
|
+
* Adopters don't call this directly.
|
|
260
|
+
*/
|
|
261
|
+
abstract handle(params: T): Promise<McpToolResult>;
|
|
262
|
+
|
|
263
|
+
/** Override to opt out of registration when a runtime dependency is missing. Default: `true`. */
|
|
264
|
+
canRegister(): boolean {
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** Translates (alias-or-real) ids to real ids; partitions ids absent from the model into `missingIds`. */
|
|
269
|
+
protected resolveIds(ids: string[]): { realIds: string[]; missingIds: string[] } {
|
|
270
|
+
const realIds: string[] = [];
|
|
271
|
+
const missingIds: string[] = [];
|
|
272
|
+
for (const id of ids) {
|
|
273
|
+
const realId = this.aliasService.lookup(id);
|
|
274
|
+
if (this.modelState.index.find(realId)) {
|
|
275
|
+
realIds.push(realId);
|
|
276
|
+
} else {
|
|
277
|
+
missingIds.push(id);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return { realIds, missingIds };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** Like {@link resolveIds} but throws {@link McpElementsNotFoundError} when any input id is absent. */
|
|
284
|
+
protected resolveExistingIds(ids: string[] | undefined): string[] {
|
|
285
|
+
if (!ids || ids.length === 0) {
|
|
286
|
+
return [];
|
|
287
|
+
}
|
|
288
|
+
const { realIds, missingIds } = this.resolveIds(ids);
|
|
289
|
+
if (missingIds.length > 0) {
|
|
290
|
+
throw new McpElementsNotFoundError(missingIds);
|
|
291
|
+
}
|
|
292
|
+
return realIds;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Resolves a list of structured inputs against the model: each input's id is looked up via the
|
|
297
|
+
* alias service and the resolved element is retrieved from the index. Throws
|
|
298
|
+
* {@link McpElementsNotFoundError} if any input's id is absent. The returned tuples preserve
|
|
299
|
+
* caller's input/element pairing for downstream processing (e.g. type-checks per kind).
|
|
300
|
+
*/
|
|
301
|
+
protected lookupElements<I>(inputs: I[], extractId: (input: I) => string): Array<[I, GModelElement]> {
|
|
302
|
+
const found: Array<[I, GModelElement]> = [];
|
|
303
|
+
const missing: string[] = [];
|
|
304
|
+
for (const input of inputs) {
|
|
305
|
+
const inputId = extractId(input);
|
|
306
|
+
const realId = this.aliasService.lookup(inputId);
|
|
307
|
+
const element = this.modelState.index.find(realId);
|
|
308
|
+
if (element) {
|
|
309
|
+
found.push([input, element]);
|
|
310
|
+
} else {
|
|
311
|
+
missing.push(inputId);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (missing.length > 0) {
|
|
315
|
+
throw new McpElementsNotFoundError(missing);
|
|
316
|
+
}
|
|
317
|
+
return found;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** Encodes real ids to alias ids (passthrough when {@link NullMcpIdAliasService} is bound). */
|
|
321
|
+
protected encodeIds(ids: string[]): string[] {
|
|
322
|
+
return ids.map(id => this.aliasService.alias(id));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Compact identity for an element — `{ id, elementTypeId, label? }`. Mutating tools (create
|
|
327
|
+
* / modify / delete) echo this so the LLM can refer to the element by label or type in
|
|
328
|
+
* user-facing prose without a follow-up `query-elements` call. Returns `undefined` when
|
|
329
|
+
* the element is no longer in the model (e.g. just deleted).
|
|
330
|
+
*/
|
|
331
|
+
protected describeElement(aliasOrRealId: string): ElementIdentity | undefined {
|
|
332
|
+
const realId = this.aliasService.lookup(aliasOrRealId);
|
|
333
|
+
const element = this.modelState.index.find(realId);
|
|
334
|
+
return element ? this.describeResolvedElement(element) : undefined;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** Describe an already-resolved {@link GModelElement} — skips the model-lookup round-trip. */
|
|
338
|
+
protected describeResolvedElement(element: GModelElement): ElementIdentity {
|
|
339
|
+
const label = this.labelProvider.getLabel(element)?.text;
|
|
340
|
+
// Conditional spread keeps `label` absent (not `undefined`) when the element has none —
|
|
341
|
+
// matches Zod's `.optional()` semantics so the structured payload is clean.
|
|
342
|
+
return { id: this.aliasService.alias(element.id), elementTypeId: element.type, ...(label !== undefined ? { label } : {}) };
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Diagram-scope tool base for **query-style** tools that read the model without mutating it
|
|
348
|
+
* (validate, get-selection, set-view, query-elements). Sibling of
|
|
349
|
+
* {@link OperationMcpDiagramToolHandler} — extend that one when the tool dispatches a
|
|
350
|
+
* model-mutating Operation.
|
|
351
|
+
*
|
|
352
|
+
* @experimental
|
|
353
|
+
*/
|
|
354
|
+
@injectable()
|
|
355
|
+
export abstract class AbstractMcpDiagramToolHandler<
|
|
356
|
+
T extends McpDiagramScopedInput = McpDiagramScopedInput
|
|
357
|
+
> extends BaseMcpDiagramToolHandler<T> {
|
|
358
|
+
handle(params: T): Promise<McpToolResult> {
|
|
359
|
+
return this.execute(() => this.createResult(params));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Diagram-scope tool base for **operation-style** tools that mutate the model by dispatching a
|
|
365
|
+
* GLSP `Operation` (or other model-mutating `Action` like `UndoAction` / `RedoAction`) on
|
|
366
|
+
* behalf of the LLM — create, modify, delete, undo, redo. Sibling of
|
|
367
|
+
* {@link AbstractMcpDiagramToolHandler}.
|
|
368
|
+
*
|
|
369
|
+
* The base bakes in two pieces beyond the read sibling:
|
|
370
|
+
* - `@inject(ActionDispatcher)`. Every adopter extending this base dispatches, so the
|
|
371
|
+
* dispatcher belongs on the base, not in per-handler boilerplate.
|
|
372
|
+
* - Throws {@link McpReadOnlyError} when `modelState.isReadonly`, surfacing a hard failure
|
|
373
|
+
* to the LLM. The MCP-side gate is necessary even though core's `OperationActionHandler`
|
|
374
|
+
* checks readonly itself: core's gate is a *soft* warning (returns a `MessageAction`, the
|
|
375
|
+
* dispatch resolves successfully and the tool body would otherwise report success while
|
|
376
|
+
* nothing changed), and `UndoRedoActionHandler` doesn't gate readonly at all.
|
|
377
|
+
*
|
|
378
|
+
* Parallels core's `OperationHandler` (sibling to `ActionHandler`, not a refinement) — but
|
|
379
|
+
* note the role flip: core's `OperationHandler` is downstream of dispatch and only needs
|
|
380
|
+
* `ModelState`; ours is upstream of dispatch (the LLM-side handler that triggers the Operation)
|
|
381
|
+
* and so additionally injects `ActionDispatcher`.
|
|
382
|
+
*
|
|
383
|
+
* @experimental
|
|
384
|
+
*/
|
|
385
|
+
@injectable()
|
|
386
|
+
export abstract class OperationMcpDiagramToolHandler<
|
|
387
|
+
T extends McpDiagramScopedInput = McpDiagramScopedInput
|
|
388
|
+
> extends BaseMcpDiagramToolHandler<T> {
|
|
389
|
+
@inject(ActionDispatcher) protected actionDispatcher: ActionDispatcher;
|
|
390
|
+
|
|
391
|
+
// Operation tools mutate the model — flip the read defaults. Concrete handlers override
|
|
392
|
+
// `destructiveHint` / `idempotentHint` (one line each) where it applies; the explicit `false`
|
|
393
|
+
// defaults below override the MCP spec's "true if unset" semantics so non-destructive,
|
|
394
|
+
// non-idempotent writes don't trigger overzealous client-side confirmation prompts.
|
|
395
|
+
override readonly readOnlyHint = false;
|
|
396
|
+
override readonly destructiveHint: boolean = false;
|
|
397
|
+
override readonly idempotentHint: boolean = false;
|
|
398
|
+
|
|
399
|
+
handle(params: T): Promise<McpToolResult> {
|
|
400
|
+
return this.execute(() => {
|
|
401
|
+
if (this.modelState.isReadonly) {
|
|
402
|
+
throw new McpReadOnlyError();
|
|
403
|
+
}
|
|
404
|
+
return this.createResult(params);
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Convenience for tools that initiate a `RequestAction` round-trip (rather than the
|
|
410
|
+
* fire-and-forget operation dispatch this base is named for) — wraps
|
|
411
|
+
* {@link requestActionOrFail} with `this.actionDispatcher` and a default label of
|
|
412
|
+
* `this.name`. Pass an explicit label only when the tool handles multiple distinct
|
|
413
|
+
* round-trips and wants to disambiguate them in error messages.
|
|
414
|
+
*/
|
|
415
|
+
protected requestAction<R extends ResponseAction>(request: RequestAction<R>, timeoutMs: number, label: string = this.name): Promise<R> {
|
|
416
|
+
return requestActionOrFail(this.actionDispatcher, request, timeoutMs, label);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Multi-binding identifier for diagram-scope tool handler constructors — covers both
|
|
422
|
+
* {@link AbstractMcpDiagramToolHandler} and {@link OperationMcpDiagramToolHandler}. Bound via
|
|
423
|
+
* `AbstractMcpDiagramModule.configureToolHandlers`; the per-GLSP-session registry initializer
|
|
424
|
+
* reads the list at session-open and resolves each constructor against the session container.
|
|
425
|
+
*
|
|
426
|
+
* Mirrors core's `OperationHandlerConstructor` pattern: instance fields (`readonly name = '…'`)
|
|
427
|
+
* are read off `new Constructor()` at MCP-session-init for SDK catalog registration, the same
|
|
428
|
+
* trick `bindOperations` uses to read `operationType`.
|
|
429
|
+
*/
|
|
430
|
+
export type McpDiagramToolHandlerConstructor = interfaces.Newable<BaseMcpDiagramToolHandler<any>>;
|
|
431
|
+
export const McpDiagramToolHandlerConstructor = Symbol('McpDiagramToolHandlerConstructor');
|