@io.github.hj1003862396/draw-flow-mcp-server 1.0.1

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 ADDED
@@ -0,0 +1,208 @@
1
+ # Next AI Draw.io MCP Server
2
+
3
+ MCP (Model Context Protocol) server that enables AI agents like Claude Desktop and Cursor to generate and edit draw.io diagrams with **real-time browser preview**.
4
+
5
+ **Self-contained** - includes an embedded HTTP server, no external dependencies required.
6
+
7
+ ## Quick Start
8
+
9
+ ```json
10
+ {
11
+ "mcpServers": {
12
+ "drawio": {
13
+ "command": "npx",
14
+ "args": ["@next-ai-drawio/mcp-server@latest"]
15
+ }
16
+ }
17
+ }
18
+ ```
19
+
20
+ ## Installation
21
+
22
+ ### Claude Desktop
23
+
24
+ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
25
+
26
+ ```json
27
+ {
28
+ "mcpServers": {
29
+ "drawio": {
30
+ "command": "npx",
31
+ "args": ["@next-ai-drawio/mcp-server@latest"]
32
+ }
33
+ }
34
+ }
35
+ ```
36
+
37
+ ### VS Code
38
+
39
+ Add to your VS Code settings (`.vscode/mcp.json` in workspace or user settings):
40
+
41
+ ```json
42
+ {
43
+ "mcpServers": {
44
+ "drawio": {
45
+ "command": "npx",
46
+ "args": ["@next-ai-drawio/mcp-server@latest"]
47
+ }
48
+ }
49
+ }
50
+ ```
51
+
52
+ ### Cursor
53
+
54
+ Add to Cursor MCP config (`~/.cursor/mcp.json`):
55
+
56
+ ```json
57
+ {
58
+ "mcpServers": {
59
+ "drawio": {
60
+ "command": "npx",
61
+ "args": ["@next-ai-drawio/mcp-server@latest"]
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ ### Cline (VS Code Extension)
68
+
69
+ 1. Click the **MCP Servers** icon in Cline's top menu bar
70
+ 2. Select the **Configure** tab
71
+ 3. Click **Configure MCP Servers** to edit `cline_mcp_settings.json`
72
+ 4. Add the drawio server:
73
+
74
+ ```json
75
+ {
76
+ "mcpServers": {
77
+ "drawio": {
78
+ "command": "npx",
79
+ "args": ["@next-ai-drawio/mcp-server@latest"]
80
+ }
81
+ }
82
+ }
83
+ ```
84
+
85
+ ### Claude Code CLI
86
+
87
+ ```bash
88
+ claude mcp add drawio -- npx @next-ai-drawio/mcp-server@latest
89
+ ```
90
+
91
+ ### Other MCP Clients
92
+
93
+ Use the standard MCP configuration with:
94
+ - **Command**: `npx`
95
+ - **Args**: `["@next-ai-drawio/mcp-server@latest"]`
96
+
97
+ ## Usage
98
+
99
+ 1. Restart your MCP client after updating config
100
+ 2. Ask the AI to create a diagram:
101
+ > "Create a flowchart showing user authentication with login, MFA, and session management"
102
+ 3. The diagram appears in your browser in real-time!
103
+
104
+ ## Features
105
+
106
+ - **Real-time Preview**: Diagrams appear and update in your browser as the AI creates them
107
+ - **Version History**: Restore previous diagram versions with visual thumbnails - click the clock button (bottom-right) to browse and restore earlier states
108
+ - **Natural Language**: Describe diagrams in plain text - flowcharts, architecture diagrams, etc.
109
+ - **Edit Support**: Modify existing diagrams with natural language instructions
110
+ - **Export**: Save diagrams as `.drawio` files
111
+ - **Self-contained**: Embedded server, works offline (except draw.io UI which loads from `embed.diagrams.net` by default, configurable via `DRAWIO_BASE_URL`)
112
+
113
+ ## Available Tools
114
+
115
+ | Tool | Description |
116
+ |------|-------------|
117
+ | `start_session` | Opens browser with real-time diagram preview |
118
+ | `create_new_diagram` | Create a new diagram from XML (requires `xml` argument) |
119
+ | `edit_diagram` | Edit diagram by ID-based operations (update/add/delete cells) |
120
+ | `get_diagram` | Get the current diagram XML |
121
+ | `export_diagram` | Save diagram to a `.drawio` file |
122
+
123
+ ## How It Works
124
+
125
+ ```
126
+ ┌─────────────────┐ stdio ┌─────────────────┐
127
+ │ Claude Desktop │ <───────────> │ MCP Server │
128
+ │ (AI Agent) │ │ (this package) │
129
+ └─────────────────┘ └────────┬────────┘
130
+
131
+ ┌────────▼────────┐
132
+ │ Embedded HTTP │
133
+ │ Server (:6002) │
134
+ └────────┬────────┘
135
+
136
+ ┌────────▼────────┐
137
+ │ User's Browser │
138
+ │ (draw.io embed) │
139
+ └─────────────────┘
140
+ ```
141
+
142
+ 1. **MCP Server** receives tool calls from Claude via stdio
143
+ 2. **Embedded HTTP Server** serves the draw.io UI and handles state
144
+ 3. **Browser** shows real-time diagram updates via polling
145
+
146
+ ## Configuration
147
+
148
+ | Variable | Default | Description |
149
+ |----------|---------|-------------|
150
+ | `PORT` | `6002` | Port for the embedded HTTP server |
151
+ | `DRAWIO_BASE_URL` | `https://embed.diagrams.net` | Base URL for the draw.io embed. Set this to use a self-hosted draw.io instance for private deployments. |
152
+
153
+ ### Private Deployment (Self-hosted draw.io)
154
+
155
+ For security-sensitive environments that require private deployment of draw.io:
156
+
157
+ ```json
158
+ {
159
+ "mcpServers": {
160
+ "drawio": {
161
+ "command": "npx",
162
+ "args": ["@next-ai-drawio/mcp-server@latest"],
163
+ "env": {
164
+ "DRAWIO_BASE_URL": "https://drawio.your-company.com"
165
+ }
166
+ }
167
+ }
168
+ }
169
+ ```
170
+
171
+ You can deploy your own draw.io instance using the official Docker image:
172
+
173
+ ```bash
174
+ docker run -d -p 8080:8080 jgraph/drawio
175
+ ```
176
+
177
+ Then set `DRAWIO_BASE_URL=http://localhost:8080` (or your server's URL).
178
+
179
+ ## Troubleshooting
180
+
181
+ ### Port already in use
182
+
183
+ If port 6002 is in use, the server will automatically try the next available port (up to 6020).
184
+
185
+ Or set a custom port:
186
+ ```json
187
+ {
188
+ "mcpServers": {
189
+ "drawio": {
190
+ "command": "npx",
191
+ "args": ["@next-ai-drawio/mcp-server@latest"],
192
+ "env": { "PORT": "6003" }
193
+ }
194
+ }
195
+ }
196
+ ```
197
+
198
+ ### "No active session"
199
+
200
+ Call `start_session` first to open the browser window.
201
+
202
+ ### Browser not updating
203
+
204
+ Check that the browser URL has the `?mcp=` query parameter. The MCP session ID connects the browser to the server.
205
+
206
+ ## License
207
+
208
+ Apache-2.0
@@ -0,0 +1,28 @@
1
+ /**
2
+ * ID-based diagram operations
3
+ * Copied from lib/utils.ts to avoid cross-package imports
4
+ */
5
+ export interface DiagramOperation {
6
+ operation: "update" | "add" | "delete";
7
+ cell_id: string;
8
+ new_xml?: string;
9
+ }
10
+ export interface OperationError {
11
+ type: "update" | "add" | "delete";
12
+ cellId: string;
13
+ message: string;
14
+ }
15
+ export interface ApplyOperationsResult {
16
+ result: string;
17
+ errors: OperationError[];
18
+ }
19
+ /**
20
+ * Apply diagram operations (update/add/delete) using ID-based lookup.
21
+ * This replaces the text-matching approach with direct DOM manipulation.
22
+ *
23
+ * @param xmlContent - The full mxfile XML content
24
+ * @param operations - Array of operations to apply
25
+ * @returns Object with result XML and any errors
26
+ */
27
+ export declare function applyDiagramOperations(xmlContent: string, operations: DiagramOperation[]): ApplyOperationsResult;
28
+ //# sourceMappingURL=diagram-operations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagram-operations.d.ts","sourceRoot":"","sources":["../src/diagram-operations.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,gBAAgB;IAC7B,SAAS,EAAE,QAAQ,GAAG,KAAK,GAAG,QAAQ,CAAA;IACtC,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,QAAQ,GAAG,KAAK,GAAG,QAAQ,CAAA;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,qBAAqB;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,cAAc,EAAE,CAAA;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAClC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,gBAAgB,EAAE,GAC/B,qBAAqB,CAsOvB"}
@@ -0,0 +1,211 @@
1
+ /**
2
+ * ID-based diagram operations
3
+ * Copied from lib/utils.ts to avoid cross-package imports
4
+ */
5
+ /**
6
+ * Apply diagram operations (update/add/delete) using ID-based lookup.
7
+ * This replaces the text-matching approach with direct DOM manipulation.
8
+ *
9
+ * @param xmlContent - The full mxfile XML content
10
+ * @param operations - Array of operations to apply
11
+ * @returns Object with result XML and any errors
12
+ */
13
+ export function applyDiagramOperations(xmlContent, operations) {
14
+ const errors = [];
15
+ // Parse the XML
16
+ const parser = new DOMParser();
17
+ const doc = parser.parseFromString(xmlContent, "text/xml");
18
+ // Check for parse errors
19
+ const parseError = doc.querySelector("parsererror");
20
+ if (parseError) {
21
+ return {
22
+ result: xmlContent,
23
+ errors: [
24
+ {
25
+ type: "update",
26
+ cellId: "",
27
+ message: `XML parse error: ${parseError.textContent}`,
28
+ },
29
+ ],
30
+ };
31
+ }
32
+ // Find the root element (inside mxGraphModel)
33
+ const root = doc.querySelector("root");
34
+ if (!root) {
35
+ return {
36
+ result: xmlContent,
37
+ errors: [
38
+ {
39
+ type: "update",
40
+ cellId: "",
41
+ message: "Could not find <root> element in XML",
42
+ },
43
+ ],
44
+ };
45
+ }
46
+ // Build a map of cell IDs to elements
47
+ const cellMap = new Map();
48
+ root.querySelectorAll("mxCell").forEach((cell) => {
49
+ const id = cell.getAttribute("id");
50
+ if (id)
51
+ cellMap.set(id, cell);
52
+ });
53
+ // Process each operation
54
+ for (const op of operations) {
55
+ if (op.operation === "update") {
56
+ const existingCell = cellMap.get(op.cell_id);
57
+ if (!existingCell) {
58
+ errors.push({
59
+ type: "update",
60
+ cellId: op.cell_id,
61
+ message: `Cell with id="${op.cell_id}" not found`,
62
+ });
63
+ continue;
64
+ }
65
+ if (!op.new_xml) {
66
+ errors.push({
67
+ type: "update",
68
+ cellId: op.cell_id,
69
+ message: "new_xml is required for update operation",
70
+ });
71
+ continue;
72
+ }
73
+ // Parse the new XML
74
+ const newDoc = parser.parseFromString(`<wrapper>${op.new_xml}</wrapper>`, "text/xml");
75
+ const newCell = newDoc.querySelector("mxCell");
76
+ if (!newCell) {
77
+ errors.push({
78
+ type: "update",
79
+ cellId: op.cell_id,
80
+ message: "new_xml must contain an mxCell element",
81
+ });
82
+ continue;
83
+ }
84
+ // Validate ID matches
85
+ const newCellId = newCell.getAttribute("id");
86
+ if (newCellId !== op.cell_id) {
87
+ errors.push({
88
+ type: "update",
89
+ cellId: op.cell_id,
90
+ message: `ID mismatch: cell_id is "${op.cell_id}" but new_xml has id="${newCellId}"`,
91
+ });
92
+ continue;
93
+ }
94
+ // Import and replace the node
95
+ const importedNode = doc.importNode(newCell, true);
96
+ existingCell.parentNode?.replaceChild(importedNode, existingCell);
97
+ // Update the map with the new element
98
+ cellMap.set(op.cell_id, importedNode);
99
+ }
100
+ else if (op.operation === "add") {
101
+ // Check if ID already exists
102
+ if (cellMap.has(op.cell_id)) {
103
+ errors.push({
104
+ type: "add",
105
+ cellId: op.cell_id,
106
+ message: `Cell with id="${op.cell_id}" already exists`,
107
+ });
108
+ continue;
109
+ }
110
+ if (!op.new_xml) {
111
+ errors.push({
112
+ type: "add",
113
+ cellId: op.cell_id,
114
+ message: "new_xml is required for add operation",
115
+ });
116
+ continue;
117
+ }
118
+ // Parse the new XML
119
+ const newDoc = parser.parseFromString(`<wrapper>${op.new_xml}</wrapper>`, "text/xml");
120
+ const newCell = newDoc.querySelector("mxCell");
121
+ if (!newCell) {
122
+ errors.push({
123
+ type: "add",
124
+ cellId: op.cell_id,
125
+ message: "new_xml must contain an mxCell element",
126
+ });
127
+ continue;
128
+ }
129
+ // Validate ID matches
130
+ const newCellId = newCell.getAttribute("id");
131
+ if (newCellId !== op.cell_id) {
132
+ errors.push({
133
+ type: "add",
134
+ cellId: op.cell_id,
135
+ message: `ID mismatch: cell_id is "${op.cell_id}" but new_xml has id="${newCellId}"`,
136
+ });
137
+ continue;
138
+ }
139
+ // Import and append the node
140
+ const importedNode = doc.importNode(newCell, true);
141
+ root.appendChild(importedNode);
142
+ // Add to map
143
+ cellMap.set(op.cell_id, importedNode);
144
+ }
145
+ else if (op.operation === "delete") {
146
+ // Protect root cells from deletion
147
+ if (op.cell_id === "0" || op.cell_id === "1") {
148
+ errors.push({
149
+ type: "delete",
150
+ cellId: op.cell_id,
151
+ message: `Cannot delete root cell "${op.cell_id}"`,
152
+ });
153
+ continue;
154
+ }
155
+ const existingCell = cellMap.get(op.cell_id);
156
+ if (!existingCell) {
157
+ // Cell not found - might have been cascade-deleted by a previous operation
158
+ // Skip silently instead of erroring (AI may redundantly list children/edges)
159
+ continue;
160
+ }
161
+ // Cascade delete: collect all cells to delete (children + edges + self)
162
+ const cellsToDelete = new Set();
163
+ // Recursive function to find all descendants
164
+ const collectDescendants = (cellId) => {
165
+ if (cellsToDelete.has(cellId))
166
+ return;
167
+ cellsToDelete.add(cellId);
168
+ // Find children (cells where parent === cellId)
169
+ const children = root.querySelectorAll(`mxCell[parent="${cellId}"]`);
170
+ children.forEach((child) => {
171
+ const childId = child.getAttribute("id");
172
+ if (childId && childId !== "0" && childId !== "1") {
173
+ collectDescendants(childId);
174
+ }
175
+ });
176
+ };
177
+ // Collect the target cell and all its descendants
178
+ collectDescendants(op.cell_id);
179
+ // Find edges referencing any of the cells to be deleted
180
+ // Also recursively collect children of those edges (e.g., edge labels)
181
+ for (const cellId of cellsToDelete) {
182
+ const referencingEdges = root.querySelectorAll(`mxCell[source="${cellId}"], mxCell[target="${cellId}"]`);
183
+ referencingEdges.forEach((edge) => {
184
+ const edgeId = edge.getAttribute("id");
185
+ // Protect root cells from being added via edge references
186
+ if (edgeId && edgeId !== "0" && edgeId !== "1") {
187
+ // Recurse to collect edge's children (like labels)
188
+ collectDescendants(edgeId);
189
+ }
190
+ });
191
+ }
192
+ // Log what will be deleted
193
+ if (cellsToDelete.size > 1) {
194
+ console.log(`[applyDiagramOperations] Cascade delete "${op.cell_id}" → deleting ${cellsToDelete.size} cells: ${Array.from(cellsToDelete).join(", ")}`);
195
+ }
196
+ // Delete all collected cells
197
+ for (const cellId of cellsToDelete) {
198
+ const cell = cellMap.get(cellId);
199
+ if (cell) {
200
+ cell.parentNode?.removeChild(cell);
201
+ cellMap.delete(cellId);
202
+ }
203
+ }
204
+ }
205
+ }
206
+ // Serialize back to string
207
+ const serializer = new XMLSerializer();
208
+ const result = serializer.serializeToString(doc);
209
+ return { result, errors };
210
+ }
211
+ //# sourceMappingURL=diagram-operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagram-operations.js","sourceRoot":"","sources":["../src/diagram-operations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Simple diagram history - matches Next.js app pattern
3
+ * Stores {xml, svg} entries in a circular buffer
4
+ */
5
+ export declare function addHistory(sessionId: string, xml: string, svg?: string): number;
6
+ export declare function getHistory(sessionId: string): Array<{
7
+ xml: string;
8
+ svg: string;
9
+ }>;
10
+ export declare function getHistoryEntry(sessionId: string, index: number): {
11
+ xml: string;
12
+ svg: string;
13
+ } | undefined;
14
+ export declare function clearHistory(sessionId: string): void;
15
+ export declare function updateLastHistorySvg(sessionId: string, svg: string): boolean;
16
+ //# sourceMappingURL=history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,SAAK,GAAG,MAAM,CAsB3E;AAED,wBAAgB,UAAU,CACtB,SAAS,EAAE,MAAM,GAClB,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAErC;AAED,wBAAgB,eAAe,CAC3B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACd;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAG1C;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAS5E"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Simple diagram history - matches Next.js app pattern
3
+ * Stores {xml, svg} entries in a circular buffer
4
+ */
5
+ import { log } from "./logger.js";
6
+ const MAX_HISTORY = 20;
7
+ const historyStore = new Map();
8
+ export function addHistory(sessionId, xml, svg = "") {
9
+ let history = historyStore.get(sessionId);
10
+ if (!history) {
11
+ history = [];
12
+ historyStore.set(sessionId, history);
13
+ }
14
+ // Dedupe: skip if same as last entry
15
+ const last = history[history.length - 1];
16
+ if (last?.xml === xml) {
17
+ return history.length - 1;
18
+ }
19
+ history.push({ xml, svg });
20
+ // Circular buffer
21
+ if (history.length > MAX_HISTORY) {
22
+ history.shift();
23
+ }
24
+ log.debug(`History: session=${sessionId}, entries=${history.length}`);
25
+ return history.length - 1;
26
+ }
27
+ export function getHistory(sessionId) {
28
+ return historyStore.get(sessionId) || [];
29
+ }
30
+ export function getHistoryEntry(sessionId, index) {
31
+ const history = historyStore.get(sessionId);
32
+ return history?.[index];
33
+ }
34
+ export function clearHistory(sessionId) {
35
+ historyStore.delete(sessionId);
36
+ }
37
+ export function updateLastHistorySvg(sessionId, svg) {
38
+ const history = historyStore.get(sessionId);
39
+ if (!history || history.length === 0)
40
+ return false;
41
+ const last = history[history.length - 1];
42
+ if (!last.svg) {
43
+ last.svg = svg;
44
+ return true;
45
+ }
46
+ return false;
47
+ }
48
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAEjC,MAAM,WAAW,GAAG,EAAE,CAAA;AACtB,MAAM,YAAY,GAAG,IAAI,GAAG,EAA+C,CAAA;AAE3E,MAAM,UAAU,UAAU,CAAC,SAAiB,EAAE,GAAW,EAAE,GAAG,GAAG,EAAE;IAC/D,IAAI,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;IACzC,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,OAAO,GAAG,EAAE,CAAA;QACZ,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;IACxC,CAAC;IAED,qCAAqC;IACrC,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IACxC,IAAI,IAAI,EAAE,GAAG,KAAK,GAAG,EAAE,CAAC;QACpB,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAA;IAC7B,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAA;IAE1B,kBAAkB;IAClB,IAAI,OAAO,CAAC,MAAM,GAAG,WAAW,EAAE,CAAC;QAC/B,OAAO,CAAC,KAAK,EAAE,CAAA;IACnB,CAAC;IAED,GAAG,CAAC,KAAK,CAAC,oBAAoB,SAAS,aAAa,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IACrE,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAA;AAC7B,CAAC;AAED,MAAM,UAAU,UAAU,CACtB,SAAiB;IAEjB,OAAO,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAA;AAC5C,CAAC;AAED,MAAM,UAAU,eAAe,CAC3B,SAAiB,EACjB,KAAa;IAEb,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;IAC3C,OAAO,OAAO,EAAE,CAAC,KAAK,CAAC,CAAA;AAC3B,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC1C,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;AAClC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,SAAiB,EAAE,GAAW;IAC/D,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;IAC3C,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAClD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IACxC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QACZ,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QACd,OAAO,IAAI,CAAA;IACf,CAAC;IACD,OAAO,KAAK,CAAA;AAChB,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Embedded HTTP Server for MCP
3
+ * Serves draw.io embed with state sync and history UI
4
+ */
5
+ interface SessionState {
6
+ xml: string;
7
+ version: number;
8
+ lastUpdated: Date;
9
+ svg?: string;
10
+ syncRequested?: number;
11
+ }
12
+ export declare const stateStore: Map<string, SessionState>;
13
+ export declare function getState(sessionId: string): SessionState | undefined;
14
+ export declare function setState(sessionId: string, xml: string, svg?: string): number;
15
+ export declare function requestSync(sessionId: string): boolean;
16
+ export declare function waitForSync(sessionId: string, timeoutMs?: number): Promise<boolean>;
17
+ export declare function startHttpServer(port?: number): Promise<number>;
18
+ export declare function stopHttpServer(): void;
19
+ export declare function shutdown(): void;
20
+ export declare function getServerPort(): number;
21
+ export {};
22
+ //# sourceMappingURL=http-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAmDH,UAAU,YAAY;IAClB,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,IAAI,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,aAAa,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,eAAO,MAAM,UAAU,2BAAkC,CAAA;AAOzD,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAEpE;AAED,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAY7E;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAStD;AAED,wBAAsB,WAAW,CAC7B,SAAS,EAAE,MAAM,EACjB,SAAS,SAAO,GACjB,OAAO,CAAC,OAAO,CAAC,CASlB;AAED,wBAAgB,eAAe,CAAC,IAAI,SAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAoC5D;AAED,wBAAgB,cAAc,IAAI,IAAI,CAKrC;AAeD,wBAAgB,QAAQ,IAAI,IAAI,CAG/B;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}