@next-ai-drawio/mcp-server 0.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 ADDED
@@ -0,0 +1,132 @@
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
+ ## Features
6
+
7
+ - **Real-time Preview**: Diagrams appear and update in your browser as the AI creates them
8
+ - **Natural Language**: Describe diagrams in plain text - flowcharts, architecture diagrams, etc.
9
+ - **Edit Support**: Modify existing diagrams with natural language instructions
10
+ - **Export**: Save diagrams as `.drawio` files
11
+
12
+ ## Quick Start
13
+
14
+ ### Claude Desktop Configuration
15
+
16
+ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
17
+
18
+ ```json
19
+ {
20
+ "mcpServers": {
21
+ "drawio": {
22
+ "command": "npx",
23
+ "args": ["-y", "@next-ai-drawio/mcp-server"]
24
+ }
25
+ }
26
+ }
27
+ ```
28
+
29
+ That's it! The MCP server connects to the hosted app at `https://next-ai-drawio.jiang.jp`.
30
+
31
+ ### Use with Claude
32
+
33
+ 1. Restart Claude Desktop after updating config
34
+ 2. Ask Claude to create a diagram:
35
+ > "Create a flowchart showing user authentication with login, MFA, and session management"
36
+ 3. The diagram appears in your browser in real-time!
37
+
38
+ ## Local Deployment (Optional)
39
+
40
+ If you want to run the app locally instead of using the hosted version:
41
+
42
+ ### Option 1: Docker
43
+
44
+ ```bash
45
+ docker run -p 6002:3000 ghcr.io/biki-dev/next-ai-draw-io
46
+ ```
47
+
48
+ Then configure Claude Desktop:
49
+
50
+ ```json
51
+ {
52
+ "mcpServers": {
53
+ "drawio": {
54
+ "command": "npx",
55
+ "args": ["-y", "@next-ai-drawio/mcp-server"],
56
+ "env": {
57
+ "NEXT_APP_URL": "http://localhost:6002"
58
+ }
59
+ }
60
+ }
61
+ }
62
+ ```
63
+
64
+ ### Option 2: From Source
65
+
66
+ ```bash
67
+ # Clone the repo
68
+ git clone https://github.com/Biki-dev/next-ai-draw-io.git
69
+ cd next-ai-draw-io
70
+
71
+ # Install and run
72
+ npm install
73
+ npm run dev
74
+ ```
75
+
76
+ Then configure with `NEXT_APP_URL=http://localhost:6002`.
77
+
78
+ ## Available Tools
79
+
80
+ | Tool | Description |
81
+ |------|-------------|
82
+ | `start_session` | Opens browser with real-time diagram preview |
83
+ | `display_diagram` | Create a new diagram from XML |
84
+ | `edit_diagram` | Edit diagram by ID-based operations (update/add/delete cells) |
85
+ | `get_diagram` | Get the current diagram XML |
86
+ | `export_diagram` | Save diagram to a `.drawio` file |
87
+
88
+ ## Environment Variables
89
+
90
+ | Variable | Default | Description |
91
+ |----------|---------|-------------|
92
+ | `NEXT_APP_URL` | `https://next-ai-drawio.jiang.jp` | URL of the Next.js app |
93
+ | `ACCESS_CODE` | - | Optional access code if the app is protected |
94
+
95
+ ## How It Works
96
+
97
+ ```
98
+ ┌─────────────────┐ stdio ┌─────────────────┐ HTTP ┌─────────────────┐
99
+ │ Claude Desktop │ <───────────> │ MCP Server │ <───────────> │ Hosted App │
100
+ │ (AI Agent) │ │ (this package) │ │ (or local) │
101
+ └─────────────────┘ └─────────────────┘ └────────┬────────┘
102
+ │ │
103
+ │ POST state Poll state
104
+ ▼ │
105
+ ┌─────────────────┐ ┌────────▼────────┐
106
+ │ State Store │◄──────────────│ User's Browser │
107
+ │ (in-memory) │ │ (draw.io UI) │
108
+ └─────────────────┘ └─────────────────┘
109
+ ```
110
+
111
+ 1. **MCP Server** receives tool calls from Claude via stdio
112
+ 2. **MCP Server** pushes diagram state to the hosted app
113
+ 3. **Browser** polls for state changes and loads new diagrams automatically
114
+
115
+ ## Troubleshooting
116
+
117
+ ### "Cannot connect to..."
118
+
119
+ - Check your internet connection
120
+ - If using a custom `NEXT_APP_URL`, ensure the server is running
121
+
122
+ ### "No active session"
123
+
124
+ Call `start_session` first to open the browser window.
125
+
126
+ ### Browser not updating
127
+
128
+ Check that the browser URL has the `?mcp=` query parameter. The MCP session ID connects the browser to the server.
129
+
130
+ ## License
131
+
132
+ Apache-2.0
@@ -0,0 +1,30 @@
1
+ /**
2
+ * API client for communicating with the Next.js app
3
+ *
4
+ * Handles:
5
+ * - Health checks
6
+ * - State synchronization for browser updates
7
+ */
8
+ interface StateResult {
9
+ xml?: string;
10
+ version?: number;
11
+ lastUpdated?: string;
12
+ error?: string;
13
+ }
14
+ declare class ApiClient {
15
+ /**
16
+ * Check if the Next.js app is running
17
+ */
18
+ checkHealth(serverUrl: string): Promise<boolean>;
19
+ /**
20
+ * Get diagram state from the Next.js app (for browser sync)
21
+ */
22
+ getState(serverUrl: string, sessionId: string): Promise<StateResult>;
23
+ /**
24
+ * Push diagram state to the Next.js app (for browser sync)
25
+ */
26
+ pushState(serverUrl: string, sessionId: string, xml: string): Promise<boolean>;
27
+ }
28
+ export declare const apiClient: ApiClient;
29
+ export {};
30
+ //# sourceMappingURL=api-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,UAAU,WAAW;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,cAAM,SAAS;IACX;;OAEG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAqBtD;;OAEG;IACG,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IA0B1E;;OAEG;IACG,SAAS,CACX,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACZ,OAAO,CAAC,OAAO,CAAC;CAuBtB;AAGD,eAAO,MAAM,SAAS,WAAkB,CAAA"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * API client for communicating with the Next.js app
3
+ *
4
+ * Handles:
5
+ * - Health checks
6
+ * - State synchronization for browser updates
7
+ */
8
+ import { log } from "./logger.js";
9
+ class ApiClient {
10
+ /**
11
+ * Check if the Next.js app is running
12
+ */
13
+ async checkHealth(serverUrl) {
14
+ try {
15
+ const response = await fetch(`${serverUrl}/api/mcp/health`, {
16
+ method: "GET",
17
+ signal: AbortSignal.timeout(5000),
18
+ });
19
+ return response.ok;
20
+ }
21
+ catch {
22
+ // If health endpoint doesn't exist, try the main page
23
+ try {
24
+ const response = await fetch(serverUrl, {
25
+ method: "HEAD",
26
+ signal: AbortSignal.timeout(5000),
27
+ });
28
+ return response.ok;
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ }
34
+ }
35
+ /**
36
+ * Get diagram state from the Next.js app (for browser sync)
37
+ */
38
+ async getState(serverUrl, sessionId) {
39
+ try {
40
+ const response = await fetch(`${serverUrl}/api/mcp/state?sessionId=${encodeURIComponent(sessionId)}`, {
41
+ method: "GET",
42
+ signal: AbortSignal.timeout(5000),
43
+ });
44
+ if (!response.ok) {
45
+ if (response.status === 404) {
46
+ // No state yet, that's okay
47
+ return {};
48
+ }
49
+ return { error: `State API error: ${response.status}` };
50
+ }
51
+ return await response.json();
52
+ }
53
+ catch (error) {
54
+ const message = error instanceof Error ? error.message : String(error);
55
+ log.debug(`getState failed: ${message}`);
56
+ return {}; // Don't error out, just return empty
57
+ }
58
+ }
59
+ /**
60
+ * Push diagram state to the Next.js app (for browser sync)
61
+ */
62
+ async pushState(serverUrl, sessionId, xml) {
63
+ try {
64
+ const response = await fetch(`${serverUrl}/api/mcp/state`, {
65
+ method: "POST",
66
+ headers: {
67
+ "Content-Type": "application/json",
68
+ },
69
+ body: JSON.stringify({ sessionId, xml }),
70
+ signal: AbortSignal.timeout(5000),
71
+ });
72
+ if (!response.ok) {
73
+ log.warn(`pushState failed: ${response.status}`);
74
+ return false;
75
+ }
76
+ return true;
77
+ }
78
+ catch (error) {
79
+ const message = error instanceof Error ? error.message : String(error);
80
+ log.warn(`pushState failed: ${message}`);
81
+ return false;
82
+ }
83
+ }
84
+ }
85
+ // Export singleton instance
86
+ export const apiClient = new ApiClient();
87
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
@@ -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
+ type: "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,IAAI,EAAE,QAAQ,GAAG,KAAK,GAAG,QAAQ,CAAA;IACjC,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,CAyLvB"}
@@ -0,0 +1,173 @@
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.type === "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.type === "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.type === "delete") {
146
+ const existingCell = cellMap.get(op.cell_id);
147
+ if (!existingCell) {
148
+ errors.push({
149
+ type: "delete",
150
+ cellId: op.cell_id,
151
+ message: `Cell with id="${op.cell_id}" not found`,
152
+ });
153
+ continue;
154
+ }
155
+ // Check for edges referencing this cell (warning only, still delete)
156
+ const referencingEdges = root.querySelectorAll(`mxCell[source="${op.cell_id}"], mxCell[target="${op.cell_id}"]`);
157
+ if (referencingEdges.length > 0) {
158
+ const edgeIds = Array.from(referencingEdges)
159
+ .map((e) => e.getAttribute("id"))
160
+ .join(", ");
161
+ console.warn(`[applyDiagramOperations] Deleting cell "${op.cell_id}" which is referenced by edges: ${edgeIds}`);
162
+ }
163
+ // Remove the node
164
+ existingCell.parentNode?.removeChild(existingCell);
165
+ cellMap.delete(op.cell_id);
166
+ }
167
+ }
168
+ // Serialize back to string
169
+ const serializer = new XMLSerializer();
170
+ const result = serializer.serializeToString(doc);
171
+ return { result, errors };
172
+ }
173
+ //# 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,10 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP Server for Next AI Draw.io
4
+ *
5
+ * Enables AI agents (Claude Desktop, Cursor, etc.) to generate and edit
6
+ * draw.io diagrams through the Next AI Draw.io application with real-time
7
+ * browser preview.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;GAMG"}
package/dist/index.js ADDED
@@ -0,0 +1,425 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP Server for Next AI Draw.io
4
+ *
5
+ * Enables AI agents (Claude Desktop, Cursor, etc.) to generate and edit
6
+ * draw.io diagrams through the Next AI Draw.io application with real-time
7
+ * browser preview.
8
+ */
9
+ // Setup DOM polyfill for Node.js (required for lib/utils.ts XML functions)
10
+ import { DOMParser } from "linkedom";
11
+ globalThis.DOMParser = DOMParser;
12
+ // Create XMLSerializer polyfill using outerHTML (linkedom doesn't export XMLSerializer)
13
+ class XMLSerializerPolyfill {
14
+ serializeToString(node) {
15
+ if (node.outerHTML !== undefined) {
16
+ return node.outerHTML;
17
+ }
18
+ // For document nodes, serialize the document element
19
+ if (node.documentElement) {
20
+ return node.documentElement.outerHTML;
21
+ }
22
+ return "";
23
+ }
24
+ }
25
+ ;
26
+ globalThis.XMLSerializer = XMLSerializerPolyfill;
27
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
28
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
29
+ import open from "open";
30
+ import { z } from "zod";
31
+ import { apiClient } from "./api-client.js";
32
+ import { applyDiagramOperations, } from "./diagram-operations.js";
33
+ import { log } from "./logger.js";
34
+ import { sessionManager } from "./session.js";
35
+ // Server configuration from environment
36
+ const config = {
37
+ nextAppUrl: process.env.NEXT_APP_URL || "https://next-ai-drawio.jiang.jp",
38
+ accessCode: process.env.ACCESS_CODE,
39
+ };
40
+ // Create MCP server
41
+ const server = new McpServer({
42
+ name: "next-ai-drawio",
43
+ version: "0.1.0",
44
+ });
45
+ // Register prompt with workflow guidance
46
+ server.prompt("diagram-workflow", "Guidelines for creating and editing draw.io diagrams", () => ({
47
+ messages: [
48
+ {
49
+ role: "user",
50
+ content: {
51
+ type: "text",
52
+ text: `# Draw.io Diagram Workflow Guidelines
53
+
54
+ ## Creating a New Diagram
55
+ 1. Call start_session to open the browser preview
56
+ 2. Use display_diagram with complete mxGraphModel XML to create a new diagram
57
+
58
+ ## Adding Elements to Existing Diagram
59
+ 1. Use edit_diagram with "add" operation
60
+ 2. Provide a unique cell_id and complete mxCell XML
61
+ 3. No need to call get_diagram first - the server fetches latest state automatically
62
+
63
+ ## Modifying or Deleting Existing Elements
64
+ 1. FIRST call get_diagram to see current cell IDs and structure
65
+ 2. THEN call edit_diagram with "update" or "delete" operations
66
+ 3. For update, provide the cell_id and complete new mxCell XML
67
+
68
+ ## Important Notes
69
+ - display_diagram REPLACES the entire diagram - only use for new diagrams
70
+ - edit_diagram PRESERVES user's manual changes (fetches browser state first)
71
+ - Always use unique cell_ids when adding elements (e.g., "shape-1", "arrow-2")`,
72
+ },
73
+ },
74
+ ],
75
+ }));
76
+ // Tool: start_session
77
+ // Opens browser with MCP session for real-time diagram preview
78
+ server.registerTool("start_session", {
79
+ description: "Start a new diagram session and open the browser for real-time preview. " +
80
+ "Connects to the hosted Next AI Draw.io app. " +
81
+ "The browser will show diagram updates as they happen.",
82
+ inputSchema: {},
83
+ }, async () => {
84
+ try {
85
+ // Check if hosted app is reachable
86
+ const isReady = await ensureServerReachable();
87
+ if (!isReady) {
88
+ return {
89
+ content: [
90
+ {
91
+ type: "text",
92
+ text: `Error: Cannot connect to ${config.nextAppUrl}. Check your internet connection or use NEXT_APP_URL env var for a custom server.`,
93
+ },
94
+ ],
95
+ isError: true,
96
+ };
97
+ }
98
+ // Create new session
99
+ const session = sessionManager.createSession(config.nextAppUrl);
100
+ // Open browser with session URL
101
+ const browserUrl = `${config.nextAppUrl}?mcp=${session.id}`;
102
+ await open(browserUrl);
103
+ log.info(`Started session ${session.id}, opened browser at ${browserUrl}`);
104
+ return {
105
+ content: [
106
+ {
107
+ type: "text",
108
+ text: `Session started successfully!\n\nSession ID: ${session.id}\nBrowser URL: ${browserUrl}\n\nThe browser will now show real-time diagram updates.`,
109
+ },
110
+ ],
111
+ };
112
+ }
113
+ catch (error) {
114
+ const message = error instanceof Error ? error.message : String(error);
115
+ log.error("start_session failed:", message);
116
+ return {
117
+ content: [{ type: "text", text: `Error: ${message}` }],
118
+ isError: true,
119
+ };
120
+ }
121
+ });
122
+ // Tool: display_diagram
123
+ // Display a draw.io diagram from XML (same as Next.js API)
124
+ server.registerTool("display_diagram", {
125
+ description: "Display a NEW draw.io diagram from XML. REPLACES the entire diagram. " +
126
+ "Use this for creating new diagrams from scratch. " +
127
+ "To ADD elements to an existing diagram, use edit_diagram with 'add' operation instead. " +
128
+ "You should generate valid draw.io/mxGraph XML format.",
129
+ inputSchema: {
130
+ xml: z
131
+ .string()
132
+ .describe("The draw.io XML to display (mxGraphModel format)"),
133
+ },
134
+ }, async ({ xml }) => {
135
+ try {
136
+ const session = sessionManager.getCurrentSession();
137
+ if (!session) {
138
+ return {
139
+ content: [
140
+ {
141
+ type: "text",
142
+ text: "Error: No active session. Please call start_session first.",
143
+ },
144
+ ],
145
+ isError: true,
146
+ };
147
+ }
148
+ log.info(`Displaying diagram, ${xml.length} chars`);
149
+ // Update session state
150
+ session.previousXml = session.xml;
151
+ session.xml = xml;
152
+ session.version++;
153
+ session.lastUpdated = new Date();
154
+ // Push state to Next.js for browser sync
155
+ await apiClient.pushState(session.serverUrl, session.id, session.xml);
156
+ log.info(`Diagram displayed successfully`);
157
+ return {
158
+ content: [
159
+ {
160
+ type: "text",
161
+ text: `Diagram displayed successfully!\n\nThe diagram is now visible in your browser.\n\nXML length: ${xml.length} characters`,
162
+ },
163
+ ],
164
+ };
165
+ }
166
+ catch (error) {
167
+ const message = error instanceof Error ? error.message : String(error);
168
+ log.error("display_diagram failed:", message);
169
+ return {
170
+ content: [{ type: "text", text: `Error: ${message}` }],
171
+ isError: true,
172
+ };
173
+ }
174
+ });
175
+ // Tool: edit_diagram
176
+ // Edit the diagram using ID-based operations (same as Next.js API)
177
+ server.registerTool("edit_diagram", {
178
+ description: "Edit the current diagram by ID-based operations (update/add/delete cells). " +
179
+ "ALWAYS fetches the latest state from browser by using 'get_diagram' tool before 'edit_diagram', so user's manual changes are preserved.\n\n" +
180
+ "IMPORTANT workflow:\n" +
181
+ "- For ADD operations: Can use directly - just provide new unique cell_id and new_xml.\n" +
182
+ "- For UPDATE/DELETE: Call get_diagram FIRST to see current cell IDs, then edit.\n\n" +
183
+ "Operations:\n" +
184
+ "- add: Add a new cell. Provide cell_id (new unique id) and new_xml.\n" +
185
+ "- update: Replace an existing cell by its id. Provide cell_id and complete new_xml.\n" +
186
+ "- delete: Remove a cell by its id. Only cell_id is needed.\n\n" +
187
+ "For add/update, new_xml must be a complete mxCell element including mxGeometry.",
188
+ inputSchema: {
189
+ operations: z
190
+ .array(z.object({
191
+ type: z
192
+ .enum(["update", "add", "delete"])
193
+ .describe("Operation type"),
194
+ cell_id: z.string().describe("The id of the mxCell"),
195
+ new_xml: z
196
+ .string()
197
+ .optional()
198
+ .describe("Complete mxCell XML element (required for update/add)"),
199
+ }))
200
+ .describe("Array of operations to apply"),
201
+ },
202
+ }, async ({ operations }) => {
203
+ try {
204
+ const session = sessionManager.getCurrentSession();
205
+ if (!session) {
206
+ return {
207
+ content: [
208
+ {
209
+ type: "text",
210
+ text: "Error: No active session. Please call start_session first.",
211
+ },
212
+ ],
213
+ isError: true,
214
+ };
215
+ }
216
+ // Fetch latest state from browser (user may have modified diagram manually)
217
+ const browserState = await apiClient.getState(session.serverUrl, session.id);
218
+ if (browserState.xml) {
219
+ session.xml = browserState.xml;
220
+ log.info("Fetched latest diagram state from browser");
221
+ }
222
+ if (!session.xml) {
223
+ return {
224
+ content: [
225
+ {
226
+ type: "text",
227
+ text: "Error: No diagram to edit. Please create a diagram first with display_diagram.",
228
+ },
229
+ ],
230
+ isError: true,
231
+ };
232
+ }
233
+ log.info(`Editing diagram with ${operations.length} operation(s)`);
234
+ // Apply operations using shared utility
235
+ const { result, errors } = applyDiagramOperations(session.xml, operations);
236
+ if (errors.length > 0) {
237
+ const errorMessages = errors
238
+ .map((e) => `${e.type} ${e.cellId}: ${e.message}`)
239
+ .join("\n");
240
+ log.warn(`Edit had ${errors.length} error(s): ${errorMessages}`);
241
+ }
242
+ // Update session state
243
+ session.previousXml = session.xml;
244
+ session.xml = result;
245
+ session.version++;
246
+ session.lastUpdated = new Date();
247
+ // Push state to Next.js for browser sync
248
+ await apiClient.pushState(session.serverUrl, session.id, session.xml);
249
+ log.info(`Diagram edited successfully`);
250
+ const successMsg = `Diagram edited successfully!\n\nApplied ${operations.length} operation(s).`;
251
+ const errorMsg = errors.length > 0
252
+ ? `\n\nWarnings:\n${errors.map((e) => `- ${e.type} ${e.cellId}: ${e.message}`).join("\n")}`
253
+ : "";
254
+ return {
255
+ content: [
256
+ {
257
+ type: "text",
258
+ text: successMsg + errorMsg,
259
+ },
260
+ ],
261
+ };
262
+ }
263
+ catch (error) {
264
+ const message = error instanceof Error ? error.message : String(error);
265
+ log.error("edit_diagram failed:", message);
266
+ return {
267
+ content: [{ type: "text", text: `Error: ${message}` }],
268
+ isError: true,
269
+ };
270
+ }
271
+ });
272
+ // Tool: get_diagram
273
+ // Get the current diagram XML state
274
+ server.registerTool("get_diagram", {
275
+ description: "Get the current diagram XML (fetches latest from browser, including user's manual edits). " +
276
+ "Call this BEFORE edit_diagram if you need to update or delete existing elements, " +
277
+ "so you can see the current cell IDs and structure.",
278
+ }, async () => {
279
+ try {
280
+ const session = sessionManager.getCurrentSession();
281
+ if (!session) {
282
+ return {
283
+ content: [
284
+ {
285
+ type: "text",
286
+ text: "Error: No active session. Please call start_session first.",
287
+ },
288
+ ],
289
+ isError: true,
290
+ };
291
+ }
292
+ // Fetch latest state from browser
293
+ const browserState = await apiClient.getState(session.serverUrl, session.id);
294
+ if (browserState.xml) {
295
+ session.xml = browserState.xml;
296
+ }
297
+ if (!session.xml) {
298
+ return {
299
+ content: [
300
+ {
301
+ type: "text",
302
+ text: "No diagram exists yet. Use create_diagram to create one.",
303
+ },
304
+ ],
305
+ };
306
+ }
307
+ return {
308
+ content: [
309
+ {
310
+ type: "text",
311
+ text: `Current diagram XML:\n\n${session.xml}`,
312
+ },
313
+ ],
314
+ };
315
+ }
316
+ catch (error) {
317
+ const message = error instanceof Error ? error.message : String(error);
318
+ log.error("get_diagram failed:", message);
319
+ return {
320
+ content: [{ type: "text", text: `Error: ${message}` }],
321
+ isError: true,
322
+ };
323
+ }
324
+ });
325
+ // Tool: export_diagram
326
+ // Save the diagram to a file
327
+ server.registerTool("export_diagram", {
328
+ description: "Export the current diagram to a .drawio file.",
329
+ inputSchema: {
330
+ path: z
331
+ .string()
332
+ .describe("File path to save the diagram (e.g., ./diagram.drawio)"),
333
+ },
334
+ }, async ({ path }) => {
335
+ try {
336
+ const session = sessionManager.getCurrentSession();
337
+ if (!session) {
338
+ return {
339
+ content: [
340
+ {
341
+ type: "text",
342
+ text: "Error: No active session. Please call start_session first.",
343
+ },
344
+ ],
345
+ isError: true,
346
+ };
347
+ }
348
+ // Fetch latest state from browser
349
+ const browserState = await apiClient.getState(session.serverUrl, session.id);
350
+ if (browserState.xml) {
351
+ session.xml = browserState.xml;
352
+ }
353
+ if (!session.xml) {
354
+ return {
355
+ content: [
356
+ {
357
+ type: "text",
358
+ text: "Error: No diagram to export. Please create a diagram first.",
359
+ },
360
+ ],
361
+ isError: true,
362
+ };
363
+ }
364
+ // Import fs dynamically
365
+ const fs = await import("node:fs/promises");
366
+ const nodePath = await import("node:path");
367
+ // Ensure path ends with .drawio
368
+ let filePath = path;
369
+ if (!filePath.endsWith(".drawio")) {
370
+ filePath = `${filePath}.drawio`;
371
+ }
372
+ // Resolve to absolute path
373
+ const absolutePath = nodePath.resolve(filePath);
374
+ // Write the XML to file
375
+ await fs.writeFile(absolutePath, session.xml, "utf-8");
376
+ log.info(`Diagram exported to ${absolutePath}`);
377
+ return {
378
+ content: [
379
+ {
380
+ type: "text",
381
+ text: `Diagram exported successfully!\n\nFile: ${absolutePath}\nSize: ${session.xml.length} characters`,
382
+ },
383
+ ],
384
+ };
385
+ }
386
+ catch (error) {
387
+ const message = error instanceof Error ? error.message : String(error);
388
+ log.error("export_diagram failed:", message);
389
+ return {
390
+ content: [{ type: "text", text: `Error: ${message}` }],
391
+ isError: true,
392
+ };
393
+ }
394
+ });
395
+ // Track if server has been verified as reachable
396
+ let serverReachable = false;
397
+ /**
398
+ * Check if the hosted app is reachable
399
+ */
400
+ async function ensureServerReachable() {
401
+ if (serverReachable)
402
+ return true;
403
+ const isHealthy = await apiClient.checkHealth(config.nextAppUrl);
404
+ if (isHealthy) {
405
+ serverReachable = true;
406
+ log.info(`Connected to ${config.nextAppUrl}`);
407
+ }
408
+ else {
409
+ log.warn(`Cannot reach ${config.nextAppUrl}`);
410
+ }
411
+ return isHealthy;
412
+ }
413
+ // Start the MCP server
414
+ async function main() {
415
+ log.info("Starting MCP server for Next AI Draw.io...");
416
+ log.info(`App URL: ${config.nextAppUrl}`);
417
+ const transport = new StdioServerTransport();
418
+ await server.connect(transport);
419
+ log.info("MCP server running on stdio");
420
+ }
421
+ main().catch((error) => {
422
+ log.error("Fatal error:", error);
423
+ process.exit(1);
424
+ });
425
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Logger for MCP server
3
+ *
4
+ * CRITICAL: MCP servers communicate via STDIO (stdin/stdout).
5
+ * Using console.log() will corrupt the JSON-RPC protocol messages.
6
+ * ALL logging MUST use console.error() which writes to stderr.
7
+ */
8
+ export declare const log: {
9
+ info: (msg: string, ...args: unknown[]) => void;
10
+ error: (msg: string, ...args: unknown[]) => void;
11
+ debug: (msg: string, ...args: unknown[]) => void;
12
+ warn: (msg: string, ...args: unknown[]) => void;
13
+ };
14
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,GAAG;gBACA,MAAM,WAAW,OAAO,EAAE;iBAGzB,MAAM,WAAW,OAAO,EAAE;iBAG1B,MAAM,WAAW,OAAO,EAAE;gBAK3B,MAAM,WAAW,OAAO,EAAE;CAGzC,CAAA"}
package/dist/logger.js ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Logger for MCP server
3
+ *
4
+ * CRITICAL: MCP servers communicate via STDIO (stdin/stdout).
5
+ * Using console.log() will corrupt the JSON-RPC protocol messages.
6
+ * ALL logging MUST use console.error() which writes to stderr.
7
+ */
8
+ export const log = {
9
+ info: (msg, ...args) => {
10
+ console.error(`[MCP-DrawIO] [INFO] ${msg}`, ...args);
11
+ },
12
+ error: (msg, ...args) => {
13
+ console.error(`[MCP-DrawIO] [ERROR] ${msg}`, ...args);
14
+ },
15
+ debug: (msg, ...args) => {
16
+ if (process.env.DEBUG === "true") {
17
+ console.error(`[MCP-DrawIO] [DEBUG] ${msg}`, ...args);
18
+ }
19
+ },
20
+ warn: (msg, ...args) => {
21
+ console.error(`[MCP-DrawIO] [WARN] ${msg}`, ...args);
22
+ },
23
+ };
24
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,GAAG,GAAG;IACf,IAAI,EAAE,CAAC,GAAW,EAAE,GAAG,IAAe,EAAE,EAAE;QACtC,OAAO,CAAC,KAAK,CAAC,uBAAuB,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,CAAA;IACxD,CAAC;IACD,KAAK,EAAE,CAAC,GAAW,EAAE,GAAG,IAAe,EAAE,EAAE;QACvC,OAAO,CAAC,KAAK,CAAC,wBAAwB,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,CAAA;IACzD,CAAC;IACD,KAAK,EAAE,CAAC,GAAW,EAAE,GAAG,IAAe,EAAE,EAAE;QACvC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC/B,OAAO,CAAC,KAAK,CAAC,wBAAwB,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,CAAA;QACzD,CAAC;IACL,CAAC;IACD,IAAI,EAAE,CAAC,GAAW,EAAE,GAAG,IAAe,EAAE,EAAE;QACtC,OAAO,CAAC,KAAK,CAAC,uBAAuB,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,CAAA;IACxD,CAAC;CACJ,CAAA"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Session management for MCP server
3
+ *
4
+ * Manages diagram sessions with TTL cleanup.
5
+ */
6
+ export interface Session {
7
+ id: string;
8
+ serverUrl: string;
9
+ xml: string;
10
+ previousXml: string;
11
+ version: number;
12
+ lastUpdated: Date;
13
+ createdAt: Date;
14
+ }
15
+ declare class SessionManager {
16
+ private sessions;
17
+ private currentSessionId;
18
+ private cleanupTimer;
19
+ constructor();
20
+ /**
21
+ * Generate a unique session ID
22
+ */
23
+ private generateSessionId;
24
+ /**
25
+ * Create a new session
26
+ */
27
+ createSession(serverUrl: string): Session;
28
+ /**
29
+ * Get the current active session
30
+ */
31
+ getCurrentSession(): Session | null;
32
+ /**
33
+ * Get a session by ID
34
+ */
35
+ getSession(sessionId: string): Session | null;
36
+ /**
37
+ * Update session state
38
+ */
39
+ updateSession(sessionId: string, updates: Partial<Session>): Session | null;
40
+ /**
41
+ * Delete a session
42
+ */
43
+ deleteSession(sessionId: string): boolean;
44
+ /**
45
+ * Cleanup expired sessions (older than TTL)
46
+ */
47
+ private cleanupExpiredSessions;
48
+ /**
49
+ * Get all active sessions (for debugging)
50
+ */
51
+ getAllSessions(): Session[];
52
+ /**
53
+ * Stop the cleanup timer (for graceful shutdown)
54
+ */
55
+ destroy(): void;
56
+ }
57
+ export declare const sessionManager: SessionManager;
58
+ export {};
59
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,WAAW,OAAO;IACpB,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,IAAI,CAAA;IACjB,SAAS,EAAE,IAAI,CAAA;CAClB;AAQD,cAAM,cAAc;IAChB,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,gBAAgB,CAAsB;IAC9C,OAAO,CAAC,YAAY,CAA8B;;IASlD;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAMzB;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAkBzC;;OAEG;IACH,iBAAiB,IAAI,OAAO,GAAG,IAAI;IAOnC;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;IAI7C;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,GAAG,IAAI;IAU3E;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAOzC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAoB9B;;OAEG;IACH,cAAc,IAAI,OAAO,EAAE;IAI3B;;OAEG;IACH,OAAO,IAAI,IAAI;CAMlB;AAGD,eAAO,MAAM,cAAc,gBAAuB,CAAA"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Session management for MCP server
3
+ *
4
+ * Manages diagram sessions with TTL cleanup.
5
+ */
6
+ import { log } from "./logger.js";
7
+ // Session TTL in milliseconds (1 hour)
8
+ const SESSION_TTL = 60 * 60 * 1000;
9
+ // Cleanup interval (5 minutes)
10
+ const CLEANUP_INTERVAL = 5 * 60 * 1000;
11
+ class SessionManager {
12
+ sessions = new Map();
13
+ currentSessionId = null;
14
+ cleanupTimer = null;
15
+ constructor() {
16
+ // Start cleanup timer
17
+ this.cleanupTimer = setInterval(() => {
18
+ this.cleanupExpiredSessions();
19
+ }, CLEANUP_INTERVAL);
20
+ }
21
+ /**
22
+ * Generate a unique session ID
23
+ */
24
+ generateSessionId() {
25
+ const timestamp = Date.now().toString(36);
26
+ const random = Math.random().toString(36).substring(2, 8);
27
+ return `mcp-${timestamp}-${random}`;
28
+ }
29
+ /**
30
+ * Create a new session
31
+ */
32
+ createSession(serverUrl) {
33
+ const session = {
34
+ id: this.generateSessionId(),
35
+ serverUrl,
36
+ xml: "",
37
+ previousXml: "",
38
+ version: 0,
39
+ lastUpdated: new Date(),
40
+ createdAt: new Date(),
41
+ };
42
+ this.sessions.set(session.id, session);
43
+ this.currentSessionId = session.id;
44
+ log.info(`Created session: ${session.id}`);
45
+ return session;
46
+ }
47
+ /**
48
+ * Get the current active session
49
+ */
50
+ getCurrentSession() {
51
+ if (!this.currentSessionId) {
52
+ return null;
53
+ }
54
+ return this.sessions.get(this.currentSessionId) || null;
55
+ }
56
+ /**
57
+ * Get a session by ID
58
+ */
59
+ getSession(sessionId) {
60
+ return this.sessions.get(sessionId) || null;
61
+ }
62
+ /**
63
+ * Update session state
64
+ */
65
+ updateSession(sessionId, updates) {
66
+ const session = this.sessions.get(sessionId);
67
+ if (!session) {
68
+ return null;
69
+ }
70
+ Object.assign(session, updates, { lastUpdated: new Date() });
71
+ return session;
72
+ }
73
+ /**
74
+ * Delete a session
75
+ */
76
+ deleteSession(sessionId) {
77
+ if (this.currentSessionId === sessionId) {
78
+ this.currentSessionId = null;
79
+ }
80
+ return this.sessions.delete(sessionId);
81
+ }
82
+ /**
83
+ * Cleanup expired sessions (older than TTL)
84
+ */
85
+ cleanupExpiredSessions() {
86
+ const now = Date.now();
87
+ let cleanedCount = 0;
88
+ for (const [id, session] of this.sessions) {
89
+ const age = now - session.lastUpdated.getTime();
90
+ if (age > SESSION_TTL) {
91
+ this.sessions.delete(id);
92
+ if (this.currentSessionId === id) {
93
+ this.currentSessionId = null;
94
+ }
95
+ cleanedCount++;
96
+ }
97
+ }
98
+ if (cleanedCount > 0) {
99
+ log.info(`Cleaned up ${cleanedCount} expired session(s)`);
100
+ }
101
+ }
102
+ /**
103
+ * Get all active sessions (for debugging)
104
+ */
105
+ getAllSessions() {
106
+ return Array.from(this.sessions.values());
107
+ }
108
+ /**
109
+ * Stop the cleanup timer (for graceful shutdown)
110
+ */
111
+ destroy() {
112
+ if (this.cleanupTimer) {
113
+ clearInterval(this.cleanupTimer);
114
+ this.cleanupTimer = null;
115
+ }
116
+ }
117
+ }
118
+ // Export singleton instance
119
+ export const sessionManager = new SessionManager();
120
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@next-ai-drawio/mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Next AI Draw.io - AI-powered diagram generation with real-time browser preview",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "next-ai-drawio-mcp": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsx watch src/index.ts",
13
+ "start": "node dist/index.js",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "drawio",
19
+ "diagram",
20
+ "ai",
21
+ "claude",
22
+ "model-context-protocol"
23
+ ],
24
+ "author": "Biki-dev",
25
+ "license": "Apache-2.0",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/Biki-dev/next-ai-draw-io",
29
+ "directory": "packages/mcp-server"
30
+ },
31
+ "homepage": "https://next-ai-drawio.jiang.jp",
32
+ "bugs": {
33
+ "url": "https://github.com/Biki-dev/next-ai-draw-io/issues"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "dependencies": {
39
+ "@modelcontextprotocol/sdk": "^1.0.4",
40
+ "linkedom": "^0.18.0",
41
+ "open": "^10.1.0",
42
+ "zod": "^3.24.0"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^20",
46
+ "tsx": "^4.19.0",
47
+ "typescript": "^5"
48
+ },
49
+ "engines": {
50
+ "node": ">=18"
51
+ },
52
+ "files": [
53
+ "dist"
54
+ ]
55
+ }