drawio-mcp-server 2.0.3 → 2.1.0
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/README.md +29 -3
- package/build/assets/downloader.js +16 -17
- package/build/config.js +253 -5
- package/build/config.test.js +449 -1
- package/build/emitter_bus.js +3 -0
- package/build/emitter_bus.test.js +7 -0
- package/build/index.capabilities.test.js +45 -0
- package/build/index.js +496 -106
- package/build/install-desktop-plugin.js +56 -0
- package/build/install-desktop-plugin.test.js +66 -0
- package/build/multi-transport.test.js +277 -0
- package/build/plugin/mcp-plugin.js +2180 -1105
- package/build/prefetch-assets.js +6 -5
- package/build/real-environment/document-targeting.test.js +124 -0
- package/build/real-environment/export-diagram.test.js +473 -0
- package/build/real-environment/harness.js +120 -32
- package/build/real-environment/import-mermaid.test.js +81 -0
- package/build/real-environment/pages-and-concurrency.test.js +448 -0
- package/build/real-environment/shapes.test.js +58 -0
- package/build/real-environment/tools.js +51 -6
- package/build/register-tool.js +31 -0
- package/build/request_queue.js +29 -0
- package/build/request_queue.test.js +98 -0
- package/build/stdio-transport-purity.test.js +88 -0
- package/build/strip-schema.js +61 -0
- package/build/tls/expiry.js +14 -0
- package/build/tls/expiry.test.js +54 -0
- package/build/tls/generate.js +123 -0
- package/build/tls/generate.test.js +115 -0
- package/build/tls/index.js +80 -0
- package/build/tls/index.test.js +141 -0
- package/build/tls/install-hint.js +45 -0
- package/build/tls/install-hint.test.js +32 -0
- package/build/tls/load.js +18 -0
- package/build/tls/load.test.js +40 -0
- package/build/tls/paths.js +30 -0
- package/build/tls/paths.test.js +53 -0
- package/build/tls/san.js +27 -0
- package/build/tls/san.test.js +72 -0
- package/build/tool-registry.test.js +823 -0
- package/build/tool.js +74 -15
- package/build/tool.test.js +250 -7
- package/build/tools/add-cell-of-shape.js +4 -2
- package/build/tools/add-edge.js +3 -1
- package/build/tools/add-rectangle.js +4 -2
- package/build/tools/copy-page.js +13 -0
- package/build/tools/create-layer.js +4 -2
- package/build/tools/create-page.js +8 -0
- package/build/tools/delete-cell-by-id.js +4 -2
- package/build/tools/edit-cell.js +3 -1
- package/build/tools/edit-edge.js +3 -1
- package/build/tools/export-diagram.js +7 -3
- package/build/tools/get-active-layer.js +4 -1
- package/build/tools/get-current-page.js +5 -0
- package/build/tools/get-selected-cell.js +4 -1
- package/build/tools/import-diagram.js +12 -2
- package/build/tools/import-mermaid.js +31 -0
- package/build/tools/index.js +14 -0
- package/build/tools/list-documents.js +17 -0
- package/build/tools/list-layers.js +4 -1
- package/build/tools/list-paged-model.js +4 -3
- package/build/tools/list-pages.js +5 -0
- package/build/tools/move-cell-to-layer.js +3 -1
- package/build/tools/rename-page.js +10 -0
- package/build/tools/set-active-layer.js +4 -2
- package/build/tools/set-cell-data.js +3 -1
- package/build/tools/set-cell-parent.js +3 -1
- package/build/tools/set-cell-shape.js +3 -1
- package/build/tools/shared.js +35 -0
- package/build/tools/shared.test.js +29 -0
- package/package.json +22 -14
package/README.md
CHANGED
|
@@ -9,11 +9,21 @@ Let's do some Vibe Diagramming with the most wide-spread diagramming tool called
|
|
|
9
9
|
|
|
10
10
|
## Key Highlights
|
|
11
11
|
|
|
12
|
+
- Enable Draw.io MCP in IFrames 
|
|
13
|
+
- AWS, GCP, Azure, Cisco19, and CiscoSafe stencils auto-discovered at runtime from drawio's sidebar 
|
|
14
|
+
- Multi-document targeting with `list-documents` and `target_document` selectors for multi-tab workflows 
|
|
15
|
+
- Multi-page targeting with required `target_page` selectors for page-scoped tools 
|
|
16
|
+
- Per-document FIFO serialization for live operations, so multiple agents can work on different files safely 
|
|
17
|
+
- Page management tools: `list-pages`, `get-current-page`, `create-page`, `copy-page`, `rename-page` 
|
|
18
|
+
- Import, embed, or expand [Mermaid](https://mermaid.js.org/) diagrams 
|
|
19
|
+
- Firefox support is back, TLS mode is necessary 
|
|
20
|
+
- Server supports TLS mode and optionally generates self-signed certificates 
|
|
21
|
+
|
|
12
22
|
- Import and export diagrams from/to XML, SVG (with embedded XML), or PNG (with embedded XML) 
|
|
13
23
|
- Edge geometry control with waypoints and automatic self-connector routing 
|
|
14
24
|
- Parent-child relationships for nested shapes and grouping 
|
|
15
25
|
- Unified Server and Extension in the same mono-repo 
|
|
16
|
-
- Built-in Draw.io editor - no browser extension required
|
|
26
|
+
- Built-in Draw.io editor - no browser extension required
|
|
17
27
|
- MCP server that lets AI agents control Draw.io diagrams
|
|
18
28
|
- Programmatic diagram creation, inspection, and modification via MCP tools
|
|
19
29
|
- Layer management for complex diagrams
|
|
@@ -27,6 +37,8 @@ Two ways to use:
|
|
|
27
37
|
1. **Built-in editor** - Server hosts Draw.io directly, accessible in your browser
|
|
28
38
|
2. **Browser extension** - Connect to Draw.io running in your browser via extension
|
|
29
39
|
|
|
40
|
+
Experimental: integration with the **draw.io desktop (Electron) app** is in progress but currently blocked by an upstream CSP issue — see [DESKTOP.md](./DESKTOP.md).
|
|
41
|
+
|
|
30
42
|
## Requirements
|
|
31
43
|
|
|
32
44
|
- **Node.js** (v20 or higher) - Runtime environment for the MCP server
|
|
@@ -134,6 +146,8 @@ Example prompts you can try:
|
|
|
134
146
|
|
|
135
147
|
> "Create an event-driven architecture diagram showing a message queue with producers, consumers, and three backend services"
|
|
136
148
|
|
|
149
|
+
> "Create a three-page event-driven architecture diagram. Use three agents in parallel for service topology, message flow, and retry/failure handling, with each agent assigned to a separate target page."
|
|
150
|
+
|
|
137
151
|
> "Draw a CRUD API diagram with a database, API gateway, and four microservices with their endpoints"
|
|
138
152
|
|
|
139
153
|
> "Add a new layer called 'Background' and move all decorative elements to it, then create a new layer for annotations"
|
|
@@ -144,9 +158,13 @@ Your AI assistant can now control the diagram using MCP tools.
|
|
|
144
158
|
|
|
145
159
|
The server provides MCP tools for:
|
|
146
160
|
|
|
147
|
-
- **
|
|
148
|
-
- **Diagram
|
|
161
|
+
- **Document discovery** - list connected Draw.io document instances and route later calls to a specific tab/file instance
|
|
162
|
+
- **Diagram inspection** - read shapes, pages, layers, and cell properties
|
|
163
|
+
- **Diagram modification** - add/edit/delete shapes, edges, and labels on a target page
|
|
164
|
+
- **Page management** - list pages, inspect the current page, create pages, copy pages, and rename pages without forcing a visible page switch on supported runtimes
|
|
149
165
|
- **Layer management** - create, switch, and organize layers
|
|
166
|
+
- **Vendor shape coverage** - AWS, GCP, Azure, Cisco19, and CiscoSafe stencils auto-discovered at runtime from drawio's sidebar, so agents can place icons like `mxgraph.gcp2.cloud_run` or `mxgraph.cisco19.router` without hand-curated catalogs
|
|
167
|
+
- **Built-in TLS** — opt-in HTTPS + WSS with manual cert/key or auto-generated self-signed material via a per-user local CA. See [CONFIG.md → TLS](./CONFIG.md#tls-https--wss).
|
|
150
168
|
|
|
151
169
|
See [Tools Reference](./TOOLS.md) for the complete list of available tools.
|
|
152
170
|
|
|
@@ -179,6 +197,12 @@ Configuration without `--editor`:
|
|
|
179
197
|
|
|
180
198
|
See the [extension documentation](./packages/drawio-mcp-extension/README.md) for more details.
|
|
181
199
|
|
|
200
|
+
## Experimental: Draw.io Desktop
|
|
201
|
+
|
|
202
|
+
Integration with the [draw.io desktop](https://github.com/jgraph/drawio-desktop) (Electron) app is **experimental** and currently blocked end-to-end by an upstream CSP issue. The plugin loads inside draw.io desktop, but its WebSocket connection back to the MCP server is rejected by draw.io's hard-coded `connect-src 'self'` policy.
|
|
203
|
+
|
|
204
|
+
See [DESKTOP.md](./DESKTOP.md) for the full setup steps and the current limitation.
|
|
205
|
+
|
|
182
206
|
## Related Resources
|
|
183
207
|
|
|
184
208
|
[Configuration](./CONFIG.md) - CLI flags and advanced options
|
|
@@ -195,6 +219,8 @@ See the [extension documentation](./packages/drawio-mcp-extension/README.md) for
|
|
|
195
219
|
|
|
196
220
|
[Development](./DEVELOPMENT.md)
|
|
197
221
|
|
|
222
|
+
[Draw.io Desktop (experimental)](./DESKTOP.md) - install path and known CSP limitation
|
|
223
|
+
|
|
198
224
|
## Star History
|
|
199
225
|
|
|
200
226
|
<a href="https://star-history.com/#lgazo/drawio-mcp-server&Date">
|
|
@@ -43,7 +43,7 @@ export async function extractWar(warPath, extractDir) {
|
|
|
43
43
|
extract.on("error", reject);
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
|
-
export function cleanupExtractedFiles(extractDir) {
|
|
46
|
+
export function cleanupExtractedFiles(extractDir, log) {
|
|
47
47
|
const webappDir = join(extractDir, "webapp");
|
|
48
48
|
const pathsToRemove = [
|
|
49
49
|
join(webappDir, "WEB-INF"),
|
|
@@ -53,44 +53,43 @@ export function cleanupExtractedFiles(extractDir) {
|
|
|
53
53
|
if (existsSync(path)) {
|
|
54
54
|
try {
|
|
55
55
|
rmSync(path, { recursive: true, force: true });
|
|
56
|
-
|
|
56
|
+
log.log("info", `Removed: ${path}`);
|
|
57
57
|
}
|
|
58
58
|
catch (err) {
|
|
59
|
-
|
|
59
|
+
log.log("warning", `Failed to remove ${path}:`, err);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
-
export async function downloadAndExtractAssets(targetDir,
|
|
65
|
-
|
|
66
|
-
progress("Fetching draw.io release info...");
|
|
64
|
+
export async function downloadAndExtractAssets(targetDir, log) {
|
|
65
|
+
log.log("info", "Fetching draw.io release info...");
|
|
67
66
|
const warUrl = await getLatestWarUrl();
|
|
68
67
|
const warPath = join(targetDir, "draw.war");
|
|
69
|
-
|
|
68
|
+
log.log("info", `Downloading draw.war from ${warUrl}...`);
|
|
70
69
|
await downloadFile(warUrl, warPath);
|
|
71
|
-
|
|
70
|
+
log.log("info", "Download complete.");
|
|
72
71
|
const webappDir = join(targetDir, "webapp");
|
|
73
|
-
|
|
72
|
+
log.log("info", "Extracting archive...");
|
|
74
73
|
await extractWar(warPath, webappDir);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
cleanupExtractedFiles(targetDir);
|
|
74
|
+
log.log("info", "Extraction complete.");
|
|
75
|
+
log.log("info", "Cleaning up unnecessary files...");
|
|
76
|
+
cleanupExtractedFiles(targetDir, log);
|
|
78
77
|
// Remove the WAR file
|
|
79
78
|
try {
|
|
80
79
|
rmSync(warPath, { force: true });
|
|
81
80
|
}
|
|
82
81
|
catch (err) {
|
|
83
|
-
|
|
82
|
+
log.log("warning", "Failed to remove WAR file:", err);
|
|
84
83
|
}
|
|
85
|
-
|
|
84
|
+
log.log("info", "Assets ready!");
|
|
86
85
|
}
|
|
87
|
-
export async function ensureAssets(config,
|
|
86
|
+
export async function ensureAssets(config, log) {
|
|
88
87
|
const { getCacheDir, getAssetRoot, assetsExist } = await import("./manager.js");
|
|
89
88
|
const cacheDir = getCacheDir(config.assetPath);
|
|
90
89
|
const assetRoot = getAssetRoot(config);
|
|
91
90
|
if (!assetsExist(config)) {
|
|
92
|
-
|
|
93
|
-
await downloadAndExtractAssets(cacheDir,
|
|
91
|
+
log.log("info", `Assets not found in ${assetRoot}. Downloading...`);
|
|
92
|
+
await downloadAndExtractAssets(cacheDir, log);
|
|
94
93
|
}
|
|
95
94
|
return { assetRoot, isLocal: true };
|
|
96
95
|
}
|
package/build/config.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Application configuration interface
|
|
3
|
-
*/
|
|
1
|
+
import { isIP } from "node:net";
|
|
4
2
|
/**
|
|
5
3
|
* Default configuration values
|
|
6
4
|
*/
|
|
@@ -9,6 +7,9 @@ const DEFAULT_CONFIG = {
|
|
|
9
7
|
httpPort: 3000,
|
|
10
8
|
transports: ["stdio"],
|
|
11
9
|
editorEnabled: false,
|
|
10
|
+
logger: "console",
|
|
11
|
+
tlsEnabled: false,
|
|
12
|
+
tlsAuto: false,
|
|
12
13
|
};
|
|
13
14
|
/**
|
|
14
15
|
* Valid port range
|
|
@@ -49,6 +50,54 @@ export const parseHttpPortValue = (value) => {
|
|
|
49
50
|
}
|
|
50
51
|
return port;
|
|
51
52
|
};
|
|
53
|
+
/**
|
|
54
|
+
* Parse WebSocket URL value - pure function
|
|
55
|
+
* Accepts ws:// or wss:// URLs only
|
|
56
|
+
*/
|
|
57
|
+
export const parseWebSocketUrlValue = (value) => {
|
|
58
|
+
if (!value) {
|
|
59
|
+
return new Error("--websocket-url flag requires a URL");
|
|
60
|
+
}
|
|
61
|
+
let parsed;
|
|
62
|
+
try {
|
|
63
|
+
parsed = new URL(value);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return new Error(`Invalid WebSocket URL "${value}"`);
|
|
67
|
+
}
|
|
68
|
+
if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") {
|
|
69
|
+
return new Error(`Invalid WebSocket URL "${value}". Protocol must be ws:// or wss://`);
|
|
70
|
+
}
|
|
71
|
+
return value;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Parse host value - pure function
|
|
75
|
+
* Accepts IPv4 or IPv6 addresses only (no hostnames)
|
|
76
|
+
*/
|
|
77
|
+
export const parseHostValue = (value) => {
|
|
78
|
+
if (!value) {
|
|
79
|
+
return new Error("--host flag requires an IP address");
|
|
80
|
+
}
|
|
81
|
+
if (isIP(value) === 0) {
|
|
82
|
+
return new Error(`Invalid host "${value}". Must be a valid IPv4 or IPv6 address`);
|
|
83
|
+
}
|
|
84
|
+
return value;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Parse logger mode value - pure function
|
|
88
|
+
* Accepts "console" or "mcp-server". For convenience the legacy underscore
|
|
89
|
+
* form "mcp_server" is normalised to "mcp-server".
|
|
90
|
+
*/
|
|
91
|
+
export const parseLoggerValue = (value) => {
|
|
92
|
+
if (!value) {
|
|
93
|
+
return new Error("--logger flag requires a mode (console|mcp-server)");
|
|
94
|
+
}
|
|
95
|
+
const normalized = value === "mcp_server" ? "mcp-server" : value;
|
|
96
|
+
if (normalized === "console" || normalized === "mcp-server") {
|
|
97
|
+
return normalized;
|
|
98
|
+
}
|
|
99
|
+
return new Error(`Invalid logger "${value}". Must be one of: console, mcp-server`);
|
|
100
|
+
};
|
|
52
101
|
export const parseTransports = (values) => {
|
|
53
102
|
if (!values || values.length === 0) {
|
|
54
103
|
return DEFAULT_CONFIG.transports;
|
|
@@ -103,6 +152,14 @@ export const parseConfig = (args) => {
|
|
|
103
152
|
let transportValues;
|
|
104
153
|
let editorEnabled = false;
|
|
105
154
|
let assetPath;
|
|
155
|
+
let webSocketUrlValue;
|
|
156
|
+
let hostValue;
|
|
157
|
+
let loggerValue;
|
|
158
|
+
let tlsEnabled = false;
|
|
159
|
+
let tlsAuto = false;
|
|
160
|
+
let tlsCert;
|
|
161
|
+
let tlsKey;
|
|
162
|
+
let tlsDir;
|
|
106
163
|
for (let i = 0; i < args.length; i += 1) {
|
|
107
164
|
const arg = args[i];
|
|
108
165
|
if (arg === "--extension-port" || arg === "-p") {
|
|
@@ -154,6 +211,84 @@ export const parseConfig = (args) => {
|
|
|
154
211
|
assetPath = nextValue;
|
|
155
212
|
i += 1;
|
|
156
213
|
}
|
|
214
|
+
else if (arg === "--websocket-url") {
|
|
215
|
+
const nextValue = args[i + 1];
|
|
216
|
+
if (nextValue === undefined) {
|
|
217
|
+
return new Error("--websocket-url flag requires a URL");
|
|
218
|
+
}
|
|
219
|
+
webSocketUrlValue = nextValue;
|
|
220
|
+
i += 1;
|
|
221
|
+
}
|
|
222
|
+
else if (arg === "--host") {
|
|
223
|
+
const nextValue = args[i + 1];
|
|
224
|
+
if (nextValue === undefined) {
|
|
225
|
+
return new Error("--host flag requires an IP address");
|
|
226
|
+
}
|
|
227
|
+
hostValue = nextValue;
|
|
228
|
+
i += 1;
|
|
229
|
+
}
|
|
230
|
+
else if (arg === "--logger") {
|
|
231
|
+
const nextValue = args[i + 1];
|
|
232
|
+
if (nextValue === undefined) {
|
|
233
|
+
return new Error("--logger flag requires a mode (console|mcp-server)");
|
|
234
|
+
}
|
|
235
|
+
loggerValue = nextValue;
|
|
236
|
+
i += 1;
|
|
237
|
+
}
|
|
238
|
+
else if (arg === "--tls") {
|
|
239
|
+
tlsEnabled = true;
|
|
240
|
+
}
|
|
241
|
+
else if (arg === "--tls-auto") {
|
|
242
|
+
tlsAuto = true;
|
|
243
|
+
}
|
|
244
|
+
else if (arg === "--tls-cert") {
|
|
245
|
+
const nextValue = args[i + 1];
|
|
246
|
+
if (nextValue === undefined) {
|
|
247
|
+
return new Error("--tls-cert flag requires a path");
|
|
248
|
+
}
|
|
249
|
+
tlsCert = nextValue;
|
|
250
|
+
i += 1;
|
|
251
|
+
}
|
|
252
|
+
else if (arg === "--tls-key") {
|
|
253
|
+
const nextValue = args[i + 1];
|
|
254
|
+
if (nextValue === undefined) {
|
|
255
|
+
return new Error("--tls-key flag requires a path");
|
|
256
|
+
}
|
|
257
|
+
tlsKey = nextValue;
|
|
258
|
+
i += 1;
|
|
259
|
+
}
|
|
260
|
+
else if (arg === "--tls-dir") {
|
|
261
|
+
const nextValue = args[i + 1];
|
|
262
|
+
if (nextValue === undefined) {
|
|
263
|
+
return new Error("--tls-dir flag requires a directory path");
|
|
264
|
+
}
|
|
265
|
+
tlsDir = nextValue;
|
|
266
|
+
i += 1;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
let logger = DEFAULT_CONFIG.logger;
|
|
270
|
+
if (loggerValue !== undefined) {
|
|
271
|
+
const parsed = parseLoggerValue(loggerValue);
|
|
272
|
+
if (parsed instanceof Error) {
|
|
273
|
+
return parsed;
|
|
274
|
+
}
|
|
275
|
+
logger = parsed;
|
|
276
|
+
}
|
|
277
|
+
let webSocketUrl;
|
|
278
|
+
if (webSocketUrlValue !== undefined) {
|
|
279
|
+
const parsed = parseWebSocketUrlValue(webSocketUrlValue);
|
|
280
|
+
if (parsed instanceof Error) {
|
|
281
|
+
return parsed;
|
|
282
|
+
}
|
|
283
|
+
webSocketUrl = parsed;
|
|
284
|
+
}
|
|
285
|
+
let host;
|
|
286
|
+
if (hostValue !== undefined) {
|
|
287
|
+
const parsed = parseHostValue(hostValue);
|
|
288
|
+
if (parsed instanceof Error) {
|
|
289
|
+
return parsed;
|
|
290
|
+
}
|
|
291
|
+
host = parsed;
|
|
157
292
|
}
|
|
158
293
|
if (httpPortValue !== undefined) {
|
|
159
294
|
const httpPort = parseHttpPortValue(httpPortValue);
|
|
@@ -162,6 +297,21 @@ export const parseConfig = (args) => {
|
|
|
162
297
|
}
|
|
163
298
|
parsedHttpPort = httpPort;
|
|
164
299
|
}
|
|
300
|
+
// TLS validation
|
|
301
|
+
if ((tlsCert || tlsKey || tlsAuto || tlsDir) && !tlsEnabled) {
|
|
302
|
+
return new Error("TLS sub-flags (--tls-cert, --tls-key, --tls-auto, --tls-dir) require --tls");
|
|
303
|
+
}
|
|
304
|
+
if (tlsEnabled) {
|
|
305
|
+
if (tlsCert && !tlsKey) {
|
|
306
|
+
return new Error("--tls-cert requires --tls-key");
|
|
307
|
+
}
|
|
308
|
+
if (tlsKey && !tlsCert) {
|
|
309
|
+
return new Error("--tls-key requires --tls-cert");
|
|
310
|
+
}
|
|
311
|
+
if (tlsAuto && (tlsCert || tlsKey)) {
|
|
312
|
+
return new Error("Cannot combine --tls-auto with --tls-cert/--tls-key. Pick one mode.");
|
|
313
|
+
}
|
|
314
|
+
}
|
|
165
315
|
if (portValue !== undefined) {
|
|
166
316
|
const extensionPort = parseExtensionPortValue(portValue);
|
|
167
317
|
if (extensionPort instanceof Error) {
|
|
@@ -178,6 +328,14 @@ export const parseConfig = (args) => {
|
|
|
178
328
|
transports,
|
|
179
329
|
editorEnabled,
|
|
180
330
|
assetPath,
|
|
331
|
+
webSocketUrl,
|
|
332
|
+
host,
|
|
333
|
+
logger,
|
|
334
|
+
tlsEnabled,
|
|
335
|
+
tlsAuto,
|
|
336
|
+
tlsCert,
|
|
337
|
+
tlsKey,
|
|
338
|
+
tlsDir,
|
|
181
339
|
};
|
|
182
340
|
}
|
|
183
341
|
if (httpPortValue !== undefined) {
|
|
@@ -191,6 +349,14 @@ export const parseConfig = (args) => {
|
|
|
191
349
|
transports,
|
|
192
350
|
editorEnabled,
|
|
193
351
|
assetPath,
|
|
352
|
+
webSocketUrl,
|
|
353
|
+
host,
|
|
354
|
+
logger,
|
|
355
|
+
tlsEnabled,
|
|
356
|
+
tlsAuto,
|
|
357
|
+
tlsCert,
|
|
358
|
+
tlsKey,
|
|
359
|
+
tlsDir,
|
|
194
360
|
};
|
|
195
361
|
}
|
|
196
362
|
const transports = parseTransports(transportValues);
|
|
@@ -203,16 +369,98 @@ export const parseConfig = (args) => {
|
|
|
203
369
|
transports,
|
|
204
370
|
editorEnabled,
|
|
205
371
|
assetPath,
|
|
372
|
+
webSocketUrl,
|
|
373
|
+
host,
|
|
374
|
+
logger,
|
|
375
|
+
tlsEnabled,
|
|
376
|
+
tlsAuto,
|
|
377
|
+
tlsCert,
|
|
378
|
+
tlsKey,
|
|
379
|
+
tlsDir,
|
|
206
380
|
};
|
|
207
381
|
};
|
|
382
|
+
/**
|
|
383
|
+
* Map known DRAWIO_MCP_* environment variables to argv tokens.
|
|
384
|
+
* Pure function - injected env in, argv out.
|
|
385
|
+
*
|
|
386
|
+
* Result is concatenated BEFORE real CLI args so that parseConfig's
|
|
387
|
+
* last-wins semantics give CLI flags precedence over env vars.
|
|
388
|
+
*/
|
|
389
|
+
export const envToArgs = (env) => {
|
|
390
|
+
const out = [];
|
|
391
|
+
const extPort = env.DRAWIO_MCP_EXTENSION_PORT;
|
|
392
|
+
if (extPort && extPort.length > 0) {
|
|
393
|
+
out.push("--extension-port", extPort);
|
|
394
|
+
}
|
|
395
|
+
const httpPort = env.DRAWIO_MCP_HTTP_PORT;
|
|
396
|
+
if (httpPort && httpPort.length > 0) {
|
|
397
|
+
out.push("--http-port", httpPort);
|
|
398
|
+
}
|
|
399
|
+
const transport = env.DRAWIO_MCP_TRANSPORT;
|
|
400
|
+
if (transport && transport.length > 0) {
|
|
401
|
+
out.push("--transport", transport);
|
|
402
|
+
}
|
|
403
|
+
const editor = env.DRAWIO_MCP_EDITOR;
|
|
404
|
+
if (editor && editor.length > 0) {
|
|
405
|
+
const normalized = editor.toLowerCase();
|
|
406
|
+
if (normalized === "true" || normalized === "false") {
|
|
407
|
+
out.push("--editor", normalized);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
const assetPath = env.DRAWIO_MCP_ASSET_PATH;
|
|
411
|
+
if (assetPath && assetPath.length > 0) {
|
|
412
|
+
out.push("--asset-path", assetPath);
|
|
413
|
+
}
|
|
414
|
+
const wsUrl = env.DRAWIO_MCP_WEBSOCKET_URL;
|
|
415
|
+
if (wsUrl && wsUrl.length > 0) {
|
|
416
|
+
out.push("--websocket-url", wsUrl);
|
|
417
|
+
}
|
|
418
|
+
const host = env.DRAWIO_MCP_HOST;
|
|
419
|
+
if (host && host.length > 0) {
|
|
420
|
+
out.push("--host", host);
|
|
421
|
+
}
|
|
422
|
+
const logger = env.DRAWIO_MCP_LOGGER;
|
|
423
|
+
if (logger && logger.length > 0) {
|
|
424
|
+
out.push("--logger", logger);
|
|
425
|
+
}
|
|
426
|
+
const tls = env.DRAWIO_MCP_TLS;
|
|
427
|
+
if (tls && tls.toLowerCase() === "true")
|
|
428
|
+
out.push("--tls");
|
|
429
|
+
const tlsAuto = env.DRAWIO_MCP_TLS_AUTO;
|
|
430
|
+
if (tlsAuto && tlsAuto.toLowerCase() === "true")
|
|
431
|
+
out.push("--tls-auto");
|
|
432
|
+
const tlsCert = env.DRAWIO_MCP_TLS_CERT;
|
|
433
|
+
if (tlsCert && tlsCert.length > 0)
|
|
434
|
+
out.push("--tls-cert", tlsCert);
|
|
435
|
+
const tlsKey = env.DRAWIO_MCP_TLS_KEY;
|
|
436
|
+
if (tlsKey && tlsKey.length > 0)
|
|
437
|
+
out.push("--tls-key", tlsKey);
|
|
438
|
+
const tlsDir = env.DRAWIO_MCP_TLS_DIR;
|
|
439
|
+
if (tlsDir && tlsDir.length > 0)
|
|
440
|
+
out.push("--tls-dir", tlsDir);
|
|
441
|
+
return out;
|
|
442
|
+
};
|
|
208
443
|
/**
|
|
209
444
|
* Build configuration from process.argv
|
|
210
445
|
* This is the main entry point for configuration
|
|
211
446
|
* Returns Error for invalid config, or ServerConfig
|
|
212
447
|
*/
|
|
213
448
|
export const buildConfig = () => {
|
|
214
|
-
const
|
|
215
|
-
|
|
449
|
+
const envArgs = envToArgs(process.env);
|
|
450
|
+
const cliArgs = process.argv.slice(2);
|
|
451
|
+
return parseConfig([...envArgs, ...cliArgs]);
|
|
452
|
+
};
|
|
453
|
+
/**
|
|
454
|
+
* Convenience: parse an empty argument list and return the default
|
|
455
|
+
* ServerConfig. Used by test harnesses that need a config object but do not
|
|
456
|
+
* exercise the parser.
|
|
457
|
+
*/
|
|
458
|
+
export const defaultConfig = () => {
|
|
459
|
+
const result = parseConfig([]);
|
|
460
|
+
if (result instanceof Error) {
|
|
461
|
+
throw result;
|
|
462
|
+
}
|
|
463
|
+
return result;
|
|
216
464
|
};
|
|
217
465
|
export function getHttpFeatureConfig(config) {
|
|
218
466
|
return {
|