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.
Files changed (71) hide show
  1. package/README.md +29 -3
  2. package/build/assets/downloader.js +16 -17
  3. package/build/config.js +253 -5
  4. package/build/config.test.js +449 -1
  5. package/build/emitter_bus.js +3 -0
  6. package/build/emitter_bus.test.js +7 -0
  7. package/build/index.capabilities.test.js +45 -0
  8. package/build/index.js +496 -106
  9. package/build/install-desktop-plugin.js +56 -0
  10. package/build/install-desktop-plugin.test.js +66 -0
  11. package/build/multi-transport.test.js +277 -0
  12. package/build/plugin/mcp-plugin.js +2180 -1105
  13. package/build/prefetch-assets.js +6 -5
  14. package/build/real-environment/document-targeting.test.js +124 -0
  15. package/build/real-environment/export-diagram.test.js +473 -0
  16. package/build/real-environment/harness.js +120 -32
  17. package/build/real-environment/import-mermaid.test.js +81 -0
  18. package/build/real-environment/pages-and-concurrency.test.js +448 -0
  19. package/build/real-environment/shapes.test.js +58 -0
  20. package/build/real-environment/tools.js +51 -6
  21. package/build/register-tool.js +31 -0
  22. package/build/request_queue.js +29 -0
  23. package/build/request_queue.test.js +98 -0
  24. package/build/stdio-transport-purity.test.js +88 -0
  25. package/build/strip-schema.js +61 -0
  26. package/build/tls/expiry.js +14 -0
  27. package/build/tls/expiry.test.js +54 -0
  28. package/build/tls/generate.js +123 -0
  29. package/build/tls/generate.test.js +115 -0
  30. package/build/tls/index.js +80 -0
  31. package/build/tls/index.test.js +141 -0
  32. package/build/tls/install-hint.js +45 -0
  33. package/build/tls/install-hint.test.js +32 -0
  34. package/build/tls/load.js +18 -0
  35. package/build/tls/load.test.js +40 -0
  36. package/build/tls/paths.js +30 -0
  37. package/build/tls/paths.test.js +53 -0
  38. package/build/tls/san.js +27 -0
  39. package/build/tls/san.test.js +72 -0
  40. package/build/tool-registry.test.js +823 -0
  41. package/build/tool.js +74 -15
  42. package/build/tool.test.js +250 -7
  43. package/build/tools/add-cell-of-shape.js +4 -2
  44. package/build/tools/add-edge.js +3 -1
  45. package/build/tools/add-rectangle.js +4 -2
  46. package/build/tools/copy-page.js +13 -0
  47. package/build/tools/create-layer.js +4 -2
  48. package/build/tools/create-page.js +8 -0
  49. package/build/tools/delete-cell-by-id.js +4 -2
  50. package/build/tools/edit-cell.js +3 -1
  51. package/build/tools/edit-edge.js +3 -1
  52. package/build/tools/export-diagram.js +7 -3
  53. package/build/tools/get-active-layer.js +4 -1
  54. package/build/tools/get-current-page.js +5 -0
  55. package/build/tools/get-selected-cell.js +4 -1
  56. package/build/tools/import-diagram.js +12 -2
  57. package/build/tools/import-mermaid.js +31 -0
  58. package/build/tools/index.js +14 -0
  59. package/build/tools/list-documents.js +17 -0
  60. package/build/tools/list-layers.js +4 -1
  61. package/build/tools/list-paged-model.js +4 -3
  62. package/build/tools/list-pages.js +5 -0
  63. package/build/tools/move-cell-to-layer.js +3 -1
  64. package/build/tools/rename-page.js +10 -0
  65. package/build/tools/set-active-layer.js +4 -2
  66. package/build/tools/set-cell-data.js +3 -1
  67. package/build/tools/set-cell-parent.js +3 -1
  68. package/build/tools/set-cell-shape.js +3 -1
  69. package/build/tools/shared.js +35 -0
  70. package/build/tools/shared.test.js +29 -0
  71. package/package.json +22 -14
@@ -1,5 +1,8 @@
1
1
  import { default_tool } from "../tool.js";
2
+ import { target_page_field } from "./shared.js";
2
3
  export const TOOL_get_selected_cell = "get-selected-cell";
3
4
  export const registerGetSelectedCellTool = (server, context) => {
4
- server.tool(TOOL_get_selected_cell, "This tool allows you to retrieve selected cell (whether vertex or edge) on the current page of a Draw.io diagram. The response is a JSON containing attributes of the cell.", {}, default_tool(TOOL_get_selected_cell, context));
5
+ server.tool(TOOL_get_selected_cell, "This tool retrieves the selected cell on the target page of the current Draw.io document. If the target page is not currently visible, Draw.io may switch the visible page first because selection is UI-bound.", {
6
+ target_page: target_page_field(),
7
+ }, default_tool(TOOL_get_selected_cell, context, { queue: true }));
5
8
  };
@@ -1,8 +1,10 @@
1
1
  import { z } from "zod";
2
2
  import { default_tool } from "../tool.js";
3
+ import { target_page_field } from "./shared.js";
3
4
  export const TOOL_import_diagram = "import-diagram";
4
5
  export const registerImportDiagramTool = (server, context) => {
5
- server.tool(TOOL_import_diagram, "Import a diagram from XML, SVG with embedded XML, or PNG with embedded XML into the current Draw.io instance.", {
6
+ server.tool(TOOL_import_diagram, "Import a diagram from XML, SVG with embedded XML, or PNG with embedded XML into the current Draw.io instance. `target_page` is required for `replace` and `add`, and optional for `new-page`.", {
7
+ target_page: target_page_field().optional(),
6
8
  data: z
7
9
  .string()
8
10
  .describe("The diagram data: raw XML string, or base64-encoded SVG/PNG with embedded XML"),
@@ -18,5 +20,13 @@ export const registerImportDiagramTool = (server, context) => {
18
20
  .string()
19
21
  .optional()
20
22
  .describe("Optional original filename for context"),
21
- }, default_tool(TOOL_import_diagram, context));
23
+ }, async (args, extra) => {
24
+ if (args.mode !== "new-page" && !args.target_page) {
25
+ throw new Error("`target_page` is required when import-diagram mode is `replace` or `add`");
26
+ }
27
+ const handler = default_tool(TOOL_import_diagram, context, {
28
+ queue: true,
29
+ });
30
+ return handler(args, extra);
31
+ });
22
32
  };
@@ -0,0 +1,31 @@
1
+ import { z } from "zod";
2
+ import { default_tool } from "../tool.js";
3
+ import { target_page_field } from "./shared.js";
4
+ export const TOOL_import_mermaid = "import-mermaid";
5
+ export const registerImportMermaidTool = (server, context) => {
6
+ server.tool(TOOL_import_mermaid, "Insert a Mermaid diagram into the target Draw.io page. Conversion runs inside the Draw.io editor via its bundled Mermaid pipeline (EditorUi.parseMermaidDiagram). mode='native' converts supported Mermaid types into native mxGraph cells; unsupported types fall back to an embedded image cell. mode='embed' always emits a single image cell that preserves the Mermaid source for re-editing inside Draw.io. `target_page` is required for `replace` and `add`, and optional for `new-page`.", {
7
+ target_page: target_page_field().optional(),
8
+ mermaid_source: z
9
+ .string()
10
+ .min(1)
11
+ .describe("Raw Mermaid syntax. Examples: 'graph TD; A-->B;', 'sequenceDiagram\\nA->>B: hi'."),
12
+ mode: z
13
+ .enum(["native", "embed"])
14
+ .optional()
15
+ .default("native")
16
+ .describe("native = convert to native mxGraph cells when the diagram type is supported (best editability). embed = single image cell with mermaidData attribute for round-trip Mermaid editing in Draw.io."),
17
+ insert_mode: z
18
+ .enum(["replace", "add", "new-page"])
19
+ .optional()
20
+ .default("add")
21
+ .describe("How the resulting XML is merged into the diagram: replace, add, or new-page."),
22
+ }, async (args, extra) => {
23
+ if (args.insert_mode !== "new-page" && !args.target_page) {
24
+ throw new Error("`target_page` is required when import-mermaid insert_mode is `replace` or `add`");
25
+ }
26
+ const handler = default_tool(TOOL_import_mermaid, context, {
27
+ queue: true,
28
+ });
29
+ return handler(args, extra);
30
+ });
31
+ };
@@ -1,20 +1,27 @@
1
1
  import { registerAddCellOfShapeTool } from "./add-cell-of-shape.js";
2
2
  import { registerAddEdgeTool } from "./add-edge.js";
3
3
  import { registerAddRectangleTool } from "./add-rectangle.js";
4
+ import { registerCopyPageTool } from "./copy-page.js";
4
5
  import { registerCreateLayerTool } from "./create-layer.js";
6
+ import { registerCreatePageTool } from "./create-page.js";
5
7
  import { registerDeleteCellByIdTool } from "./delete-cell-by-id.js";
6
8
  import { registerEditCellTool } from "./edit-cell.js";
7
9
  import { registerEditEdgeTool } from "./edit-edge.js";
8
10
  import { registerExportDiagramTool } from "./export-diagram.js";
9
11
  import { registerGetActiveLayerTool } from "./get-active-layer.js";
12
+ import { registerGetCurrentPageTool } from "./get-current-page.js";
10
13
  import { registerGetSelectedCellTool } from "./get-selected-cell.js";
11
14
  import { registerGetShapeByNameTool } from "./get-shape-by-name.js";
12
15
  import { registerGetShapeCategoriesTool } from "./get-shape-categories.js";
13
16
  import { registerGetShapesInCategoryTool } from "./get-shapes-in-category.js";
14
17
  import { registerImportDiagramTool } from "./import-diagram.js";
18
+ import { registerImportMermaidTool } from "./import-mermaid.js";
15
19
  import { registerListLayersTool } from "./list-layers.js";
20
+ import { registerListDocumentsTool } from "./list-documents.js";
21
+ import { registerListPagesTool } from "./list-pages.js";
16
22
  import { registerListPagedModelTool } from "./list-paged-model.js";
17
23
  import { registerMoveCellToLayerTool } from "./move-cell-to-layer.js";
24
+ import { registerRenamePageTool } from "./rename-page.js";
18
25
  import { registerSetActiveLayerTool } from "./set-active-layer.js";
19
26
  import { registerSetCellDataTool } from "./set-cell-data.js";
20
27
  import { registerSetCellParentTool } from "./set-cell-parent.js";
@@ -41,6 +48,13 @@ const registrars = [
41
48
  registerCreateLayerTool,
42
49
  registerExportDiagramTool,
43
50
  registerImportDiagramTool,
51
+ registerImportMermaidTool,
52
+ registerListDocumentsTool,
53
+ registerListPagesTool,
54
+ registerGetCurrentPageTool,
55
+ registerCreatePageTool,
56
+ registerCopyPageTool,
57
+ registerRenamePageTool,
44
58
  ];
45
59
  export function registerTools(...args) {
46
60
  for (const register of registrars) {
@@ -0,0 +1,17 @@
1
+ export const TOOL_list_documents = "list-documents";
2
+ export const registerListDocumentsTool = (server, context) => {
3
+ server.tool(TOOL_list_documents, "Lists all connected Draw.io document instances with their stable instance IDs and basic metadata.", {}, async () => {
4
+ const documents = await context.document_routing.list_documents();
5
+ return {
6
+ content: [
7
+ {
8
+ type: "text",
9
+ text: JSON.stringify({
10
+ success: true,
11
+ result: documents,
12
+ }),
13
+ },
14
+ ],
15
+ };
16
+ });
17
+ };
@@ -1,5 +1,8 @@
1
1
  import { default_tool } from "../tool.js";
2
+ import { target_page_field } from "./shared.js";
2
3
  export const TOOL_list_layers = "list-layers";
3
4
  export const registerListLayersTool = (server, context) => {
4
- server.tool(TOOL_list_layers, "Lists all available layers in the diagram with their IDs and names.", {}, default_tool(TOOL_list_layers, context));
5
+ server.tool(TOOL_list_layers, "Lists all available layers on the target page with their IDs and names.", {
6
+ target_page: target_page_field(),
7
+ }, default_tool(TOOL_list_layers, context, { queue: true }));
5
8
  };
@@ -1,9 +1,10 @@
1
1
  import { z } from "zod";
2
2
  import { default_tool } from "../tool.js";
3
- import { Attributes } from "./shared.js";
3
+ import { Attributes, target_page_field } from "./shared.js";
4
4
  export const TOOL_list_paged_model = "list-paged-model";
5
5
  export const registerListPagedModelTool = (server, context) => {
6
- server.tool(TOOL_list_paged_model, "Retrieves a paginated view of all cells (vertices and edges) in the current Draw.io diagram. This tool provides access to the complete model data with essential fields only, sanitized to remove circular dependencies and excessive data. It allows to filter based on multiple criteria and attribute boolean logic. Useful for programmatic inspection of diagram structure without overwhelming response sizes.", {
6
+ server.tool(TOOL_list_paged_model, "Retrieves a paginated view of all cells (vertices and edges) on the target page. This tool provides access to the complete model data with essential fields only, sanitized to remove circular dependencies and excessive data.", {
7
+ target_page: target_page_field(),
7
8
  page: z
8
9
  .number()
9
10
  .optional()
@@ -37,5 +38,5 @@ export const registerListPagedModelTool = (server, context) => {
37
38
  .optional()
38
39
  .describe("Optional filter criteria to apply to cells before pagination")
39
40
  .default({}),
40
- }, default_tool(TOOL_list_paged_model, context));
41
+ }, default_tool(TOOL_list_paged_model, context, { queue: true }));
41
42
  };
@@ -0,0 +1,5 @@
1
+ import { default_tool } from "../tool.js";
2
+ export const TOOL_list_pages = "list-pages";
3
+ export const registerListPagesTool = (server, context) => {
4
+ server.tool(TOOL_list_pages, "Lists all pages in the current Draw.io document with their index, id, name, and current-page flag.", {}, default_tool(TOOL_list_pages, context, { queue: true }));
5
+ };
@@ -1,11 +1,13 @@
1
1
  import { z } from "zod";
2
2
  import { default_tool } from "../tool.js";
3
+ import { target_page_field } from "./shared.js";
3
4
  export const TOOL_move_cell_to_layer = "move-cell-to-layer";
4
5
  export const registerMoveCellToLayerTool = (server, context) => {
5
6
  server.tool(TOOL_move_cell_to_layer, "Moves a cell from its current layer to a target layer.", {
7
+ target_page: target_page_field(),
6
8
  cell_id: z.string().describe("ID of the cell to move"),
7
9
  target_layer_id: z
8
10
  .string()
9
11
  .describe("ID of the target layer where the cell will be moved"),
10
- }, default_tool(TOOL_move_cell_to_layer, context));
12
+ }, default_tool(TOOL_move_cell_to_layer, context, { queue: true }));
11
13
  };
@@ -0,0 +1,10 @@
1
+ import { z } from "zod";
2
+ import { default_tool } from "../tool.js";
3
+ import { target_page_field } from "./shared.js";
4
+ export const TOOL_rename_page = "rename-page";
5
+ export const registerRenamePageTool = (server, context) => {
6
+ server.tool(TOOL_rename_page, "Renames the selected page without changing the visible page.", {
7
+ page: target_page_field().describe("Page selector for the page to rename. Provide exactly one of `{ index }` or `{ id }`."),
8
+ name: z.string().describe("New page name"),
9
+ }, default_tool(TOOL_rename_page, context, { queue: true }));
10
+ };
@@ -1,8 +1,10 @@
1
1
  import { z } from "zod";
2
2
  import { default_tool } from "../tool.js";
3
+ import { target_page_field } from "./shared.js";
3
4
  export const TOOL_set_active_layer = "set-active-layer";
4
5
  export const registerSetActiveLayerTool = (server, context) => {
5
- server.tool(TOOL_set_active_layer, "Sets the active layer for creating new elements. All subsequent element creation will happen in this layer.", {
6
+ server.tool(TOOL_set_active_layer, "Sets the active layer for creating new elements. All subsequent element creation will happen in this layer. If the target page is not currently visible, Draw.io may switch the visible page first because active-layer state is UI-bound.", {
7
+ target_page: target_page_field(),
6
8
  layer_id: z.string().describe("ID of the layer to set as active"),
7
- }, default_tool(TOOL_set_active_layer, context));
9
+ }, default_tool(TOOL_set_active_layer, context, { queue: true }));
8
10
  };
@@ -1,8 +1,10 @@
1
1
  import { z } from "zod";
2
2
  import { default_tool } from "../tool.js";
3
+ import { target_page_field } from "./shared.js";
3
4
  export const TOOL_set_cell_data = "set-cell-data";
4
5
  export const registerSetCellDataTool = (server, context) => {
5
6
  server.tool(TOOL_set_cell_data, "Sets or updates a custom attribute on an existing cell.", {
7
+ target_page: target_page_field(),
6
8
  cell_id: z
7
9
  .string()
8
10
  .describe("Identifier (`id` attribute) of the cell to update with custom data."),
@@ -10,5 +12,5 @@ export const registerSetCellDataTool = (server, context) => {
10
12
  value: z
11
13
  .union([z.string(), z.number(), z.boolean()])
12
14
  .describe("Value to store for the attribute. Non-string values are stringified before storage."),
13
- }, default_tool(TOOL_set_cell_data, context));
15
+ }, default_tool(TOOL_set_cell_data, context, { queue: true }));
14
16
  };
@@ -1,9 +1,11 @@
1
1
  import { z } from "zod";
2
2
  import { default_tool } from "../tool.js";
3
+ import { target_page_field } from "./shared.js";
3
4
  export const TOOL_set_cell_parent = "set-cell-parent";
4
5
  export const registerSetCellParentTool = (server, context) => {
5
6
  server.tool(TOOL_set_cell_parent, "Sets the parent of a cell, making it a child of the specified parent cell. This allows creating hierarchical relationships where moving the parent also moves its children.", {
7
+ target_page: target_page_field(),
6
8
  cell_id: z.string().describe("ID of the cell to reparent"),
7
9
  parent_id: z.string().describe("ID of the new parent cell"),
8
- }, default_tool(TOOL_set_cell_parent, context));
10
+ }, default_tool(TOOL_set_cell_parent, context, { queue: true }));
9
11
  };
@@ -1,13 +1,15 @@
1
1
  import { z } from "zod";
2
2
  import { default_tool } from "../tool.js";
3
+ import { target_page_field } from "./shared.js";
3
4
  export const TOOL_set_cell_shape = "set-cell-shape";
4
5
  export const registerSetCellShapeTool = (server, context) => {
5
6
  server.tool(TOOL_set_cell_shape, "Updates the visual style of an existing vertex cell to match a library shape by name.", {
7
+ target_page: target_page_field(),
6
8
  cell_id: z
7
9
  .string()
8
10
  .describe("Identifier (`id` attribute) of the cell whose shape should change."),
9
11
  shape_name: z
10
12
  .string()
11
13
  .describe("Name of the library shape whose style should be applied to the existing cell."),
12
- }, default_tool(TOOL_set_cell_shape, context));
14
+ }, default_tool(TOOL_set_cell_shape, context, { queue: true }));
13
15
  };
@@ -5,3 +5,38 @@ export const Attributes = z.lazy(() => z
5
5
  message: "If not empty, the first element must be a string operator",
6
6
  })
7
7
  .default([]));
8
+ const BaseTargetPageSchema = z
9
+ .object({
10
+ index: z
11
+ .number()
12
+ .int()
13
+ .nonnegative()
14
+ .optional()
15
+ .describe("Zero-based page index in the current Draw.io document."),
16
+ id: z
17
+ .string()
18
+ .optional()
19
+ .describe("Stable Draw.io page identifier from `list-pages`."),
20
+ })
21
+ .superRefine((value, ctx) => {
22
+ const hasIndex = value.index !== undefined;
23
+ const hasId = value.id !== undefined && value.id.length > 0;
24
+ if (hasIndex === hasId) {
25
+ ctx.addIssue({
26
+ code: z.ZodIssueCode.custom,
27
+ message: "Provide exactly one of `index` or `id`.",
28
+ });
29
+ }
30
+ });
31
+ const BaseTargetDocumentSchema = z.object({
32
+ id: z
33
+ .string()
34
+ .min(1)
35
+ .describe("Stable Draw.io document instance identifier from `list-documents`."),
36
+ });
37
+ export function target_page_field() {
38
+ return BaseTargetPageSchema.describe("Target page selector. Provide exactly one of `{ index }` or `{ id }` from `list-pages`.");
39
+ }
40
+ export function target_document_field() {
41
+ return BaseTargetDocumentSchema.describe("Target document selector. Provide `{ id }` from `list-documents`.");
42
+ }
@@ -0,0 +1,29 @@
1
+ import { describe, expect, it } from "@jest/globals";
2
+ import { target_document_field, target_page_field } from "./shared.js";
3
+ describe("target_page schema", () => {
4
+ const schema = target_page_field();
5
+ it("accepts an index selector", () => {
6
+ expect(schema.safeParse({ index: 0 }).success).toBe(true);
7
+ });
8
+ it("accepts an id selector", () => {
9
+ expect(schema.safeParse({ id: "page-123" }).success).toBe(true);
10
+ });
11
+ it("rejects missing selector fields", () => {
12
+ const result = schema.safeParse({});
13
+ expect(result.success).toBe(false);
14
+ });
15
+ it("rejects providing both index and id", () => {
16
+ const result = schema.safeParse({ index: 1, id: "page-123" });
17
+ expect(result.success).toBe(false);
18
+ });
19
+ });
20
+ describe("target_document schema", () => {
21
+ const schema = target_document_field();
22
+ it("accepts a document id selector", () => {
23
+ expect(schema.safeParse({ id: "doc-123" }).success).toBe(true);
24
+ });
25
+ it("rejects an empty document id", () => {
26
+ const result = schema.safeParse({ id: "" });
27
+ expect(result.success).toBe(false);
28
+ });
29
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drawio-mcp-server",
3
- "version": "2.0.3",
3
+ "version": "2.1.0",
4
4
  "description": "Provides Draw.io services to MCP Clients",
5
5
  "type": "module",
6
6
  "main": "build/index.js",
@@ -31,42 +31,50 @@
31
31
  "url": "git+https://github.com/lgazo/drawio-mcp-server.git"
32
32
  },
33
33
  "dependencies": {
34
- "@hono/node-server": "1.19.7",
35
- "@modelcontextprotocol/sdk": "1.25.1",
36
- "cachedir": "^2.4.0",
37
- "hono": "4.11.1",
34
+ "@hono/node-server": "1.19.13",
35
+ "@modelcontextprotocol/sdk": "1.29.0",
36
+ "cachedir": "2.4.0",
37
+ "hono": "4.12.14",
38
38
  "nanoid": "5.1.6",
39
- "unzipper": "^0.12.0",
39
+ "node-forge": "1.4.0",
40
+ "unzipper": "0.12.3",
40
41
  "ws": "8.18.3",
41
42
  "zod": "4.2.1"
42
43
  },
43
44
  "devDependencies": {
45
+ "@biomejs/biome": "2.4.13",
44
46
  "@jest/globals": "30.2.0",
45
- "@playwright/test": "1.54.2",
47
+ "@playwright/test": "1.59.1",
46
48
  "@types/jest": "30.0.0",
47
49
  "@types/node": "25.0.3",
48
- "@types/unzipper": "^0.10.11",
49
- "@types/ws": "^8.18.1",
50
- "esbuild": "^0.24.0",
50
+ "@types/node-forge": "1.3.11",
51
+ "@types/unzipper": "0.10.11",
52
+ "@types/ws": "8.18.1",
53
+ "concurrently": "9.1.2",
54
+ "esbuild": "0.25.12",
51
55
  "globals": "16.5.0",
52
56
  "jest": "30.2.0",
53
57
  "jest-environment-node": "30.2.0",
54
58
  "prettier": "3.7.4",
55
- "rimraf": "6.1.2",
56
- "ts-jest": "29.4.6",
59
+ "rimraf": "6.1.3",
60
+ "ts-jest": "29.4.9",
57
61
  "typescript": "5.9.3",
58
- "drawio-mcp-plugin": "2.0.0"
62
+ "drawio-mcp-dev-proxy": "1.0.0",
63
+ "drawio-mcp-plugin": "2.1.0"
59
64
  },
60
65
  "scripts": {
61
66
  "build": "rimraf build && tsc && mkdir -p build/plugin && cp node_modules/drawio-mcp-plugin/dist/mcp-plugin.js build/plugin/mcp-plugin.js",
62
67
  "ci": "pnpm install --frozen-lockfile",
63
68
  "dev": "tsc --watch",
69
+ "dev:server": "concurrently --kill-others-on-fail -n tsc,node -c cyan,green \"tsc --watch --preserveWatchOutput\" \"node --watch --watch-path=build build/index.js\"",
64
70
  "inspect": "HOST=0.0.0.0 pnpx @modelcontextprotocol/inspector --config ./inspector.json --server drawio",
65
- "lint": "tsc --noEmit",
71
+ "lint": "biome check src/ && tsc --noEmit",
72
+ "lint:fix": "biome check --write src/",
66
73
  "prefetch-assets": "node build/prefetch-assets.js",
67
74
  "format": "prettier --write \"**/*.ts\"",
68
75
  "format:check": "prettier --check \"**/*.ts\"",
69
76
  "test:real-environment": "NODE_OPTIONS=--experimental-vm-modules jest build/real-environment --runInBand",
77
+ "test:real-environment:https": "HARNESS_HTTPS=1 NODE_OPTIONS=--experimental-vm-modules jest build/real-environment --runInBand",
70
78
  "test": "NODE_OPTIONS=--experimental-vm-modules jest build/",
71
79
  "test:watch": "jest --watch",
72
80
  "test:coverage": "jest --config ../../jest.config.js --coverage",