@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 +132 -0
- package/dist/api-client.d.ts +30 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +87 -0
- package/dist/api-client.js.map +1 -0
- package/dist/diagram-operations.d.ts +28 -0
- package/dist/diagram-operations.d.ts.map +1 -0
- package/dist/diagram-operations.js +173 -0
- package/dist/diagram-operations.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +425 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +14 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +24 -0
- package/dist/logger.js.map +1 -0
- package/dist/session.d.ts +59 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +120 -0
- package/dist/session.js.map +1 -0
- package/package.json +55 -0
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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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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":";AACA;;;;;;GAMG;AAEH,2EAA2E;AAC3E,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CACnC;AAAC,UAAkB,CAAC,SAAS,GAAG,SAAS,CAAA;AAE1C,wFAAwF;AACxF,MAAM,qBAAqB;IACvB,iBAAiB,CAAC,IAAS;QACvB,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC,SAAS,CAAA;QACzB,CAAC;QACD,qDAAqD;QACrD,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC,eAAe,CAAC,SAAS,CAAA;QACzC,CAAC;QACD,OAAO,EAAE,CAAA;IACb,CAAC;CACJ;AACD,CAAC;AAAC,UAAkB,CAAC,aAAa,GAAG,qBAAqB,CAAA;AAE1D,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EACH,sBAAsB,GAEzB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAE7C,wCAAwC;AACxC,MAAM,MAAM,GAAG;IACX,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,iCAAiC;IACzE,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW;CACtC,CAAA;AAED,oBAAoB;AACpB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IACzB,IAAI,EAAE,gBAAgB;IACtB,OAAO,EAAE,OAAO;CACnB,CAAC,CAAA;AAEF,yCAAyC;AACzC,MAAM,CAAC,MAAM,CACT,kBAAkB,EAClB,sDAAsD,EACtD,GAAG,EAAE,CAAC,CAAC;IACH,QAAQ,EAAE;QACN;YACI,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE;gBACL,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE;;;;;;;;;;;;;;;;;;;+EAmBqD;aAC9D;SACJ;KACJ;CACJ,CAAC,CACL,CAAA;AAED,sBAAsB;AACtB,+DAA+D;AAC/D,MAAM,CAAC,YAAY,CACf,eAAe,EACf;IACI,WAAW,EACP,0EAA0E;QAC1E,8CAA8C;QAC9C,uDAAuD;IAC3D,WAAW,EAAE,EAAE;CAClB,EACD,KAAK,IAAI,EAAE;IACP,IAAI,CAAC;QACD,mCAAmC;QACnC,MAAM,OAAO,GAAG,MAAM,qBAAqB,EAAE,CAAA;QAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,4BAA4B,MAAM,CAAC,UAAU,mFAAmF;qBACzI;iBACJ;gBACD,OAAO,EAAE,IAAI;aAChB,CAAA;QACL,CAAC;QAED,qBAAqB;QACrB,MAAM,OAAO,GAAG,cAAc,CAAC,aAAa,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;QAE/D,gCAAgC;QAChC,MAAM,UAAU,GAAG,GAAG,MAAM,CAAC,UAAU,QAAQ,OAAO,CAAC,EAAE,EAAE,CAAA;QAC3D,MAAM,IAAI,CAAC,UAAU,CAAC,CAAA;QAEtB,GAAG,CAAC,IAAI,CACJ,mBAAmB,OAAO,CAAC,EAAE,uBAAuB,UAAU,EAAE,CACnE,CAAA;QAED,OAAO;YACH,OAAO,EAAE;gBACL;oBACI,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,gDAAgD,OAAO,CAAC,EAAE,kBAAkB,UAAU,0DAA0D;iBACzJ;aACJ;SACJ,CAAA;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,OAAO,GACT,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAC1D,GAAG,CAAC,KAAK,CAAC,uBAAuB,EAAE,OAAO,CAAC,CAAA;QAC3C,OAAO;YACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC;YACtD,OAAO,EAAE,IAAI;SAChB,CAAA;IACL,CAAC;AACL,CAAC,CACJ,CAAA;AAED,wBAAwB;AACxB,2DAA2D;AAC3D,MAAM,CAAC,YAAY,CACf,iBAAiB,EACjB;IACI,WAAW,EACP,uEAAuE;QACvE,mDAAmD;QACnD,yFAAyF;QACzF,uDAAuD;IAC3D,WAAW,EAAE;QACT,GAAG,EAAE,CAAC;aACD,MAAM,EAAE;aACR,QAAQ,CAAC,kDAAkD,CAAC;KACpE;CACJ,EACD,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;IACd,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,cAAc,CAAC,iBAAiB,EAAE,CAAA;QAClD,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,4DAA4D;qBACrE;iBACJ;gBACD,OAAO,EAAE,IAAI;aAChB,CAAA;QACL,CAAC;QAED,GAAG,CAAC,IAAI,CAAC,uBAAuB,GAAG,CAAC,MAAM,QAAQ,CAAC,CAAA;QAEnD,uBAAuB;QACvB,OAAO,CAAC,WAAW,GAAG,OAAO,CAAC,GAAG,CAAA;QACjC,OAAO,CAAC,GAAG,GAAG,GAAG,CAAA;QACjB,OAAO,CAAC,OAAO,EAAE,CAAA;QACjB,OAAO,CAAC,WAAW,GAAG,IAAI,IAAI,EAAE,CAAA;QAEhC,yCAAyC;QACzC,MAAM,SAAS,CAAC,SAAS,CACrB,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,EAAE,EACV,OAAO,CAAC,GAAG,CACd,CAAA;QAED,GAAG,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAA;QAE1C,OAAO;YACH,OAAO,EAAE;gBACL;oBACI,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,iGAAiG,GAAG,CAAC,MAAM,aAAa;iBACjI;aACJ;SACJ,CAAA;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,OAAO,GACT,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAC1D,GAAG,CAAC,KAAK,CAAC,yBAAyB,EAAE,OAAO,CAAC,CAAA;QAC7C,OAAO;YACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC;YACtD,OAAO,EAAE,IAAI;SAChB,CAAA;IACL,CAAC;AACL,CAAC,CACJ,CAAA;AAED,qBAAqB;AACrB,mEAAmE;AACnE,MAAM,CAAC,YAAY,CACf,cAAc,EACd;IACI,WAAW,EACP,6EAA6E;QAC7E,6IAA6I;QAC7I,uBAAuB;QACvB,yFAAyF;QACzF,qFAAqF;QACrF,eAAe;QACf,uEAAuE;QACvE,uFAAuF;QACvF,gEAAgE;QAChE,iFAAiF;IACrF,WAAW,EAAE;QACT,UAAU,EAAE,CAAC;aACR,KAAK,CACF,CAAC,CAAC,MAAM,CAAC;YACL,IAAI,EAAE,CAAC;iBACF,IAAI,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;iBACjC,QAAQ,CAAC,gBAAgB,CAAC;YAC/B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC;YACpD,OAAO,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CACL,uDAAuD,CAC1D;SACR,CAAC,CACL;aACA,QAAQ,CAAC,8BAA8B,CAAC;KAChD;CACJ,EACD,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;IACrB,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,cAAc,CAAC,iBAAiB,EAAE,CAAA;QAClD,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,4DAA4D;qBACrE;iBACJ;gBACD,OAAO,EAAE,IAAI;aAChB,CAAA;QACL,CAAC;QAED,4EAA4E;QAC5E,MAAM,YAAY,GAAG,MAAM,SAAS,CAAC,QAAQ,CACzC,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,EAAE,CACb,CAAA;QACD,IAAI,YAAY,CAAC,GAAG,EAAE,CAAC;YACnB,OAAO,CAAC,GAAG,GAAG,YAAY,CAAC,GAAG,CAAA;YAC9B,GAAG,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAA;QACzD,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YACf,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,gFAAgF;qBACzF;iBACJ;gBACD,OAAO,EAAE,IAAI;aAChB,CAAA;QACL,CAAC;QAED,GAAG,CAAC,IAAI,CAAC,wBAAwB,UAAU,CAAC,MAAM,eAAe,CAAC,CAAA;QAElE,wCAAwC;QACxC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,sBAAsB,CAC7C,OAAO,CAAC,GAAG,EACX,UAAgC,CACnC,CAAA;QAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,aAAa,GAAG,MAAM;iBACvB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;iBACjD,IAAI,CAAC,IAAI,CAAC,CAAA;YACf,GAAG,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,MAAM,cAAc,aAAa,EAAE,CAAC,CAAA;QACpE,CAAC;QAED,uBAAuB;QACvB,OAAO,CAAC,WAAW,GAAG,OAAO,CAAC,GAAG,CAAA;QACjC,OAAO,CAAC,GAAG,GAAG,MAAM,CAAA;QACpB,OAAO,CAAC,OAAO,EAAE,CAAA;QACjB,OAAO,CAAC,WAAW,GAAG,IAAI,IAAI,EAAE,CAAA;QAEhC,yCAAyC;QACzC,MAAM,SAAS,CAAC,SAAS,CACrB,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,EAAE,EACV,OAAO,CAAC,GAAG,CACd,CAAA;QAED,GAAG,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAA;QAEvC,MAAM,UAAU,GAAG,2CAA2C,UAAU,CAAC,MAAM,gBAAgB,CAAA;QAC/F,MAAM,QAAQ,GACV,MAAM,CAAC,MAAM,GAAG,CAAC;YACb,CAAC,CAAC,kBAAkB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YAC3F,CAAC,CAAC,EAAE,CAAA;QAEZ,OAAO;YACH,OAAO,EAAE;gBACL;oBACI,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,UAAU,GAAG,QAAQ;iBAC9B;aACJ;SACJ,CAAA;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,OAAO,GACT,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAC1D,GAAG,CAAC,KAAK,CAAC,sBAAsB,EAAE,OAAO,CAAC,CAAA;QAC1C,OAAO;YACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC;YACtD,OAAO,EAAE,IAAI;SAChB,CAAA;IACL,CAAC;AACL,CAAC,CACJ,CAAA;AAED,oBAAoB;AACpB,oCAAoC;AACpC,MAAM,CAAC,YAAY,CACf,aAAa,EACb;IACI,WAAW,EACP,4FAA4F;QAC5F,mFAAmF;QACnF,oDAAoD;CAC3D,EACD,KAAK,IAAI,EAAE;IACP,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,cAAc,CAAC,iBAAiB,EAAE,CAAA;QAClD,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,4DAA4D;qBACrE;iBACJ;gBACD,OAAO,EAAE,IAAI;aAChB,CAAA;QACL,CAAC;QAED,kCAAkC;QAClC,MAAM,YAAY,GAAG,MAAM,SAAS,CAAC,QAAQ,CACzC,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,EAAE,CACb,CAAA;QACD,IAAI,YAAY,CAAC,GAAG,EAAE,CAAC;YACnB,OAAO,CAAC,GAAG,GAAG,YAAY,CAAC,GAAG,CAAA;QAClC,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YACf,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,0DAA0D;qBACnE;iBACJ;aACJ,CAAA;QACL,CAAC;QAED,OAAO;YACH,OAAO,EAAE;gBACL;oBACI,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,2BAA2B,OAAO,CAAC,GAAG,EAAE;iBACjD;aACJ;SACJ,CAAA;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,OAAO,GACT,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAC1D,GAAG,CAAC,KAAK,CAAC,qBAAqB,EAAE,OAAO,CAAC,CAAA;QACzC,OAAO;YACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC;YACtD,OAAO,EAAE,IAAI;SAChB,CAAA;IACL,CAAC;AACL,CAAC,CACJ,CAAA;AAED,uBAAuB;AACvB,6BAA6B;AAC7B,MAAM,CAAC,YAAY,CACf,gBAAgB,EAChB;IACI,WAAW,EAAE,+CAA+C;IAC5D,WAAW,EAAE;QACT,IAAI,EAAE,CAAC;aACF,MAAM,EAAE;aACR,QAAQ,CACL,wDAAwD,CAC3D;KACR;CACJ,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;IACf,IAAI,CAAC;QACD,MAAM,OAAO,GAAG,cAAc,CAAC,iBAAiB,EAAE,CAAA;QAClD,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,4DAA4D;qBACrE;iBACJ;gBACD,OAAO,EAAE,IAAI;aAChB,CAAA;QACL,CAAC;QAED,kCAAkC;QAClC,MAAM,YAAY,GAAG,MAAM,SAAS,CAAC,QAAQ,CACzC,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,EAAE,CACb,CAAA;QACD,IAAI,YAAY,CAAC,GAAG,EAAE,CAAC;YACnB,OAAO,CAAC,GAAG,GAAG,YAAY,CAAC,GAAG,CAAA;QAClC,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YACf,OAAO;gBACH,OAAO,EAAE;oBACL;wBACI,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,6DAA6D;qBACtE;iBACJ;gBACD,OAAO,EAAE,IAAI;aAChB,CAAA;QACL,CAAC;QAED,wBAAwB;QACxB,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAA;QAC3C,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAA;QAE1C,gCAAgC;QAChC,IAAI,QAAQ,GAAG,IAAI,CAAA;QACnB,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAChC,QAAQ,GAAG,GAAG,QAAQ,SAAS,CAAA;QACnC,CAAC;QAED,2BAA2B;QAC3B,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QAE/C,wBAAwB;QACxB,MAAM,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAEtD,GAAG,CAAC,IAAI,CAAC,uBAAuB,YAAY,EAAE,CAAC,CAAA;QAE/C,OAAO;YACH,OAAO,EAAE;gBACL;oBACI,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,2CAA2C,YAAY,WAAW,OAAO,CAAC,GAAG,CAAC,MAAM,aAAa;iBAC1G;aACJ;SACJ,CAAA;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,OAAO,GACT,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAC1D,GAAG,CAAC,KAAK,CAAC,wBAAwB,EAAE,OAAO,CAAC,CAAA;QAC5C,OAAO;YACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC;YACtD,OAAO,EAAE,IAAI;SAChB,CAAA;IACL,CAAC;AACL,CAAC,CACJ,CAAA;AAED,iDAAiD;AACjD,IAAI,eAAe,GAAG,KAAK,CAAA;AAE3B;;GAEG;AACH,KAAK,UAAU,qBAAqB;IAChC,IAAI,eAAe;QAAE,OAAO,IAAI,CAAA;IAEhC,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IAChE,IAAI,SAAS,EAAE,CAAC;QACZ,eAAe,GAAG,IAAI,CAAA;QACtB,GAAG,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;IACjD,CAAC;SAAM,CAAC;QACJ,GAAG,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;IACjD,CAAC;IACD,OAAO,SAAS,CAAA;AACpB,CAAC;AAED,uBAAuB;AACvB,KAAK,UAAU,IAAI;IACf,GAAG,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAA;IACtD,GAAG,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;IAEzC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAA;IAC5C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IAE/B,GAAG,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAA;AAC3C,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACnB,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAA;IAChC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACnB,CAAC,CAAC,CAAA"}
|
package/dist/logger.d.ts
ADDED
|
@@ -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"}
|
package/dist/session.js
ADDED
|
@@ -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
|
+
}
|