drawio-mcp-server 1.8.0 → 2.0.3

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.
Files changed (47) hide show
  1. package/README.md +9 -5
  2. package/build/emitter_bus.js +2 -3
  3. package/build/index.js +178 -332
  4. package/build/plugin/mcp-plugin.js +853 -60
  5. package/build/prefetch-assets.js +11 -0
  6. package/build/real-environment/add-cell-of-shape.test.js +51 -0
  7. package/build/real-environment/add-edge.test.js +86 -0
  8. package/build/real-environment/assertions.js +18 -0
  9. package/build/real-environment/delete-cell-by-id.test.js +45 -0
  10. package/build/real-environment/edge-editing.test.js +70 -0
  11. package/build/real-environment/edit-cell.test.js +64 -0
  12. package/build/real-environment/harness.js +175 -0
  13. package/build/real-environment/import-export.test.js +82 -0
  14. package/build/real-environment/layers-and-selection.test.js +70 -0
  15. package/build/real-environment/logger.js +25 -0
  16. package/build/real-environment/screenshot.js +46 -0
  17. package/build/real-environment/set-cell-parent.test.js +64 -0
  18. package/build/real-environment/shapes.test.js +153 -0
  19. package/build/real-environment/test-helpers.js +10 -0
  20. package/build/real-environment/tools.js +22 -0
  21. package/build/real-environment/types.js +1 -0
  22. package/build/tool.js +46 -0
  23. package/build/tools/add-cell-of-shape.js +42 -0
  24. package/build/tools/add-edge.js +33 -0
  25. package/build/tools/add-rectangle.js +41 -0
  26. package/build/tools/create-layer.js +8 -0
  27. package/build/tools/delete-cell-by-id.js +10 -0
  28. package/build/tools/edit-cell.js +28 -0
  29. package/build/tools/edit-edge.js +30 -0
  30. package/build/tools/export-diagram.js +96 -0
  31. package/build/tools/get-active-layer.js +5 -0
  32. package/build/tools/get-selected-cell.js +5 -0
  33. package/build/tools/get-shape-by-name.js +10 -0
  34. package/build/tools/get-shape-categories.js +5 -0
  35. package/build/tools/get-shapes-in-category.js +10 -0
  36. package/build/tools/import-diagram.js +22 -0
  37. package/build/tools/index.js +49 -0
  38. package/build/tools/list-layers.js +5 -0
  39. package/build/tools/list-paged-model.js +41 -0
  40. package/build/tools/move-cell-to-layer.js +11 -0
  41. package/build/tools/set-active-layer.js +8 -0
  42. package/build/tools/set-cell-data.js +14 -0
  43. package/build/tools/set-cell-parent.js +9 -0
  44. package/build/tools/set-cell-shape.js +13 -0
  45. package/build/tools/shared.js +7 -0
  46. package/build/tools/types.js +1 -0
  47. package/package.json +25 -23
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  var DrawMcp = (() => {
3
- // src/plugin/shape-library.ts
3
+ // src/shape-library.ts
4
4
  var shapeLibrary = {
5
5
  rectangle: {
6
6
  category: "general",
@@ -337,7 +337,7 @@ var DrawMcp = (() => {
337
337
  }
338
338
  };
339
339
 
340
- // src/plugin/websocketManager.ts
340
+ // src/websocketManager.ts
341
341
  function createWebSocketManager(options) {
342
342
  let socket = null;
343
343
  let reconnectAttempts = 0;
@@ -495,7 +495,7 @@ var DrawMcp = (() => {
495
495
  };
496
496
  }
497
497
 
498
- // src/plugin/pluginConfig.ts
498
+ // src/pluginConfig.ts
499
499
  var DEFAULT_PLUGIN_CONFIG = {
500
500
  websocketPort: 3333,
501
501
  serverUrl: ""
@@ -573,7 +573,7 @@ var DrawMcp = (() => {
573
573
  return `ws://${host}:${config.websocketPort}`;
574
574
  }
575
575
 
576
- // src/plugin/settingsDialog.ts
576
+ // src/settingsDialog.ts
577
577
  function createDialogContainer() {
578
578
  const container = document.createElement("div");
579
579
  container.id = "mcp-settings-dialog";
@@ -1013,7 +1013,7 @@ var DrawMcp = (() => {
1013
1013
  }
1014
1014
  }
1015
1015
 
1016
- // src/plugin/drawio-tools.ts
1016
+ // src/drawio-tools.ts
1017
1017
  function transform_NamedNodeMap_to_record(attributes) {
1018
1018
  const tstr = Object.prototype.toString.call(attributes);
1019
1019
  if (tstr !== "[object NamedNodeMap]") {
@@ -1065,7 +1065,7 @@ var DrawMcp = (() => {
1065
1065
  const result = {};
1066
1066
  for (const key in obj) {
1067
1067
  const value = obj[key];
1068
- if (typeof value !== "function" && key !== "children" && key !== "edges" && !key.startsWith("aria") && key !== "ownerDocument" && key !== "part" && key !== "classList" && key !== "childNodes" && key !== "shadowRoot" && key !== "innerHTML" && key !== "outerHTML" && key !== "scrollTop" && key !== "scrollLeft" && key !== "scrollWidth" && key !== "scrollHeight" && key !== "clientTop" && key !== "clientLeft" && key !== "clientWidth" && key !== "clientHeight" && key !== "onbeforecopy" && key !== "onbeforecut" && key !== "onbeforepaste" && key !== "onsearch" && key !== "elementTiming" && key !== "onfullscreenchange" && key !== "onfullscreenerror" && key !== "onwebkitfullscreenchange" && key !== "onwebkitfullscreenerror" && key !== "firstElementChild" && key !== "lastElementChild" && key !== "childElementCount" && key !== "previousElementSibling" && key !== "nextElementSibling" && key !== "currentCSSZoom" && key !== "parentNode" && key !== "parentElement" && key !== "firstChild" && key !== "lastChild" && key !== "previousSibling" && key !== "nextSibling" && key !== "nodeValue" && key !== "textContent") {
1068
+ if (typeof value !== "function" && key !== "children" && key !== "edges" && !key.startsWith("aria") && key !== "ownerDocument" && key !== "part" && key !== "classList" && key !== "childNodes" && key !== "shadowRoot" && key !== "innerHTML" && key !== "outerHTML" && key !== "scrollTop" && key !== "scrollLeft" && key !== "scrollWidth" && key !== "scrollHeight" && key !== "clientTop" && key !== "clientLeft" && key !== "clientWidth" && key !== "clientHeight" && key !== "onbeforecopy" && key !== "onbeforecut" && key !== "onbeforepaste" && key !== "onsearch" && key !== "elementTiming" && key !== "onfullscreenchange" && key !== "onfullscreenerror" && key !== "onwebkitfullscreenchange" && key !== "onwebkitfullscreenerror" && key !== "firstElementChild" && key !== "lastElementChild" && key !== "childElementCount" && key !== "previousElementSibling" && key !== "nextElementSibling" && key !== "currentCSSZoom" && key !== "parentNode" && key !== "parentElement" && key !== "firstChild" && key !== "lastChild" && key !== "previousSibling" && key !== "nextSibling" && key !== "nodeValue" && key !== "textContent" && key !== "ELEMENT_NODE" && key !== "ATTRIBUTE_NODE" && key !== "TEXT_NODE" && key !== "CDATA_SECTION_NODE" && key !== "ENTITY_REFERENCE_NODE" && key !== "ENTITY_NODE" && key !== "PROCESSING_INSTRUCTION_NODE" && key !== "COMMENT_NODE" && key !== "DOCUMENT_NODE" && key !== "DOCUMENT_TYPE_NODE" && key !== "DOCUMENT_FRAGMENT_NODE" && key !== "NOTATION_NODE" && key !== "DOCUMENT_POSITION_DISCONNECTED" && key !== "DOCUMENT_POSITION_PRECEDING" && key !== "DOCUMENT_POSITION_FOLLOWING" && key !== "DOCUMENT_POSITION_CONTAINS" && key !== "DOCUMENT_POSITION_CONTAINED_BY" && key !== "DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC") {
1069
1069
  let stripped_value = {};
1070
1070
  if ((key === "parent" || key === "source" || key === "target") && value !== void 0 && value !== null) {
1071
1071
  stripped_value = {
@@ -1082,6 +1082,122 @@ var DrawMcp = (() => {
1082
1082
  }
1083
1083
  return result;
1084
1084
  }
1085
+ function transform_cell_for_display(cell) {
1086
+ if (!cell || typeof cell !== "object") {
1087
+ return null;
1088
+ }
1089
+ const transformed = {
1090
+ id: cell.id || "",
1091
+ mxObjectId: cell.mxObjectId || "",
1092
+ value: "",
1093
+ geometry: cell.geometry,
1094
+ style: cell.style,
1095
+ edge: cell.edge,
1096
+ edges: cell.edges,
1097
+ parent: cell.parent,
1098
+ source: cell.source,
1099
+ target: cell.target
1100
+ };
1101
+ if (cell.value !== null && cell.value !== void 0) {
1102
+ if (typeof cell.value === "string") {
1103
+ transformed.value = cell.value;
1104
+ } else if (typeof cell.value === "object") {
1105
+ const transformed_attributes = transform_cells_NamedNodeMap_to_attributes(cell);
1106
+ transformed.value = {
1107
+ attributes: transformed_attributes,
1108
+ nodeName: cell.value.nodeName,
1109
+ localName: cell.value.localName,
1110
+ tagName: cell.value.tagName
1111
+ };
1112
+ }
1113
+ }
1114
+ return transformed;
1115
+ }
1116
+ function get_cell_layer(graph, cell) {
1117
+ if (!cell || !graph) {
1118
+ return null;
1119
+ }
1120
+ try {
1121
+ const layer = graph.getLayerForCell(cell);
1122
+ if (layer) {
1123
+ return {
1124
+ id: layer.id || "",
1125
+ name: layer.value || "Default Layer"
1126
+ };
1127
+ }
1128
+ } catch (error) {
1129
+ console.warn("Could not get layer for cell:", error);
1130
+ }
1131
+ return null;
1132
+ }
1133
+ function get_cell_tags(graph, cell) {
1134
+ if (!cell || !graph) {
1135
+ return [];
1136
+ }
1137
+ try {
1138
+ const tags = graph.getTagsForCell(cell);
1139
+ return tags || [];
1140
+ } catch (error) {
1141
+ console.warn("Could not get tags for cell:", error);
1142
+ return [];
1143
+ }
1144
+ }
1145
+ function mx_isRoot(root_cell) {
1146
+ return function(cell) {
1147
+ return null != cell && cell == root_cell;
1148
+ };
1149
+ }
1150
+ function mx_isLayer(root_cell) {
1151
+ return function(cell) {
1152
+ return mx_isRoot(root_cell)(cell.getParent());
1153
+ };
1154
+ }
1155
+ function resolve_parent(graph, parent_id) {
1156
+ if (parent_id) {
1157
+ const model = graph.getModel();
1158
+ const parent = model.getCell(parent_id);
1159
+ if (!parent) {
1160
+ throw new Error(`Parent cell '${parent_id}' not found`);
1161
+ }
1162
+ return parent;
1163
+ }
1164
+ return graph.getDefaultParent();
1165
+ }
1166
+ function normalize_shape_style(shapeName, style) {
1167
+ if (!style || !shapeName.startsWith("mxgraph.aws4.")) {
1168
+ return style;
1169
+ }
1170
+ const hasExplicitShape = style.includes("shape=");
1171
+ if (hasExplicitShape) {
1172
+ return style;
1173
+ }
1174
+ return `shape=${shapeName};${style}`;
1175
+ }
1176
+ function apply_style_to_cell(graph, cell, style) {
1177
+ if (!style || !cell) {
1178
+ return;
1179
+ }
1180
+ const model = graph.getModel();
1181
+ model.setStyle(cell, style);
1182
+ if (typeof cell.setStyle === "function") {
1183
+ cell.setStyle(style);
1184
+ }
1185
+ if (typeof graph.refresh === "function") {
1186
+ graph.refresh(cell);
1187
+ }
1188
+ }
1189
+ function create_shape_vertex(graph, options) {
1190
+ const mxCell = window.mxCell;
1191
+ const mxGeometry = window.mxGeometry;
1192
+ const cell = new mxCell(
1193
+ options.text,
1194
+ new mxGeometry(options.x, options.y, options.width, options.height),
1195
+ options.style
1196
+ );
1197
+ cell.vertex = true;
1198
+ graph.addCell(cell, options.parent);
1199
+ return cell;
1200
+ }
1085
1201
  function add_new_rectangle(ui2, options) {
1086
1202
  const { editor } = ui2;
1087
1203
  const { graph } = editor;
@@ -1091,10 +1207,11 @@ var DrawMcp = (() => {
1091
1207
  const height = options.height || 60;
1092
1208
  const text = options.text || "";
1093
1209
  const style = options.style || "whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#000000;";
1210
+ const parent = resolve_parent(graph, options.parent_id);
1094
1211
  graph.getModel().beginUpdate();
1095
1212
  try {
1096
1213
  const vertex = graph.insertVertex(
1097
- graph.getDefaultParent(),
1214
+ parent,
1098
1215
  null,
1099
1216
  text,
1100
1217
  x,
@@ -1124,6 +1241,7 @@ var DrawMcp = (() => {
1124
1241
  graph.getModel().endUpdate();
1125
1242
  }
1126
1243
  }
1244
+ var SELF_CONNECTOR_DEFAULT_STYLE = "edgeStyle=loopEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;";
1127
1245
  function add_edge(ui2, options) {
1128
1246
  const { editor } = ui2;
1129
1247
  const { graph } = editor;
@@ -1133,19 +1251,20 @@ var DrawMcp = (() => {
1133
1251
  if (!source || !target) {
1134
1252
  return null;
1135
1253
  }
1136
- const defaultStyle = "endArrow=classic;html=1;rounded=0;";
1254
+ const isSelfConnector = options.source_id === options.target_id;
1255
+ const defaultStyle = isSelfConnector ? SELF_CONNECTOR_DEFAULT_STYLE : "endArrow=classic;html=1;rounded=0;";
1137
1256
  const style = options.style || defaultStyle;
1138
1257
  const text = options.text || "";
1258
+ const parent = resolve_parent(graph, options.parent_id);
1139
1259
  model.beginUpdate();
1140
1260
  try {
1141
- const edge = graph.insertEdge(
1142
- graph.getDefaultParent(),
1143
- null,
1144
- text,
1145
- source,
1146
- target,
1147
- style
1148
- );
1261
+ const edge = graph.insertEdge(parent, null, text, source, target, style);
1262
+ if (options.points && options.points.length > 0) {
1263
+ const geo = edge.getGeometry();
1264
+ const mxPoint = window.mxPoint;
1265
+ geo.points = options.points.map((p) => new mxPoint(p.x, p.y));
1266
+ model.setGeometry(edge, geo);
1267
+ }
1149
1268
  return edge;
1150
1269
  } finally {
1151
1270
  model.endUpdate();
@@ -1233,6 +1352,12 @@ var DrawMcp = (() => {
1233
1352
  if (options.style !== void 0) {
1234
1353
  graph.setCellStyle(options.style, [edge]);
1235
1354
  }
1355
+ if (options.points !== void 0) {
1356
+ const geo = edge.getGeometry().clone();
1357
+ const mxPoint = window.mxPoint;
1358
+ geo.points = options.points.map((p) => new mxPoint(p.x, p.y));
1359
+ model.setGeometry(edge, geo);
1360
+ }
1236
1361
  } finally {
1237
1362
  model.endUpdate();
1238
1363
  }
@@ -1267,7 +1392,7 @@ var DrawMcp = (() => {
1267
1392
  }
1268
1393
  model.beginUpdate();
1269
1394
  try {
1270
- graph.setCellStyle(style, [cell]);
1395
+ apply_style_to_cell(graph, cell, style);
1271
1396
  } finally {
1272
1397
  model.endUpdate();
1273
1398
  }
@@ -1343,28 +1468,34 @@ var DrawMcp = (() => {
1343
1468
  function add_cell_of_shape(ui2, options) {
1344
1469
  const { editor } = ui2;
1345
1470
  const { graph } = editor;
1346
- const shape_name = options.shape_name || "rectangle";
1471
+ const shape_name = options.shape_name;
1472
+ if (!shape_name) {
1473
+ throw new Error("add_cell_of_shape requires a shape_name");
1474
+ }
1347
1475
  const x = options.x || 100;
1348
1476
  const y = options.y || 100;
1349
1477
  const width = options.width || 120;
1350
1478
  const height = options.height || 80;
1351
1479
  const text = options.text || "";
1352
1480
  const style = options.style || "";
1481
+ const parent = resolve_parent(graph, options.parent_id);
1353
1482
  const shape_entry = get_shape_by_name(ui2, { shape_name });
1354
1483
  if (!shape_entry) return null;
1484
+ const mergedStyle = normalize_shape_style(
1485
+ shape_name,
1486
+ [shape_entry.style, style].filter((entry) => typeof entry === "string" && entry.trim().length > 0).join(";")
1487
+ );
1355
1488
  graph.getModel().beginUpdate();
1356
1489
  try {
1357
- const cell = graph.insertVertex(
1358
- graph.getDefaultParent(),
1359
- null,
1490
+ const cell = create_shape_vertex(graph, {
1491
+ parent,
1360
1492
  text,
1361
1493
  x,
1362
1494
  y,
1363
1495
  width,
1364
1496
  height,
1365
- `${shape_entry.style};${style}`,
1366
- false
1367
- );
1497
+ style: mergedStyle
1498
+ });
1368
1499
  return cell;
1369
1500
  } finally {
1370
1501
  graph.getModel().endUpdate();
@@ -1406,7 +1537,29 @@ var DrawMcp = (() => {
1406
1537
  }
1407
1538
  return attributes;
1408
1539
  }
1409
- function matches_cell_type(cell, cell_type) {
1540
+ function evaluate_filter_expression(expression, attributes) {
1541
+ if (!Array.isArray(expression) || expression.length === 0) {
1542
+ return true;
1543
+ }
1544
+ const [operator, ...operands] = expression;
1545
+ switch (operator) {
1546
+ case "and":
1547
+ return operands.every(
1548
+ (op) => evaluate_filter_expression(op, attributes)
1549
+ );
1550
+ case "or":
1551
+ return operands.some(
1552
+ (op) => evaluate_filter_expression(op, attributes)
1553
+ );
1554
+ case "equal":
1555
+ if (operands.length !== 2) return false;
1556
+ const [key, value] = operands;
1557
+ return attributes[key] === value;
1558
+ default:
1559
+ return true;
1560
+ }
1561
+ }
1562
+ function matches_cell_type(cell, cell_type, isLayer) {
1410
1563
  switch (cell_type) {
1411
1564
  case "edge":
1412
1565
  return cell.edge === true || cell.edge === 1;
@@ -1416,6 +1569,8 @@ var DrawMcp = (() => {
1416
1569
  return cell.value?.nodeName === "object";
1417
1570
  case "group":
1418
1571
  return cell.style === "group";
1572
+ case "layer":
1573
+ return isLayer(cell);
1419
1574
  default:
1420
1575
  return true;
1421
1576
  }
@@ -1423,27 +1578,60 @@ var DrawMcp = (() => {
1423
1578
  let filtered_cells = Object.values(cells);
1424
1579
  if (options.filter) {
1425
1580
  const filter = options.filter;
1426
- if (filter.cell_type) {
1427
- filtered_cells = filtered_cells.filter(
1428
- (cell) => matches_cell_type(cell, filter.cell_type)
1429
- );
1430
- }
1431
- if (filter.ids && filter.ids.length > 0) {
1432
- filtered_cells = filtered_cells.filter(
1433
- (cell) => filter.ids.includes(cell.id)
1434
- );
1435
- }
1581
+ const allParentIds = [
1582
+ ...filter.parent_ids || [],
1583
+ ...filter.layer_ids || []
1584
+ ];
1585
+ filtered_cells = filtered_cells.filter((cell) => {
1586
+ if (options.filter?.cell_type && !matches_cell_type(
1587
+ cell,
1588
+ options.filter.cell_type,
1589
+ mx_isLayer(model.root)
1590
+ )) {
1591
+ return false;
1592
+ }
1593
+ if (options.filter?.attributes && options.filter.attributes.length > 0) {
1594
+ const cellAttributes = extract_cell_attributes(cell);
1595
+ if (!evaluate_filter_expression(options.filter.attributes, cellAttributes)) {
1596
+ return false;
1597
+ }
1598
+ }
1599
+ if (allParentIds.length > 0) {
1600
+ const parent = cell.parent;
1601
+ if (!parent || !parent.id || !allParentIds.includes(parent.id)) {
1602
+ return false;
1603
+ }
1604
+ }
1605
+ if (filter.ids && filter.ids.length > 0) {
1606
+ if (!filter.ids.includes(cell.id)) {
1607
+ return false;
1608
+ }
1609
+ }
1610
+ return true;
1611
+ });
1436
1612
  }
1437
1613
  const page = Math.max(0, options.page || 0);
1438
1614
  const page_size = Math.max(1, options.page_size || 50);
1439
1615
  const start_index = page * page_size;
1440
- const paginated_ids = filtered_cells.slice(start_index, start_index + page_size).map((c) => c.id);
1616
+ const cell_ids = filtered_cells.map((cell) => cell.id);
1617
+ const paginated_ids = cell_ids.slice(start_index, start_index + page_size);
1441
1618
  const transformed_cells = [];
1442
1619
  for (const cell_id of paginated_ids) {
1443
1620
  const cell = cells[cell_id];
1444
1621
  if (cell) {
1445
1622
  const sanitized_cell = remove_circular_dependencies(cell);
1446
- transformed_cells.push(sanitized_cell);
1623
+ const transformed_cell = transform_cell_for_display(sanitized_cell);
1624
+ if (transformed_cell) {
1625
+ const layer_info = get_cell_layer(graph, cell);
1626
+ if (layer_info) {
1627
+ transformed_cell.layer = layer_info;
1628
+ }
1629
+ const tags_info = get_cell_tags(graph, cell);
1630
+ if (tags_info && tags_info.length > 0) {
1631
+ transformed_cell.tags = tags_info;
1632
+ }
1633
+ transformed_cells.push(transformed_cell);
1634
+ }
1447
1635
  }
1448
1636
  }
1449
1637
  return transformed_cells;
@@ -1506,6 +1694,29 @@ var DrawMcp = (() => {
1506
1694
  to_layer: options.target_layer_id
1507
1695
  };
1508
1696
  }
1697
+ function set_cell_parent(ui2, options) {
1698
+ const { editor } = ui2;
1699
+ const { graph } = editor;
1700
+ const model = graph.getModel();
1701
+ const cell = model.getCell(options.cell_id);
1702
+ if (!cell) {
1703
+ throw new Error(`Cell with ID ${options.cell_id} not found`);
1704
+ }
1705
+ const parent = model.getCell(options.parent_id);
1706
+ if (!parent) {
1707
+ throw new Error(`Parent cell with ID ${options.parent_id} not found`);
1708
+ }
1709
+ model.beginUpdate();
1710
+ try {
1711
+ model.add(parent, cell);
1712
+ } finally {
1713
+ model.endUpdate();
1714
+ }
1715
+ return {
1716
+ cell_id: options.cell_id,
1717
+ parent_id: options.parent_id
1718
+ };
1719
+ }
1509
1720
  function get_active_layer(ui2) {
1510
1721
  const { editor } = ui2;
1511
1722
  const { graph } = editor;
@@ -1534,8 +1745,541 @@ var DrawMcp = (() => {
1534
1745
  name: options.name
1535
1746
  };
1536
1747
  }
1748
+ var PNG_SIZE_WARNING_THRESHOLD = 5 * 1024 * 1024;
1749
+ function export_diagram(ui2, options) {
1750
+ const format = options.format || "xml";
1751
+ const scale = options.scale ?? 1;
1752
+ const border = options.border ?? 0;
1753
+ const background = options.transparent ? null : options.background ?? null;
1754
+ const includeShadow = options.shadow ?? false;
1755
+ const cropToDiagram = options.crop ?? true;
1756
+ const selectionOnly = options.selection_only ?? false;
1757
+ const embedXml = options.embed_xml ?? false;
1758
+ const size = options.size ?? "diagram";
1759
+ const dpi = options.dpi ?? 96;
1760
+ switch (format) {
1761
+ case "xml":
1762
+ return export_xml(ui2, { selectionOnly, size });
1763
+ case "svg":
1764
+ return export_svg(ui2, {
1765
+ scale,
1766
+ border,
1767
+ background,
1768
+ includeShadow,
1769
+ cropToDiagram,
1770
+ selectionOnly,
1771
+ embedXml,
1772
+ size
1773
+ });
1774
+ case "png":
1775
+ return export_png(ui2, {
1776
+ scale,
1777
+ border,
1778
+ background,
1779
+ includeShadow,
1780
+ cropToDiagram,
1781
+ selectionOnly,
1782
+ embedXml,
1783
+ size,
1784
+ dpi
1785
+ });
1786
+ default:
1787
+ throw new Error(`Unsupported export format: ${format}`);
1788
+ }
1789
+ }
1790
+ function export_xml(ui2, params) {
1791
+ const mxUtils = window.mxUtils;
1792
+ const { selectionOnly, size } = params;
1793
+ const ignoreSelection = !selectionOnly;
1794
+ const currentPage = size === "page" || size === "selection";
1795
+ const uncompressed = true;
1796
+ let xml;
1797
+ if (size === "selection" && selectionOnly) {
1798
+ const enc = new window.mxCodec();
1799
+ const node = enc.encode(ui2.editor.graph.getModel());
1800
+ xml = mxUtils.getXml(node);
1801
+ } else {
1802
+ const node = ui2.getXmlFileData(ignoreSelection, currentPage, uncompressed);
1803
+ xml = mxUtils.getXml(node);
1804
+ }
1805
+ return {
1806
+ format: "xml",
1807
+ mimeType: "application/xml",
1808
+ data: xml
1809
+ };
1810
+ }
1811
+ function export_svg(ui2, params) {
1812
+ const mxUtils = window.mxUtils;
1813
+ const Graph = window.Graph;
1814
+ const {
1815
+ scale,
1816
+ border,
1817
+ background,
1818
+ includeShadow,
1819
+ selectionOnly,
1820
+ embedXml,
1821
+ size
1822
+ } = params;
1823
+ const graph = ui2.editor.graph;
1824
+ const ignoreSelection = !selectionOnly && size !== "selection";
1825
+ const bg = background !== "none" ? background : null;
1826
+ if (embedXml) {
1827
+ const currentPage = size === "page" || size === "selection";
1828
+ const svgString2 = ui2.getFileData(
1829
+ false,
1830
+ true,
1831
+ null,
1832
+ null,
1833
+ ignoreSelection,
1834
+ currentPage
1835
+ );
1836
+ const bounds = graph.getGraphBounds();
1837
+ const w2 = Math.ceil(bounds.width * scale + bounds.x * scale + 2 * border);
1838
+ const h2 = Math.ceil(bounds.height * scale + bounds.y * scale + 2 * border);
1839
+ return {
1840
+ format: "svg",
1841
+ mimeType: "image/svg+xml",
1842
+ data: svgString2,
1843
+ width: w2,
1844
+ height: h2
1845
+ };
1846
+ }
1847
+ const svgRoot = graph.getSvg(
1848
+ bg,
1849
+ scale,
1850
+ border,
1851
+ !params.cropToDiagram,
1852
+ null,
1853
+ ignoreSelection
1854
+ );
1855
+ if (graph.shadowVisible || includeShadow) {
1856
+ graph.addSvgShadow(svgRoot, null, null, border === 0);
1857
+ }
1858
+ const svgString = (Graph.xmlDeclaration || '<?xml version="1.0" encoding="UTF-8"?>') + "\n" + (Graph.svgDoctype || "") + "\n" + mxUtils.getXml(svgRoot);
1859
+ const w = parseInt(svgRoot.getAttribute("width")) || 0;
1860
+ const h = parseInt(svgRoot.getAttribute("height")) || 0;
1861
+ return {
1862
+ format: "svg",
1863
+ mimeType: "image/svg+xml",
1864
+ data: svgString,
1865
+ width: w,
1866
+ height: h
1867
+ };
1868
+ }
1869
+ function export_png(ui2, params) {
1870
+ return new Promise((resolve, reject) => {
1871
+ const {
1872
+ scale,
1873
+ border,
1874
+ background,
1875
+ includeShadow,
1876
+ selectionOnly,
1877
+ embedXml,
1878
+ size,
1879
+ dpi,
1880
+ cropToDiagram
1881
+ } = params;
1882
+ const imageCache = {};
1883
+ const ignoreSelection = !selectionOnly && size !== "selection";
1884
+ let xml = null;
1885
+ if (embedXml) {
1886
+ xml = ui2.getFileData(
1887
+ true,
1888
+ null,
1889
+ null,
1890
+ null,
1891
+ ignoreSelection,
1892
+ size === "page" || size === "selection"
1893
+ );
1894
+ }
1895
+ ui2.exportToCanvas(
1896
+ (canvas) => {
1897
+ try {
1898
+ let dataUrl;
1899
+ if (xml != null) {
1900
+ dataUrl = ui2.createImageDataUri(canvas, xml, "png", dpi);
1901
+ } else {
1902
+ dataUrl = canvas.toDataURL("image/png");
1903
+ }
1904
+ const base64Data = dataUrl.replace(/^data:image\/png;base64,/, "");
1905
+ const result = {
1906
+ format: "png",
1907
+ mimeType: "image/png",
1908
+ data: base64Data,
1909
+ width: canvas.width,
1910
+ height: canvas.height
1911
+ };
1912
+ if (base64Data.length > PNG_SIZE_WARNING_THRESHOLD) {
1913
+ const sizeMB = (base64Data.length / (1024 * 1024)).toFixed(1);
1914
+ result.warning = `PNG export is large (${sizeMB} MB). Consider using SVG or reducing scale to reduce size.`;
1915
+ }
1916
+ resolve(result);
1917
+ } catch (err) {
1918
+ reject(err);
1919
+ }
1920
+ },
1921
+ null,
1922
+ imageCache,
1923
+ background,
1924
+ (err) => reject(err),
1925
+ null,
1926
+ ignoreSelection,
1927
+ scale,
1928
+ background === null,
1929
+ includeShadow,
1930
+ null,
1931
+ null,
1932
+ border,
1933
+ !cropToDiagram,
1934
+ false,
1935
+ null
1936
+ );
1937
+ });
1938
+ }
1939
+ function extractXmlFromSvg(svgContent) {
1940
+ try {
1941
+ const parser = new DOMParser();
1942
+ const svgDoc = parser.parseFromString(svgContent, "image/svg+xml");
1943
+ const metadata = svgDoc.querySelector("metadata");
1944
+ if (metadata) {
1945
+ const mxfile2 = metadata.querySelector("mxfile");
1946
+ if (mxfile2) {
1947
+ const serializer = new XMLSerializer();
1948
+ return serializer.serializeToString(mxfile2);
1949
+ }
1950
+ }
1951
+ const defs = svgDoc.querySelector("defs");
1952
+ if (defs) {
1953
+ const mxfile2 = defs.querySelector("mxfile");
1954
+ if (mxfile2) {
1955
+ const serializer = new XMLSerializer();
1956
+ return serializer.serializeToString(mxfile2);
1957
+ }
1958
+ }
1959
+ const mxfile = svgDoc.querySelector("mxfile");
1960
+ if (mxfile) {
1961
+ const serializer = new XMLSerializer();
1962
+ return serializer.serializeToString(mxfile);
1963
+ }
1964
+ return null;
1965
+ } catch (error) {
1966
+ console.error("[import-diagram] Error extracting XML from SVG:", error);
1967
+ return null;
1968
+ }
1969
+ }
1970
+ function extractXmlFromPng(base64Png) {
1971
+ try {
1972
+ const binaryString = atob(base64Png);
1973
+ const bytes = new Uint8Array(binaryString.length);
1974
+ for (let i = 0; i < binaryString.length; i++) {
1975
+ bytes[i] = binaryString.charCodeAt(i);
1976
+ }
1977
+ const pngSignature = [137, 80, 78, 71, 13, 10, 26, 10];
1978
+ for (let i = 0; i < 8; i++) {
1979
+ if (bytes[i] !== pngSignature[i]) {
1980
+ console.error("[import-diagram] Invalid PNG signature");
1981
+ return null;
1982
+ }
1983
+ }
1984
+ let offset = 8;
1985
+ while (offset < bytes.length) {
1986
+ const length = bytes[offset] << 24 | bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3];
1987
+ const chunkType = String.fromCharCode(
1988
+ bytes[offset + 4],
1989
+ bytes[offset + 5],
1990
+ bytes[offset + 6],
1991
+ bytes[offset + 7]
1992
+ );
1993
+ if (chunkType === "tEXt") {
1994
+ const chunkData = bytes.slice(offset + 8, offset + 8 + length);
1995
+ let nullIndex = -1;
1996
+ for (let i = 0; i < chunkData.length; i++) {
1997
+ if (chunkData[i] === 0) {
1998
+ nullIndex = i;
1999
+ break;
2000
+ }
2001
+ }
2002
+ if (nullIndex !== -1) {
2003
+ const keyword = String.fromCharCode(...chunkData.slice(0, nullIndex));
2004
+ const text = String.fromCharCode(...chunkData.slice(nullIndex + 1));
2005
+ if (keyword === "mxGraphModel" || keyword === "mxfile" || text.includes("<mxfile") || text.includes("<mxGraphModel")) {
2006
+ return text;
2007
+ }
2008
+ }
2009
+ }
2010
+ if (chunkType === "zTXt") {
2011
+ const chunkData = bytes.slice(offset + 8, offset + 8 + length);
2012
+ let nullIndex = -1;
2013
+ for (let i = 0; i < chunkData.length; i++) {
2014
+ if (chunkData[i] === 0) {
2015
+ nullIndex = i;
2016
+ break;
2017
+ }
2018
+ }
2019
+ if (nullIndex !== -1) {
2020
+ const keyword = String.fromCharCode(...chunkData.slice(0, nullIndex));
2021
+ const compressionMethod = chunkData[nullIndex + 1];
2022
+ if (compressionMethod === 0) {
2023
+ const compressedData = chunkData.slice(nullIndex + 2);
2024
+ if (typeof DecompressionStream !== "undefined") {
2025
+ console.warn(
2026
+ "[import-diagram] zTXt chunk found but decompression not implemented in sync context"
2027
+ );
2028
+ }
2029
+ }
2030
+ }
2031
+ }
2032
+ offset += 12 + length;
2033
+ }
2034
+ return null;
2035
+ } catch (error) {
2036
+ console.error("[import-diagram] Error extracting XML from PNG:", error);
2037
+ return null;
2038
+ }
2039
+ }
2040
+ function import_diagram(ui2, options) {
2041
+ const {
2042
+ data,
2043
+ format,
2044
+ mode = "replace",
2045
+ filename
2046
+ } = options;
2047
+ if (!data) {
2048
+ return { success: false, message: "No data provided" };
2049
+ }
2050
+ let xml = null;
2051
+ switch (format) {
2052
+ case "xml":
2053
+ xml = data;
2054
+ break;
2055
+ case "svg":
2056
+ let svgContent = data;
2057
+ if (!data.trim().startsWith("<")) {
2058
+ try {
2059
+ svgContent = atob(data);
2060
+ } catch (e) {
2061
+ return { success: false, message: "Invalid base64 SVG data" };
2062
+ }
2063
+ }
2064
+ xml = extractXmlFromSvg(svgContent);
2065
+ if (!xml) {
2066
+ return { success: false, message: "Could not extract XML from SVG" };
2067
+ }
2068
+ break;
2069
+ case "png":
2070
+ xml = extractXmlFromPng(data);
2071
+ if (!xml) {
2072
+ return { success: false, message: "Could not extract XML from PNG" };
2073
+ }
2074
+ break;
2075
+ default:
2076
+ return { success: false, message: `Unsupported format: ${format}` };
2077
+ }
2078
+ if (!xml) {
2079
+ return { success: false, message: "Failed to extract XML from input" };
2080
+ }
2081
+ const mxUtils = window.mxUtils;
2082
+ if (!mxUtils) {
2083
+ return { success: false, message: "mxUtils not available" };
2084
+ }
2085
+ try {
2086
+ const doc = mxUtils.parseXml(xml);
2087
+ if (!doc || !doc.documentElement) {
2088
+ return { success: false, message: "Invalid XML structure" };
2089
+ }
2090
+ const root = doc.documentElement;
2091
+ if (root.nodeName !== "mxfile" && root.nodeName !== "mxGraphModel") {
2092
+ return {
2093
+ success: false,
2094
+ message: `Invalid Draw.io XML: expected mxfile or mxGraphModel, got ${root.nodeName}`
2095
+ };
2096
+ }
2097
+ const { editor } = ui2;
2098
+ const { graph } = editor;
2099
+ const model = graph.getModel();
2100
+ let cellCount = 0;
2101
+ const cells = doc.getElementsByTagName("mxCell");
2102
+ cellCount = cells.length;
2103
+ switch (mode) {
2104
+ case "replace":
2105
+ let graphModelElement = doc.documentElement;
2106
+ if (graphModelElement.nodeName === "mxfile") {
2107
+ const diagram = graphModelElement.querySelector("diagram");
2108
+ if (diagram) {
2109
+ const mxGraphModel = diagram.querySelector("mxGraphModel");
2110
+ if (mxGraphModel) {
2111
+ graphModelElement = mxGraphModel;
2112
+ }
2113
+ }
2114
+ }
2115
+ if (editor.setGraphXml) {
2116
+ editor.setGraphXml(graphModelElement);
2117
+ graph.refresh();
2118
+ graph.fit();
2119
+ } else {
2120
+ model.clear();
2121
+ model.setRoot(new window.mxCell());
2122
+ const codec = new window.mxCodec(
2123
+ graphModelElement.ownerDocument
2124
+ );
2125
+ codec.decode(graphModelElement, model);
2126
+ }
2127
+ return {
2128
+ success: true,
2129
+ message: `Diagram replaced successfully${filename ? ` from ${filename}` : ""}`,
2130
+ cells: cellCount
2131
+ };
2132
+ case "add":
2133
+ model.beginUpdate();
2134
+ try {
2135
+ const importedDoc = mxUtils.parseXml(xml);
2136
+ const importedRoot = importedDoc.documentElement;
2137
+ let graphModel = importedRoot;
2138
+ if (importedRoot.nodeName === "mxfile") {
2139
+ const diagram = importedRoot.querySelector("diagram");
2140
+ if (diagram) {
2141
+ graphModel = diagram.querySelector("mxGraphModel") || diagram;
2142
+ }
2143
+ }
2144
+ const importedRootCell = graphModel.querySelector("root");
2145
+ if (importedRootCell) {
2146
+ const defaultParent = graph.getDefaultParent();
2147
+ const importedCells = importedRootCell.querySelectorAll("mxCell");
2148
+ const idMapping = {};
2149
+ importedCells.forEach((cell) => {
2150
+ const oldId = cell.getAttribute("id");
2151
+ if (oldId === "0" || oldId === "1") {
2152
+ return;
2153
+ }
2154
+ const newCell = new window.mxCell();
2155
+ newCell.setId(null);
2156
+ if (cell.hasAttribute("value")) {
2157
+ newCell.setValue(cell.getAttribute("value"));
2158
+ }
2159
+ if (cell.hasAttribute("style")) {
2160
+ newCell.setStyle(cell.getAttribute("style"));
2161
+ }
2162
+ if (cell.hasAttribute("vertex")) {
2163
+ newCell.vertex = cell.getAttribute("vertex") === "1";
2164
+ }
2165
+ if (cell.hasAttribute("edge")) {
2166
+ newCell.edge = cell.getAttribute("edge") === "1";
2167
+ }
2168
+ const geo = cell.querySelector("mxGeometry");
2169
+ if (geo) {
2170
+ const geometry = new window.mxGeometry(
2171
+ parseFloat(geo.getAttribute("x") || "0"),
2172
+ parseFloat(geo.getAttribute("y") || "0"),
2173
+ parseFloat(geo.getAttribute("width") || "0"),
2174
+ parseFloat(geo.getAttribute("height") || "0")
2175
+ );
2176
+ if (geo.getAttribute("relative") === "1") {
2177
+ geometry.relative = true;
2178
+ }
2179
+ newCell.setGeometry(geometry);
2180
+ }
2181
+ model.add(defaultParent, newCell);
2182
+ idMapping[oldId] = newCell.getId();
2183
+ });
2184
+ importedCells.forEach((cell) => {
2185
+ const oldId = cell.getAttribute("id");
2186
+ const newId = idMapping[oldId];
2187
+ if (!newId) return;
2188
+ const newCell = model.getCell(newId);
2189
+ if (!newCell) return;
2190
+ const parentId = cell.getAttribute("parent");
2191
+ if (parentId && idMapping[parentId]) {
2192
+ const parentCell = model.getCell(idMapping[parentId]);
2193
+ if (parentCell) {
2194
+ model.add(parentCell, newCell);
2195
+ }
2196
+ }
2197
+ if (newCell.edge) {
2198
+ const sourceId = cell.getAttribute("source");
2199
+ const targetId = cell.getAttribute("target");
2200
+ if (sourceId && idMapping[sourceId]) {
2201
+ const sourceCell = model.getCell(idMapping[sourceId]);
2202
+ if (sourceCell) {
2203
+ model.setTerminal(newCell, sourceCell, true);
2204
+ }
2205
+ }
2206
+ if (targetId && idMapping[targetId]) {
2207
+ const targetCell = model.getCell(idMapping[targetId]);
2208
+ if (targetCell) {
2209
+ model.setTerminal(newCell, targetCell, false);
2210
+ }
2211
+ }
2212
+ }
2213
+ });
2214
+ }
2215
+ } finally {
2216
+ model.endUpdate();
2217
+ }
2218
+ return {
2219
+ success: true,
2220
+ message: `Diagram imported successfully (added to current diagram)${filename ? ` from ${filename}` : ""}`,
2221
+ cells: cellCount
2222
+ };
2223
+ case "new-page":
2224
+ if (!ui2.pages || !ui2.insertPage) {
2225
+ return {
2226
+ success: false,
2227
+ message: "New page creation not supported in this Draw.io version"
2228
+ };
2229
+ }
2230
+ const newPage = ui2.insertPage();
2231
+ if (!newPage) {
2232
+ return { success: false, message: "Failed to create new page" };
2233
+ }
2234
+ ui2.selectPage(newPage);
2235
+ model.beginUpdate();
2236
+ try {
2237
+ let pageGraphModel = doc.documentElement;
2238
+ if (pageGraphModel.nodeName === "mxfile") {
2239
+ const diagram = pageGraphModel.querySelector("diagram");
2240
+ if (diagram) {
2241
+ const mxGraphModel = diagram.querySelector("mxGraphModel");
2242
+ if (mxGraphModel) {
2243
+ pageGraphModel = mxGraphModel;
2244
+ }
2245
+ }
2246
+ }
2247
+ if (editor.setGraphXml) {
2248
+ editor.setGraphXml(pageGraphModel);
2249
+ graph.refresh();
2250
+ graph.fit();
2251
+ } else {
2252
+ const codec = new window.mxCodec(
2253
+ pageGraphModel.ownerDocument
2254
+ );
2255
+ codec.decode(pageGraphModel, model);
2256
+ }
2257
+ } finally {
2258
+ model.endUpdate();
2259
+ }
2260
+ if (filename && newPage.setName) {
2261
+ const pageName = filename.replace(/\.[^/.]+$/, "");
2262
+ newPage.setName(pageName);
2263
+ }
2264
+ return {
2265
+ success: true,
2266
+ message: `Diagram imported successfully (new page created)${filename ? `: ${filename}` : ""}`,
2267
+ pages: ui2.pages.length,
2268
+ cells: cellCount
2269
+ };
2270
+ default:
2271
+ return { success: false, message: `Unsupported import mode: ${mode}` };
2272
+ }
2273
+ } catch (error) {
2274
+ console.error("[import-diagram] Error importing diagram:", error);
2275
+ return {
2276
+ success: false,
2277
+ message: `Import failed: ${error instanceof Error ? error.message : "Unknown error"}`
2278
+ };
2279
+ }
2280
+ }
1537
2281
 
1538
- // src/plugin/plugin.ts
2282
+ // src/plugin.ts
1539
2283
  function reply_name(event_name, request_id) {
1540
2284
  return `${event_name}.${request_id}`;
1541
2285
  }
@@ -1551,31 +2295,42 @@ var DrawMcp = (() => {
1551
2295
  },
1552
2296
  {}
1553
2297
  );
1554
- let reply;
1555
- try {
1556
- const result = executeFunction(ui, options);
1557
- reply = {
2298
+ const sendReply = (result, success, error) => {
2299
+ const reply = {
1558
2300
  __event: reply_name(toolName, request.__request_id),
1559
2301
  __request_id: request.__request_id,
1560
- success: true,
1561
- result: remove_circular_dependencies(result)
2302
+ success,
2303
+ result: success ? remove_circular_dependencies(result) : void 0,
2304
+ error: !success ? remove_circular_dependencies(error) : void 0
1562
2305
  };
2306
+ if (wsManager) {
2307
+ wsManager.send(reply);
2308
+ }
2309
+ return reply;
2310
+ };
2311
+ try {
2312
+ const result = executeFunction(ui, options);
2313
+ if (result instanceof Promise) {
2314
+ result.then(
2315
+ (resolved) => sendReply(resolved, true),
2316
+ (err) => {
2317
+ console.error(
2318
+ `[plugin] Tool ${toolName} failed for request ID ${request.__request_id}:`,
2319
+ err
2320
+ );
2321
+ sendReply(void 0, false, err);
2322
+ }
2323
+ );
2324
+ return void 0;
2325
+ }
2326
+ return sendReply(result, true);
1563
2327
  } catch (error) {
1564
2328
  console.error(
1565
2329
  `[plugin] Tool ${toolName} failed for request ID ${request.__request_id}:`,
1566
2330
  error
1567
2331
  );
1568
- reply = {
1569
- __event: reply_name(toolName, request.__request_id),
1570
- __request_id: request.__request_id,
1571
- success: false,
1572
- error: remove_circular_dependencies(error)
1573
- };
1574
- }
1575
- if (wsManager) {
1576
- wsManager.send(reply);
2332
+ return sendReply(void 0, false, error);
1577
2333
  }
1578
- return reply;
1579
2334
  };
1580
2335
  };
1581
2336
  var ui;
@@ -1591,7 +2346,7 @@ var DrawMcp = (() => {
1591
2346
  },
1592
2347
  {
1593
2348
  name: "add-rectangle",
1594
- params: /* @__PURE__ */ new Set(["x", "y", "width", "height", "text", "style"]),
2349
+ params: /* @__PURE__ */ new Set(["x", "y", "width", "height", "text", "style", "parent_id"]),
1595
2350
  handler: add_new_rectangle
1596
2351
  },
1597
2352
  {
@@ -1601,7 +2356,7 @@ var DrawMcp = (() => {
1601
2356
  },
1602
2357
  {
1603
2358
  name: "add-edge",
1604
- params: /* @__PURE__ */ new Set(["source_id", "target_id", "style", "text"]),
2359
+ params: /* @__PURE__ */ new Set(["source_id", "target_id", "style", "text", "parent_id", "points"]),
1605
2360
  handler: add_edge
1606
2361
  },
1607
2362
  {
@@ -1621,7 +2376,16 @@ var DrawMcp = (() => {
1621
2376
  },
1622
2377
  {
1623
2378
  name: "add-cell-of-shape",
1624
- params: /* @__PURE__ */ new Set(["x", "y", "width", "height", "text", "style"]),
2379
+ params: /* @__PURE__ */ new Set([
2380
+ "shape_name",
2381
+ "x",
2382
+ "y",
2383
+ "width",
2384
+ "height",
2385
+ "text",
2386
+ "style",
2387
+ "parent_id"
2388
+ ]),
1625
2389
  handler: add_cell_of_shape
1626
2390
  },
1627
2391
  {
@@ -1633,8 +2397,7 @@ var DrawMcp = (() => {
1633
2397
  name: "set-cell-data",
1634
2398
  params: /* @__PURE__ */ new Set(["cell_id", "key", "value"]),
1635
2399
  handler: (ui2, options) => {
1636
- const mxUtils = window.mxUtils;
1637
- return set_cell_data(mxUtils)(ui2, options);
2400
+ return set_cell_data(ui2, options);
1638
2401
  }
1639
2402
  },
1640
2403
  {
@@ -1649,7 +2412,7 @@ var DrawMcp = (() => {
1649
2412
  },
1650
2413
  {
1651
2414
  name: "edit-edge",
1652
- params: /* @__PURE__ */ new Set(["cell_id", "text", "source_id", "target_id", "style"]),
2415
+ params: /* @__PURE__ */ new Set(["cell_id", "text", "source_id", "target_id", "style", "points"]),
1653
2416
  handler: edit_edge
1654
2417
  },
1655
2418
  {
@@ -1667,6 +2430,11 @@ var DrawMcp = (() => {
1667
2430
  params: /* @__PURE__ */ new Set(["cell_id", "target_layer_id"]),
1668
2431
  handler: move_cell_to_layer
1669
2432
  },
2433
+ {
2434
+ name: "set-cell-parent",
2435
+ params: /* @__PURE__ */ new Set(["cell_id", "parent_id"]),
2436
+ handler: set_cell_parent
2437
+ },
1670
2438
  {
1671
2439
  name: "get-active-layer",
1672
2440
  params: /* @__PURE__ */ new Set([]),
@@ -1676,6 +2444,28 @@ var DrawMcp = (() => {
1676
2444
  name: "create-layer",
1677
2445
  params: /* @__PURE__ */ new Set(["name"]),
1678
2446
  handler: create_layer
2447
+ },
2448
+ {
2449
+ name: "export-diagram",
2450
+ params: /* @__PURE__ */ new Set([
2451
+ "format",
2452
+ "scale",
2453
+ "border",
2454
+ "background",
2455
+ "shadow",
2456
+ "crop",
2457
+ "selection_only",
2458
+ "transparent",
2459
+ "dpi",
2460
+ "embed_xml",
2461
+ "size"
2462
+ ]),
2463
+ handler: export_diagram
2464
+ },
2465
+ {
2466
+ name: "import-diagram",
2467
+ params: /* @__PURE__ */ new Set(["data", "format", "mode", "filename"]),
2468
+ handler: import_diagram
1679
2469
  }
1680
2470
  ];
1681
2471
  var toolHandlers = /* @__PURE__ */ new Map();
@@ -1803,6 +2593,9 @@ var DrawMcp = (() => {
1803
2593
  window.Draw.loadPlugin((drawioUI) => {
1804
2594
  console.debug("[plugin] Plugin loaded successfully");
1805
2595
  ui = drawioUI;
2596
+ if (window.__DRAWIO_MCP_TEST_HOOKS__ === true) {
2597
+ window.ui = drawioUI;
2598
+ }
1806
2599
  toolDefinitions.forEach((def) => {
1807
2600
  const handler = createToolHandler(def.name, def.params, def.handler);
1808
2601
  toolHandlers.set(def.name, handler);