@eraserlabs/eraser-mcp 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,67 @@
1
+ # @eraserlabs/eraser-mcp
2
+
3
+ MCP (Model Context Protocol) server for generating diagrams with [eraser.io](https://eraser.io/?r=0).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npx @eraserlabs/eraser-mcp
9
+ ```
10
+
11
+ ## Configuration
12
+
13
+ ### Cursor
14
+
15
+ Add to `.cursor/mcp.json`:
16
+
17
+ ```json
18
+ {
19
+ "mcpServers": {
20
+ "eraser": {
21
+ "command": "npx",
22
+ "args": ["@eraserlabs/eraser-mcp"],
23
+ "env": {
24
+ "ERASER_API_TOKEN": "your-api-token"
25
+ }
26
+ }
27
+ }
28
+ }
29
+ ```
30
+
31
+ ### Claude Desktop
32
+
33
+ Add to your Claude Desktop config:
34
+
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "eraser": {
39
+ "command": "npx",
40
+ "args": ["@eraserlabs/eraser-mcp"],
41
+ "env": {
42
+ "ERASER_API_TOKEN": "your-api-token"
43
+ }
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ ## Environment Variables
50
+
51
+ - `ERASER_API_TOKEN` (required) - Your Eraser API token
52
+ - `ERASER_API_URL` (optional) - Custom API URL (defaults to `https://app.eraser.io/api/mcp`)
53
+ - `ERASER_OUTPUT_DIR` (optional) - Local directory to save rendered diagrams (defaults to `.eraser/scratchpad`)
54
+
55
+ ## Available Tools
56
+
57
+ - `renderSequenceDiagram` - Render sequence diagrams
58
+ - `renderEntityRelationshipDiagram` - Render ERD diagrams
59
+ - `renderCloudArchitectureDiagram` - Render cloud architecture diagrams
60
+ - `renderFlowchart` - Render flowcharts
61
+ - `renderBpmnDiagram` - Render BPMN diagrams
62
+ - `renderPrompt` - Generate diagrams from natural language using AI
63
+ - `renderElements` - Render multiple diagram elements
64
+
65
+ ## License
66
+
67
+ MIT
@@ -0,0 +1,3 @@
1
+ export type { McpToolDefinition, RenderElementsInput, RenderPromptInput, SingleDiagramInput, McpToolName, } from './tools';
2
+ export { mcpTools, mcpToolMap, isMcpToolName, DiagramTypes, singleDiagramTools, isSingleDiagramTool, } from './tools';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,GACZ,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,QAAQ,EACR,UAAU,EACV,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isSingleDiagramTool = exports.singleDiagramTools = exports.DiagramTypes = exports.isMcpToolName = exports.mcpToolMap = exports.mcpTools = void 0;
4
+ var tools_1 = require("./tools");
5
+ Object.defineProperty(exports, "mcpTools", { enumerable: true, get: function () { return tools_1.mcpTools; } });
6
+ Object.defineProperty(exports, "mcpToolMap", { enumerable: true, get: function () { return tools_1.mcpToolMap; } });
7
+ Object.defineProperty(exports, "isMcpToolName", { enumerable: true, get: function () { return tools_1.isMcpToolName; } });
8
+ Object.defineProperty(exports, "DiagramTypes", { enumerable: true, get: function () { return tools_1.DiagramTypes; } });
9
+ Object.defineProperty(exports, "singleDiagramTools", { enumerable: true, get: function () { return tools_1.singleDiagramTools; } });
10
+ Object.defineProperty(exports, "isSingleDiagramTool", { enumerable: true, get: function () { return tools_1.isSingleDiagramTool; } });
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Eraser MCP stdio adapter
4
+ *
5
+ * This adapter allows MCP clients (like Cursor, Claude Desktop) to communicate
6
+ * with the Eraser API via stdio transport.
7
+ *
8
+ * Usage:
9
+ * ERASER_API_TOKEN=your-token npx @eraserlabs/eraser-mcp
10
+ *
11
+ * Or configure in .cursor/mcp.json:
12
+ * {
13
+ * "mcpServers": {
14
+ * "eraser": {
15
+ * "command": "npx",
16
+ * "args": ["@eraserlabs/eraser-mcp"],
17
+ * "env": { "ERASER_API_TOKEN": "your-token" }
18
+ * }
19
+ * }
20
+ * }
21
+ */
22
+ export {};
23
+ //# sourceMappingURL=stdio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;GAmBG"}
package/dist/stdio.js ADDED
@@ -0,0 +1,245 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * Eraser MCP stdio adapter
5
+ *
6
+ * This adapter allows MCP clients (like Cursor, Claude Desktop) to communicate
7
+ * with the Eraser API via stdio transport.
8
+ *
9
+ * Usage:
10
+ * ERASER_API_TOKEN=your-token npx @eraserlabs/eraser-mcp
11
+ *
12
+ * Or configure in .cursor/mcp.json:
13
+ * {
14
+ * "mcpServers": {
15
+ * "eraser": {
16
+ * "command": "npx",
17
+ * "args": ["@eraserlabs/eraser-mcp"],
18
+ * "env": { "ERASER_API_TOKEN": "your-token" }
19
+ * }
20
+ * }
21
+ * }
22
+ */
23
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ var desc = Object.getOwnPropertyDescriptor(m, k);
26
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
27
+ desc = { enumerable: true, get: function() { return m[k]; } };
28
+ }
29
+ Object.defineProperty(o, k2, desc);
30
+ }) : (function(o, m, k, k2) {
31
+ if (k2 === undefined) k2 = k;
32
+ o[k2] = m[k];
33
+ }));
34
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
35
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
36
+ }) : function(o, v) {
37
+ o["default"] = v;
38
+ });
39
+ var __importStar = (this && this.__importStar) || function (mod) {
40
+ if (mod && mod.__esModule) return mod;
41
+ var result = {};
42
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
43
+ __setModuleDefault(result, mod);
44
+ return result;
45
+ };
46
+ Object.defineProperty(exports, "__esModule", { value: true });
47
+ const readline = __importStar(require("readline"));
48
+ const fs = __importStar(require("fs"));
49
+ const path = __importStar(require("path"));
50
+ const tools_1 = require("./tools");
51
+ const API_URL = process.env.ERASER_API_URL || 'https://app.eraser.io/api/mcp';
52
+ const ERASER_OUTPUT_DIR = process.env.ERASER_OUTPUT_DIR || '.eraser/scratchpad';
53
+ const API_TOKEN = process.env.ERASER_API_TOKEN;
54
+ function sendResponse(response) {
55
+ process.stdout.write(JSON.stringify(response) + '\n');
56
+ }
57
+ function sendError(id, code, message, data) {
58
+ sendResponse({
59
+ jsonrpc: '2.0',
60
+ id,
61
+ error: { code, message, data },
62
+ });
63
+ }
64
+ // Server capabilities and info for MCP handshake
65
+ const SERVER_INFO = {
66
+ name: 'eraser-mcp',
67
+ version: '1.0.0',
68
+ };
69
+ const SERVER_CAPABILITIES = {
70
+ tools: {},
71
+ };
72
+ // Convert mcpTools to MCP tool list format
73
+ function getToolsList() {
74
+ return tools_1.mcpTools.map((tool) => ({
75
+ name: tool.name,
76
+ description: tool.description,
77
+ inputSchema: tool.jsonSchema,
78
+ }));
79
+ }
80
+ /**
81
+ * Extracts the title from diagram code (looks for a line starting with "title ").
82
+ * Normalizes it to a valid filename.
83
+ */
84
+ function extractTitleFromCode(code) {
85
+ if (!code) {
86
+ return undefined;
87
+ }
88
+ const lines = code.split('\n');
89
+ for (const line of lines) {
90
+ const trimmed = line.trim();
91
+ if (trimmed.toLowerCase().startsWith('title ')) {
92
+ const title = trimmed.slice(6).trim(); // Remove "title " prefix
93
+ // Normalize to filename: lowercase, replace spaces/special chars with hyphens
94
+ return title
95
+ .toLowerCase()
96
+ .replace(/[^a-z0-9]+/g, '-')
97
+ .replace(/^-+|-+$/g, ''); // Trim leading/trailing hyphens
98
+ }
99
+ }
100
+ return undefined;
101
+ }
102
+ /**
103
+ * Downloads an image from a URL and saves it locally.
104
+ * Returns the local file path if successful, undefined otherwise.
105
+ */
106
+ async function saveImageLocally(imageUrl, diagramCode) {
107
+ try {
108
+ const outputDir = path.resolve(process.cwd(), ERASER_OUTPUT_DIR);
109
+ // Ensure output directory exists
110
+ await fs.promises.mkdir(outputDir, { recursive: true });
111
+ // Generate filename from title or timestamp
112
+ const title = extractTitleFromCode(diagramCode);
113
+ const timestamp = Date.now();
114
+ const filename = title ? `${title}-${timestamp}.png` : `diagram-${timestamp}.png`;
115
+ const localPath = path.join(outputDir, filename);
116
+ // Fetch the image
117
+ const response = await fetch(imageUrl);
118
+ if (!response.ok) {
119
+ return undefined;
120
+ }
121
+ // Save to disk
122
+ const buffer = Buffer.from(await response.arrayBuffer());
123
+ await fs.promises.writeFile(localPath, buffer);
124
+ return localPath;
125
+ }
126
+ catch {
127
+ // Silently fail - local saving is a nice-to-have
128
+ return undefined;
129
+ }
130
+ }
131
+ async function handleRequest(request) {
132
+ const id = request.id ?? null;
133
+ // Handle MCP protocol methods locally
134
+ if (request.method === 'initialize') {
135
+ sendResponse({
136
+ jsonrpc: '2.0',
137
+ id,
138
+ result: {
139
+ protocolVersion: '2024-11-05',
140
+ capabilities: SERVER_CAPABILITIES,
141
+ serverInfo: SERVER_INFO,
142
+ },
143
+ });
144
+ return;
145
+ }
146
+ if (request.method === 'notifications/initialized') {
147
+ // This is a notification, no response needed
148
+ return;
149
+ }
150
+ if (request.method === 'tools/list') {
151
+ sendResponse({
152
+ jsonrpc: '2.0',
153
+ id,
154
+ result: {
155
+ tools: getToolsList(),
156
+ },
157
+ });
158
+ return;
159
+ }
160
+ // For tools/call, forward to the API
161
+ if (request.method === 'tools/call') {
162
+ if (!API_TOKEN) {
163
+ sendError(id, -32000, 'ERASER_API_TOKEN environment variable is required');
164
+ return;
165
+ }
166
+ try {
167
+ const response = await fetch(API_URL, {
168
+ method: 'POST',
169
+ headers: {
170
+ 'Content-Type': 'application/json',
171
+ Authorization: `Bearer ${API_TOKEN}`,
172
+ },
173
+ body: JSON.stringify(request),
174
+ });
175
+ if (!response.ok) {
176
+ const text = await response.text();
177
+ sendError(id, -32000, `HTTP ${response.status}: ${text}`);
178
+ return;
179
+ }
180
+ const rpcResponse = (await response.json());
181
+ // Try to save the image locally if there's an imageUrl in the result
182
+ if (rpcResponse.result) {
183
+ const result = rpcResponse.result;
184
+ if (result.content?.[0]?.type === 'text' && result.content[0].text) {
185
+ try {
186
+ const renderResult = JSON.parse(result.content[0].text);
187
+ if (renderResult.imageUrl) {
188
+ // Extract diagram code from request params for title extraction
189
+ const params = request.params;
190
+ const diagramCode = params?.arguments?.code;
191
+ const localPath = await saveImageLocally(renderResult.imageUrl, diagramCode);
192
+ if (localPath) {
193
+ renderResult.localPath = localPath;
194
+ result.content[0].text = JSON.stringify(renderResult);
195
+ }
196
+ }
197
+ }
198
+ catch {
199
+ // If parsing fails, just return the original response
200
+ }
201
+ }
202
+ }
203
+ sendResponse(rpcResponse);
204
+ }
205
+ catch (error) {
206
+ const message = error instanceof Error ? error.message : 'Unknown error';
207
+ sendError(id, -32000, `Request failed: ${message}`);
208
+ }
209
+ return;
210
+ }
211
+ // Unknown method
212
+ sendError(id, -32601, `Method not found: ${request.method}`);
213
+ }
214
+ function main() {
215
+ const rl = readline.createInterface({
216
+ input: process.stdin,
217
+ output: process.stdout,
218
+ terminal: false,
219
+ });
220
+ rl.on('line', (line) => {
221
+ if (!line.trim()) {
222
+ return;
223
+ }
224
+ try {
225
+ const request = JSON.parse(line);
226
+ if (request.jsonrpc !== '2.0' || typeof request.method !== 'string') {
227
+ sendError(request.id ?? null, -32600, 'Invalid Request');
228
+ return;
229
+ }
230
+ void handleRequest(request);
231
+ }
232
+ catch {
233
+ sendError(null, -32700, 'Parse error');
234
+ }
235
+ });
236
+ rl.on('close', () => {
237
+ process.exit(0);
238
+ });
239
+ // Prevent unhandled promise rejections from crashing
240
+ process.on('unhandledRejection', (error) => {
241
+ const message = error instanceof Error ? error.message : 'Unknown error';
242
+ sendError(null, -32000, `Unhandled error: ${message}`);
243
+ });
244
+ }
245
+ main();
@@ -0,0 +1,304 @@
1
+ import { z } from 'zod';
2
+ type JsonSchema = Record<string, unknown>;
3
+ /**
4
+ * Settings duplicated from @eraserlabs/shared/modules/diagram-parser/settings
5
+ * We avoid importing to keep eraser-mcp buildable independently.
6
+ */
7
+ export declare const colorModeSettings: readonly ["pastel", "bold", "outline"];
8
+ export declare const styleModeSettings: readonly ["plain", "shadow", "watercolor"];
9
+ export declare const typefaceSettings: readonly ["rough", "clean", "mono"];
10
+ export declare const directionSettings: readonly ["up", "down", "left", "right"];
11
+ /**
12
+ * The diagram types supported by the MCP tools.
13
+ * Duplicated from DiagramTypes enum, excluding 'custom-diagram'.
14
+ */
15
+ export declare enum DiagramTypes {
16
+ SD = "sequence-diagram",
17
+ ERD = "entity-relationship-diagram",
18
+ CAD = "cloud-architecture-diagram",
19
+ FLOW = "flowchart-diagram",
20
+ BPMN = "bpmn-diagram"
21
+ }
22
+ declare const diagramElementSchema: z.ZodObject<{
23
+ type: z.ZodLiteral<"diagram">;
24
+ diagramType: z.ZodNativeEnum<typeof DiagramTypes>;
25
+ code: z.ZodString;
26
+ x: z.ZodOptional<z.ZodNumber>;
27
+ y: z.ZodOptional<z.ZodNumber>;
28
+ }, "strip", z.ZodTypeAny, {
29
+ type: "diagram";
30
+ code: string;
31
+ diagramType: DiagramTypes;
32
+ x?: number | undefined;
33
+ y?: number | undefined;
34
+ }, {
35
+ type: "diagram";
36
+ code: string;
37
+ diagramType: DiagramTypes;
38
+ x?: number | undefined;
39
+ y?: number | undefined;
40
+ }>;
41
+ declare const renderPromptSchema: z.ZodObject<{
42
+ padding: z.ZodOptional<z.ZodNumber>;
43
+ scale: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<3>]>>;
44
+ background: z.ZodOptional<z.ZodBoolean>;
45
+ theme: z.ZodOptional<z.ZodEnum<["light", "dark"]>>;
46
+ format: z.ZodOptional<z.ZodEnum<["png", "jpeg"]>>;
47
+ selection: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
48
+ ignoreElements: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
49
+ limitToSelection: z.ZodOptional<z.ZodBoolean>;
50
+ customIcons: z.ZodOptional<z.ZodArray<z.ZodUnknown, "many">>;
51
+ typeface: z.ZodOptional<z.ZodEnum<["rough", "clean", "mono"]>>;
52
+ colorMode: z.ZodOptional<z.ZodEnum<["pastel", "bold", "outline"]>>;
53
+ styleMode: z.ZodOptional<z.ZodEnum<["plain", "shadow", "watercolor"]>>;
54
+ direction: z.ZodOptional<z.ZodEnum<["up", "down", "left", "right"]>>;
55
+ title: z.ZodOptional<z.ZodString>;
56
+ } & {
57
+ text: z.ZodString;
58
+ returnFile: z.ZodOptional<z.ZodBoolean>;
59
+ diagramType: z.ZodOptional<z.ZodString>;
60
+ mode: z.ZodOptional<z.ZodString>;
61
+ priorRequestId: z.ZodOptional<z.ZodString>;
62
+ attachments: z.ZodOptional<z.ZodArray<z.ZodUnknown, "many">>;
63
+ contextId: z.ZodOptional<z.ZodString>;
64
+ git: z.ZodOptional<z.ZodUnknown>;
65
+ fileOptions: z.ZodOptional<z.ZodObject<{
66
+ create: z.ZodOptional<z.ZodBoolean>;
67
+ linkAccess: z.ZodOptional<z.ZodEnum<["no-link-access", "anyone-with-link-can-edit", "publicly-viewable", "publicly-editable"]>>;
68
+ }, "strip", z.ZodTypeAny, {
69
+ create?: boolean | undefined;
70
+ linkAccess?: "no-link-access" | "anyone-with-link-can-edit" | "publicly-viewable" | "publicly-editable" | undefined;
71
+ }, {
72
+ create?: boolean | undefined;
73
+ linkAccess?: "no-link-access" | "anyone-with-link-can-edit" | "publicly-viewable" | "publicly-editable" | undefined;
74
+ }>>;
75
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
76
+ padding: z.ZodOptional<z.ZodNumber>;
77
+ scale: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<3>]>>;
78
+ background: z.ZodOptional<z.ZodBoolean>;
79
+ theme: z.ZodOptional<z.ZodEnum<["light", "dark"]>>;
80
+ format: z.ZodOptional<z.ZodEnum<["png", "jpeg"]>>;
81
+ selection: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
82
+ ignoreElements: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
83
+ limitToSelection: z.ZodOptional<z.ZodBoolean>;
84
+ customIcons: z.ZodOptional<z.ZodArray<z.ZodUnknown, "many">>;
85
+ typeface: z.ZodOptional<z.ZodEnum<["rough", "clean", "mono"]>>;
86
+ colorMode: z.ZodOptional<z.ZodEnum<["pastel", "bold", "outline"]>>;
87
+ styleMode: z.ZodOptional<z.ZodEnum<["plain", "shadow", "watercolor"]>>;
88
+ direction: z.ZodOptional<z.ZodEnum<["up", "down", "left", "right"]>>;
89
+ title: z.ZodOptional<z.ZodString>;
90
+ } & {
91
+ text: z.ZodString;
92
+ returnFile: z.ZodOptional<z.ZodBoolean>;
93
+ diagramType: z.ZodOptional<z.ZodString>;
94
+ mode: z.ZodOptional<z.ZodString>;
95
+ priorRequestId: z.ZodOptional<z.ZodString>;
96
+ attachments: z.ZodOptional<z.ZodArray<z.ZodUnknown, "many">>;
97
+ contextId: z.ZodOptional<z.ZodString>;
98
+ git: z.ZodOptional<z.ZodUnknown>;
99
+ fileOptions: z.ZodOptional<z.ZodObject<{
100
+ create: z.ZodOptional<z.ZodBoolean>;
101
+ linkAccess: z.ZodOptional<z.ZodEnum<["no-link-access", "anyone-with-link-can-edit", "publicly-viewable", "publicly-editable"]>>;
102
+ }, "strip", z.ZodTypeAny, {
103
+ create?: boolean | undefined;
104
+ linkAccess?: "no-link-access" | "anyone-with-link-can-edit" | "publicly-viewable" | "publicly-editable" | undefined;
105
+ }, {
106
+ create?: boolean | undefined;
107
+ linkAccess?: "no-link-access" | "anyone-with-link-can-edit" | "publicly-viewable" | "publicly-editable" | undefined;
108
+ }>>;
109
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
110
+ padding: z.ZodOptional<z.ZodNumber>;
111
+ scale: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<3>]>>;
112
+ background: z.ZodOptional<z.ZodBoolean>;
113
+ theme: z.ZodOptional<z.ZodEnum<["light", "dark"]>>;
114
+ format: z.ZodOptional<z.ZodEnum<["png", "jpeg"]>>;
115
+ selection: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
116
+ ignoreElements: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
117
+ limitToSelection: z.ZodOptional<z.ZodBoolean>;
118
+ customIcons: z.ZodOptional<z.ZodArray<z.ZodUnknown, "many">>;
119
+ typeface: z.ZodOptional<z.ZodEnum<["rough", "clean", "mono"]>>;
120
+ colorMode: z.ZodOptional<z.ZodEnum<["pastel", "bold", "outline"]>>;
121
+ styleMode: z.ZodOptional<z.ZodEnum<["plain", "shadow", "watercolor"]>>;
122
+ direction: z.ZodOptional<z.ZodEnum<["up", "down", "left", "right"]>>;
123
+ title: z.ZodOptional<z.ZodString>;
124
+ } & {
125
+ text: z.ZodString;
126
+ returnFile: z.ZodOptional<z.ZodBoolean>;
127
+ diagramType: z.ZodOptional<z.ZodString>;
128
+ mode: z.ZodOptional<z.ZodString>;
129
+ priorRequestId: z.ZodOptional<z.ZodString>;
130
+ attachments: z.ZodOptional<z.ZodArray<z.ZodUnknown, "many">>;
131
+ contextId: z.ZodOptional<z.ZodString>;
132
+ git: z.ZodOptional<z.ZodUnknown>;
133
+ fileOptions: z.ZodOptional<z.ZodObject<{
134
+ create: z.ZodOptional<z.ZodBoolean>;
135
+ linkAccess: z.ZodOptional<z.ZodEnum<["no-link-access", "anyone-with-link-can-edit", "publicly-viewable", "publicly-editable"]>>;
136
+ }, "strip", z.ZodTypeAny, {
137
+ create?: boolean | undefined;
138
+ linkAccess?: "no-link-access" | "anyone-with-link-can-edit" | "publicly-viewable" | "publicly-editable" | undefined;
139
+ }, {
140
+ create?: boolean | undefined;
141
+ linkAccess?: "no-link-access" | "anyone-with-link-can-edit" | "publicly-viewable" | "publicly-editable" | undefined;
142
+ }>>;
143
+ }, z.ZodTypeAny, "passthrough">>;
144
+ declare const renderElementsSchema: z.ZodObject<{
145
+ padding: z.ZodOptional<z.ZodNumber>;
146
+ scale: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<3>]>>;
147
+ background: z.ZodOptional<z.ZodBoolean>;
148
+ theme: z.ZodOptional<z.ZodEnum<["light", "dark"]>>;
149
+ format: z.ZodOptional<z.ZodEnum<["png", "jpeg"]>>;
150
+ selection: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
151
+ ignoreElements: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
152
+ limitToSelection: z.ZodOptional<z.ZodBoolean>;
153
+ customIcons: z.ZodOptional<z.ZodArray<z.ZodUnknown, "many">>;
154
+ typeface: z.ZodOptional<z.ZodEnum<["rough", "clean", "mono"]>>;
155
+ colorMode: z.ZodOptional<z.ZodEnum<["pastel", "bold", "outline"]>>;
156
+ styleMode: z.ZodOptional<z.ZodEnum<["plain", "shadow", "watercolor"]>>;
157
+ direction: z.ZodOptional<z.ZodEnum<["up", "down", "left", "right"]>>;
158
+ title: z.ZodOptional<z.ZodString>;
159
+ } & {
160
+ elements: z.ZodArray<z.ZodObject<{
161
+ type: z.ZodLiteral<"diagram">;
162
+ diagramType: z.ZodNativeEnum<typeof DiagramTypes>;
163
+ code: z.ZodString;
164
+ x: z.ZodOptional<z.ZodNumber>;
165
+ y: z.ZodOptional<z.ZodNumber>;
166
+ }, "strip", z.ZodTypeAny, {
167
+ type: "diagram";
168
+ code: string;
169
+ diagramType: DiagramTypes;
170
+ x?: number | undefined;
171
+ y?: number | undefined;
172
+ }, {
173
+ type: "diagram";
174
+ code: string;
175
+ diagramType: DiagramTypes;
176
+ x?: number | undefined;
177
+ y?: number | undefined;
178
+ }>, "many">;
179
+ returnFile: z.ZodOptional<z.ZodBoolean>;
180
+ fileName: z.ZodOptional<z.ZodString>;
181
+ teamId: z.ZodOptional<z.ZodString>;
182
+ returnElements: z.ZodOptional<z.ZodBoolean>;
183
+ skipCache: z.ZodOptional<z.ZodBoolean>;
184
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
185
+ padding: z.ZodOptional<z.ZodNumber>;
186
+ scale: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<3>]>>;
187
+ background: z.ZodOptional<z.ZodBoolean>;
188
+ theme: z.ZodOptional<z.ZodEnum<["light", "dark"]>>;
189
+ format: z.ZodOptional<z.ZodEnum<["png", "jpeg"]>>;
190
+ selection: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
191
+ ignoreElements: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
192
+ limitToSelection: z.ZodOptional<z.ZodBoolean>;
193
+ customIcons: z.ZodOptional<z.ZodArray<z.ZodUnknown, "many">>;
194
+ typeface: z.ZodOptional<z.ZodEnum<["rough", "clean", "mono"]>>;
195
+ colorMode: z.ZodOptional<z.ZodEnum<["pastel", "bold", "outline"]>>;
196
+ styleMode: z.ZodOptional<z.ZodEnum<["plain", "shadow", "watercolor"]>>;
197
+ direction: z.ZodOptional<z.ZodEnum<["up", "down", "left", "right"]>>;
198
+ title: z.ZodOptional<z.ZodString>;
199
+ } & {
200
+ elements: z.ZodArray<z.ZodObject<{
201
+ type: z.ZodLiteral<"diagram">;
202
+ diagramType: z.ZodNativeEnum<typeof DiagramTypes>;
203
+ code: z.ZodString;
204
+ x: z.ZodOptional<z.ZodNumber>;
205
+ y: z.ZodOptional<z.ZodNumber>;
206
+ }, "strip", z.ZodTypeAny, {
207
+ type: "diagram";
208
+ code: string;
209
+ diagramType: DiagramTypes;
210
+ x?: number | undefined;
211
+ y?: number | undefined;
212
+ }, {
213
+ type: "diagram";
214
+ code: string;
215
+ diagramType: DiagramTypes;
216
+ x?: number | undefined;
217
+ y?: number | undefined;
218
+ }>, "many">;
219
+ returnFile: z.ZodOptional<z.ZodBoolean>;
220
+ fileName: z.ZodOptional<z.ZodString>;
221
+ teamId: z.ZodOptional<z.ZodString>;
222
+ returnElements: z.ZodOptional<z.ZodBoolean>;
223
+ skipCache: z.ZodOptional<z.ZodBoolean>;
224
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
225
+ padding: z.ZodOptional<z.ZodNumber>;
226
+ scale: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<3>]>>;
227
+ background: z.ZodOptional<z.ZodBoolean>;
228
+ theme: z.ZodOptional<z.ZodEnum<["light", "dark"]>>;
229
+ format: z.ZodOptional<z.ZodEnum<["png", "jpeg"]>>;
230
+ selection: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
231
+ ignoreElements: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
232
+ limitToSelection: z.ZodOptional<z.ZodBoolean>;
233
+ customIcons: z.ZodOptional<z.ZodArray<z.ZodUnknown, "many">>;
234
+ typeface: z.ZodOptional<z.ZodEnum<["rough", "clean", "mono"]>>;
235
+ colorMode: z.ZodOptional<z.ZodEnum<["pastel", "bold", "outline"]>>;
236
+ styleMode: z.ZodOptional<z.ZodEnum<["plain", "shadow", "watercolor"]>>;
237
+ direction: z.ZodOptional<z.ZodEnum<["up", "down", "left", "right"]>>;
238
+ title: z.ZodOptional<z.ZodString>;
239
+ } & {
240
+ elements: z.ZodArray<z.ZodObject<{
241
+ type: z.ZodLiteral<"diagram">;
242
+ diagramType: z.ZodNativeEnum<typeof DiagramTypes>;
243
+ code: z.ZodString;
244
+ x: z.ZodOptional<z.ZodNumber>;
245
+ y: z.ZodOptional<z.ZodNumber>;
246
+ }, "strip", z.ZodTypeAny, {
247
+ type: "diagram";
248
+ code: string;
249
+ diagramType: DiagramTypes;
250
+ x?: number | undefined;
251
+ y?: number | undefined;
252
+ }, {
253
+ type: "diagram";
254
+ code: string;
255
+ diagramType: DiagramTypes;
256
+ x?: number | undefined;
257
+ y?: number | undefined;
258
+ }>, "many">;
259
+ returnFile: z.ZodOptional<z.ZodBoolean>;
260
+ fileName: z.ZodOptional<z.ZodString>;
261
+ teamId: z.ZodOptional<z.ZodString>;
262
+ returnElements: z.ZodOptional<z.ZodBoolean>;
263
+ skipCache: z.ZodOptional<z.ZodBoolean>;
264
+ }, z.ZodTypeAny, "passthrough">>;
265
+ declare const singleDiagramSchema: z.ZodObject<{
266
+ code: z.ZodString;
267
+ theme: z.ZodOptional<z.ZodEnum<["light", "dark"]>>;
268
+ colorMode: z.ZodOptional<z.ZodEnum<["pastel", "bold", "outline"]>>;
269
+ styleMode: z.ZodOptional<z.ZodEnum<["plain", "shadow", "watercolor"]>>;
270
+ typeface: z.ZodOptional<z.ZodEnum<["rough", "clean", "mono"]>>;
271
+ background: z.ZodOptional<z.ZodBoolean>;
272
+ }, "strip", z.ZodTypeAny, {
273
+ code: string;
274
+ background?: boolean | undefined;
275
+ theme?: "light" | "dark" | undefined;
276
+ typeface?: "rough" | "clean" | "mono" | undefined;
277
+ colorMode?: "pastel" | "bold" | "outline" | undefined;
278
+ styleMode?: "plain" | "shadow" | "watercolor" | undefined;
279
+ }, {
280
+ code: string;
281
+ background?: boolean | undefined;
282
+ theme?: "light" | "dark" | undefined;
283
+ typeface?: "rough" | "clean" | "mono" | undefined;
284
+ colorMode?: "pastel" | "bold" | "outline" | undefined;
285
+ styleMode?: "plain" | "shadow" | "watercolor" | undefined;
286
+ }>;
287
+ export type RenderPromptInput = z.infer<typeof renderPromptSchema>;
288
+ export type DiagramElementInput = z.infer<typeof diagramElementSchema>;
289
+ export type RenderElementsInput = z.infer<typeof renderElementsSchema>;
290
+ export type SingleDiagramInput = z.infer<typeof singleDiagramSchema>;
291
+ export type McpToolDefinition<TInput> = {
292
+ name: string;
293
+ description: string;
294
+ schema: z.ZodType<TInput>;
295
+ jsonSchema: JsonSchema;
296
+ };
297
+ export declare const mcpTools: ReadonlyArray<McpToolDefinition<unknown>>;
298
+ export type McpToolName = typeof mcpTools[number]['name'];
299
+ export declare function isMcpToolName(name: unknown): name is McpToolName;
300
+ export declare const mcpToolMap: Map<string, McpToolDefinition<unknown>>;
301
+ export declare const singleDiagramTools: Record<string, DiagramTypes>;
302
+ export declare function isSingleDiagramTool(name: string): name is keyof typeof singleDiagramTools;
303
+ export {};
304
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1C;;;GAGG;AACH,eAAO,MAAM,iBAAiB,wCAAyC,CAAC;AACxE,eAAO,MAAM,iBAAiB,4CAA6C,CAAC;AAC5E,eAAO,MAAM,gBAAgB,qCAAsC,CAAC;AACpE,eAAO,MAAM,iBAAiB,0CAA2C,CAAC;AAE1E;;;GAGG;AACH,oBAAY,YAAY;IACtB,EAAE,qBAAqB;IACvB,GAAG,gCAAgC;IACnC,GAAG,+BAA+B;IAClC,IAAI,sBAAsB;IAC1B,IAAI,iBAAiB;CACtB;AAED,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;EAMxB,CAAC;AAqBH,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAwBR,CAAC;AAEjB,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCASV,CAAC;AAGjB,QAAA,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;EAOvB,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACnE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACvE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACvE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAErE,MAAM,MAAM,iBAAiB,CAAC,MAAM,IAAI;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1B,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC;AA+RF,eAAO,MAAM,QAAQ,EAAE,aAAa,CAAC,iBAAiB,CAAC,OAAO,CAAC,CA6C9D,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,WAAW,CAEhE;AAED,eAAO,MAAM,UAAU,yCAAqD,CAAC;AAG7E,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAM3D,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,MAAM,OAAO,kBAAkB,CAEzF"}
package/dist/tools.js ADDED
@@ -0,0 +1,431 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isSingleDiagramTool = exports.singleDiagramTools = exports.mcpToolMap = exports.isMcpToolName = exports.mcpTools = exports.DiagramTypes = exports.directionSettings = exports.typefaceSettings = exports.styleModeSettings = exports.colorModeSettings = void 0;
4
+ const zod_1 = require("zod");
5
+ /**
6
+ * Settings duplicated from @eraserlabs/shared/modules/diagram-parser/settings
7
+ * We avoid importing to keep eraser-mcp buildable independently.
8
+ */
9
+ exports.colorModeSettings = ['pastel', 'bold', 'outline'];
10
+ exports.styleModeSettings = ['plain', 'shadow', 'watercolor'];
11
+ exports.typefaceSettings = ['rough', 'clean', 'mono'];
12
+ exports.directionSettings = ['up', 'down', 'left', 'right'];
13
+ /**
14
+ * The diagram types supported by the MCP tools.
15
+ * Duplicated from DiagramTypes enum, excluding 'custom-diagram'.
16
+ */
17
+ var DiagramTypes;
18
+ (function (DiagramTypes) {
19
+ DiagramTypes["SD"] = "sequence-diagram";
20
+ DiagramTypes["ERD"] = "entity-relationship-diagram";
21
+ DiagramTypes["CAD"] = "cloud-architecture-diagram";
22
+ DiagramTypes["FLOW"] = "flowchart-diagram";
23
+ DiagramTypes["BPMN"] = "bpmn-diagram";
24
+ })(DiagramTypes = exports.DiagramTypes || (exports.DiagramTypes = {}));
25
+ const diagramElementSchema = zod_1.z.object({
26
+ type: zod_1.z.literal('diagram'),
27
+ diagramType: zod_1.z.nativeEnum(DiagramTypes),
28
+ code: zod_1.z.string(),
29
+ x: zod_1.z.number().optional(),
30
+ y: zod_1.z.number().optional(),
31
+ });
32
+ const renderOptionsSchema = zod_1.z
33
+ .object({
34
+ padding: zod_1.z.number().optional(),
35
+ scale: zod_1.z.union([zod_1.z.literal(1), zod_1.z.literal(2), zod_1.z.literal(3)]).optional(),
36
+ background: zod_1.z.boolean().optional(),
37
+ theme: zod_1.z.enum(['light', 'dark']).optional(),
38
+ format: zod_1.z.enum(['png', 'jpeg']).optional(),
39
+ selection: zod_1.z.array(zod_1.z.string()).optional(),
40
+ ignoreElements: zod_1.z.array(zod_1.z.string()).optional(),
41
+ limitToSelection: zod_1.z.boolean().optional(),
42
+ customIcons: zod_1.z.array(zod_1.z.unknown()).optional(),
43
+ typeface: zod_1.z.enum(exports.typefaceSettings).optional(),
44
+ colorMode: zod_1.z.enum(exports.colorModeSettings).optional(),
45
+ styleMode: zod_1.z.enum(exports.styleModeSettings).optional(),
46
+ direction: zod_1.z.enum(exports.directionSettings).optional(),
47
+ title: zod_1.z.string().optional(),
48
+ })
49
+ .passthrough();
50
+ const renderPromptSchema = renderOptionsSchema
51
+ .extend({
52
+ text: zod_1.z.string(),
53
+ returnFile: zod_1.z.boolean().optional(),
54
+ diagramType: zod_1.z.string().optional(),
55
+ mode: zod_1.z.string().optional(),
56
+ priorRequestId: zod_1.z.string().optional(),
57
+ attachments: zod_1.z.array(zod_1.z.unknown()).optional(),
58
+ contextId: zod_1.z.string().optional(),
59
+ git: zod_1.z.unknown().optional(),
60
+ fileOptions: zod_1.z
61
+ .object({
62
+ create: zod_1.z.boolean().optional(),
63
+ linkAccess: zod_1.z
64
+ .enum([
65
+ 'no-link-access',
66
+ 'anyone-with-link-can-edit',
67
+ 'publicly-viewable',
68
+ 'publicly-editable',
69
+ ])
70
+ .optional(),
71
+ })
72
+ .optional(),
73
+ })
74
+ .passthrough();
75
+ const renderElementsSchema = renderOptionsSchema
76
+ .extend({
77
+ elements: zod_1.z.array(diagramElementSchema),
78
+ returnFile: zod_1.z.boolean().optional(),
79
+ fileName: zod_1.z.string().optional(),
80
+ teamId: zod_1.z.string().optional(),
81
+ returnElements: zod_1.z.boolean().optional(),
82
+ skipCache: zod_1.z.boolean().optional(),
83
+ })
84
+ .passthrough();
85
+ // Schema for individual diagram type tools
86
+ const singleDiagramSchema = zod_1.z.object({
87
+ code: zod_1.z.string(),
88
+ theme: zod_1.z.enum(['light', 'dark']).optional(),
89
+ colorMode: zod_1.z.enum(exports.colorModeSettings).optional(),
90
+ styleMode: zod_1.z.enum(exports.styleModeSettings).optional(),
91
+ typeface: zod_1.z.enum(exports.typefaceSettings).optional(),
92
+ background: zod_1.z.boolean().optional(),
93
+ });
94
+ const renderPromptJsonSchema = {
95
+ type: 'object',
96
+ additionalProperties: true,
97
+ required: ['text'],
98
+ properties: {
99
+ text: { type: 'string' },
100
+ returnFile: { type: 'boolean' },
101
+ diagramType: { type: 'string' },
102
+ mode: { type: 'string' },
103
+ priorRequestId: { type: 'string' },
104
+ attachments: { type: 'array', items: {} },
105
+ contextId: { type: 'string' },
106
+ git: {},
107
+ fileOptions: {
108
+ type: 'object',
109
+ additionalProperties: false,
110
+ properties: {
111
+ create: { type: 'boolean' },
112
+ linkAccess: {
113
+ type: 'string',
114
+ enum: [
115
+ 'no-link-access',
116
+ 'anyone-with-link-can-edit',
117
+ 'publicly-viewable',
118
+ 'publicly-editable',
119
+ ],
120
+ },
121
+ },
122
+ },
123
+ padding: { type: 'number' },
124
+ scale: { type: 'number', enum: [1, 2, 3] },
125
+ background: { type: 'boolean' },
126
+ theme: { type: 'string', enum: ['light', 'dark'] },
127
+ format: { type: 'string', enum: ['png', 'jpeg'] },
128
+ selection: { type: 'array', items: { type: 'string' } },
129
+ typeface: { type: 'string', enum: [...exports.typefaceSettings] },
130
+ colorMode: { type: 'string', enum: [...exports.colorModeSettings] },
131
+ styleMode: { type: 'string', enum: [...exports.styleModeSettings] },
132
+ direction: { type: 'string', enum: [...exports.directionSettings] },
133
+ title: { type: 'string' },
134
+ },
135
+ };
136
+ const renderElementsJsonSchema = {
137
+ type: 'object',
138
+ additionalProperties: true,
139
+ required: ['elements'],
140
+ properties: {
141
+ elements: {
142
+ type: 'array',
143
+ items: {
144
+ type: 'object',
145
+ required: ['type', 'diagramType', 'code'],
146
+ properties: {
147
+ type: { type: 'string', const: 'diagram' },
148
+ diagramType: {
149
+ type: 'string',
150
+ enum: Object.values(DiagramTypes),
151
+ },
152
+ code: { type: 'string' },
153
+ },
154
+ },
155
+ },
156
+ fileName: { type: 'string' },
157
+ teamId: { type: 'string' },
158
+ returnElements: { type: 'boolean' },
159
+ skipCache: { type: 'boolean' },
160
+ padding: { type: 'number' },
161
+ scale: { type: 'number', enum: [1, 2, 3] },
162
+ background: { type: 'boolean' },
163
+ theme: { type: 'string', enum: ['light', 'dark'] },
164
+ format: { type: 'string', enum: ['png', 'jpeg'] },
165
+ typeface: { type: 'string', enum: [...exports.typefaceSettings] },
166
+ colorMode: { type: 'string', enum: [...exports.colorModeSettings] },
167
+ styleMode: { type: 'string', enum: [...exports.styleModeSettings] },
168
+ direction: { type: 'string', enum: [...exports.directionSettings] },
169
+ },
170
+ };
171
+ const singleDiagramJsonSchema = {
172
+ type: 'object',
173
+ required: ['code'],
174
+ properties: {
175
+ code: { type: 'string', description: 'The diagram code in Eraser syntax' },
176
+ theme: { type: 'string', enum: ['light', 'dark'] },
177
+ colorMode: { type: 'string', enum: [...exports.colorModeSettings] },
178
+ styleMode: { type: 'string', enum: [...exports.styleModeSettings] },
179
+ typeface: { type: 'string', enum: [...exports.typefaceSettings] },
180
+ background: { type: 'boolean', description: 'Whether to include a solid background' },
181
+ },
182
+ };
183
+ // Diagram type descriptions with syntax examples
184
+ const SEQUENCE_DIAGRAM_DESCRIPTION = `Render a sequence diagram. Use Eraser's sequence diagram syntax.
185
+
186
+ Example syntax:
187
+ \`\`\`
188
+ title Authentication Flow
189
+
190
+ // Define actors with icons
191
+ User [icon: user]
192
+ Browser [icon: globe]
193
+ Server [icon: server]
194
+ Database [icon: database]
195
+
196
+ // Messages between actors
197
+ User > Browser: Enter credentials
198
+ Browser > Server: POST /login
199
+ Server > Database: Query user
200
+ Database > Server: User data
201
+ Server > Browser: JWT token
202
+ Browser > User: Login success
203
+
204
+ // Control flow
205
+ alt [label: "Valid credentials"] {
206
+ Server > Browser: 200 OK
207
+ }
208
+ else [label: "Invalid"] {
209
+ Server > Browser: 401 Unauthorized
210
+ }
211
+
212
+ // Activation bars
213
+ activate Server
214
+ Server > Database: Query
215
+ deactivate Server
216
+
217
+ // Loops
218
+ loop [label: "Retry 3 times"] {
219
+ Browser > Server: Request
220
+ }
221
+ \`\`\``;
222
+ const ERD_DESCRIPTION = `Render an entity-relationship diagram. Use Eraser's ERD syntax.
223
+
224
+ Example syntax:
225
+ \`\`\`
226
+ title E-commerce Database
227
+
228
+ // Define tables with columns
229
+ users [icon: user, color: blue] {
230
+ id int pk
231
+ email string
232
+ name string
233
+ created_at timestamp
234
+ }
235
+
236
+ orders [icon: shopping-cart, color: green] {
237
+ id int pk
238
+ user_id int
239
+ total decimal
240
+ status string
241
+ created_at timestamp
242
+ }
243
+
244
+ products [icon: box, color: orange] {
245
+ id int pk
246
+ name string
247
+ price decimal
248
+ stock int
249
+ }
250
+
251
+ order_items [icon: list] {
252
+ order_id int pk
253
+ product_id int pk
254
+ quantity int
255
+ price decimal
256
+ }
257
+
258
+ // Relationships
259
+ users.id < orders.user_id
260
+ orders.id < order_items.order_id
261
+ products.id < order_items.product_id
262
+ \`\`\``;
263
+ const CLOUD_ARCHITECTURE_DESCRIPTION = `Render a cloud architecture diagram. Use Eraser's cloud architecture syntax.
264
+
265
+ Example syntax:
266
+ \`\`\`
267
+ title AWS Microservices Architecture
268
+
269
+ // Groups with cloud provider icons
270
+ AWS Cloud [icon: aws] {
271
+ VPC [icon: aws-vpc] {
272
+ Public Subnet {
273
+ ALB [icon: aws-elb]
274
+ NAT Gateway [icon: aws-nat-gateway]
275
+ }
276
+ Private Subnet {
277
+ ECS Cluster [icon: aws-ecs] {
278
+ API Service [icon: aws-lambda]
279
+ Worker Service [icon: aws-lambda]
280
+ }
281
+ RDS [icon: aws-rds]
282
+ ElastiCache [icon: aws-elasticache]
283
+ }
284
+ }
285
+ S3 [icon: aws-s3]
286
+ CloudFront [icon: aws-cloudfront]
287
+ }
288
+
289
+ Users [icon: users]
290
+
291
+ // Connections
292
+ Users > CloudFront
293
+ CloudFront > ALB
294
+ ALB > API Service
295
+ API Service > RDS
296
+ API Service > ElastiCache
297
+ Worker Service > S3
298
+ \`\`\``;
299
+ const FLOWCHART_DESCRIPTION = `Render a flowchart diagram. Use Eraser's flowchart syntax.
300
+
301
+ Example syntax:
302
+ \`\`\`
303
+ title User Registration Flow
304
+
305
+ // Nodes with shapes and icons
306
+ Start [shape: oval, icon: play]
307
+ Enter Details [icon: edit]
308
+ Valid Email? [shape: diamond, icon: help-circle]
309
+ Send Verification [icon: mail]
310
+ Email Verified? [shape: diamond]
311
+ Create Account [icon: user-plus, color: green]
312
+ Show Error [icon: alert-triangle, color: red]
313
+ End [shape: oval, icon: check]
314
+
315
+ // Groups
316
+ Validation [color: blue] {
317
+ Check Password Strength [icon: lock]
318
+ Password OK? [shape: diamond]
319
+ }
320
+
321
+ // Connections with labels
322
+ Start > Enter Details
323
+ Enter Details > Valid Email?
324
+ Valid Email? > Send Verification: Yes
325
+ Valid Email? > Show Error: No
326
+ Send Verification > Email Verified?
327
+ Email Verified? > Create Account: Yes
328
+ Email Verified? > Show Error: No
329
+ Create Account > End
330
+ Show Error > Enter Details
331
+ \`\`\``;
332
+ const BPMN_DESCRIPTION = `Render a BPMN (Business Process Model and Notation) diagram. Use Eraser's BPMN syntax.
333
+
334
+ Example syntax:
335
+ \`\`\`
336
+ title Order Fulfillment Process
337
+
338
+ // Swimlanes (pools)
339
+ Customer [color: blue] {
340
+ Place Order [type: event, icon: shopping-cart]
341
+ Receive Confirmation [type: event, icon: mail]
342
+ Receive Package [type: event, icon: package]
343
+ }
344
+
345
+ Sales [color: green] {
346
+ Process Order [icon: clipboard]
347
+ Check Inventory [icon: database]
348
+ In Stock? [type: gateway, icon: help-circle]
349
+ Create Backorder [icon: clock]
350
+ Confirm Order [icon: check]
351
+ }
352
+
353
+ Warehouse [color: orange] {
354
+ Pick Items [icon: box]
355
+ Pack Order [icon: package]
356
+ Ship Order [icon: truck]
357
+ }
358
+
359
+ // Flow connections (use --> for message flows between pools)
360
+ Place Order --> Process Order: Order details
361
+ Process Order > Check Inventory
362
+ Check Inventory > In Stock?
363
+ In Stock? > Confirm Order: Yes
364
+ In Stock? > Create Backorder: No
365
+ Confirm Order --> Receive Confirmation: Confirmation email
366
+ Confirm Order > Pick Items
367
+ Pick Items > Pack Order
368
+ Pack Order > Ship Order
369
+ Ship Order --> Receive Package: Delivery
370
+ \`\`\``;
371
+ exports.mcpTools = [
372
+ {
373
+ name: 'renderPrompt',
374
+ description: 'Generate a diagram using AI from a natural language prompt, existing code, infrastructure configuration, or other diagram languages. Best for when you want AI to create the diagram code for you.',
375
+ schema: renderPromptSchema,
376
+ jsonSchema: renderPromptJsonSchema,
377
+ },
378
+ {
379
+ name: 'renderElements',
380
+ description: 'Render multiple diagram elements. Advanced use case for rendering multiple diagrams at once.',
381
+ schema: renderElementsSchema,
382
+ jsonSchema: renderElementsJsonSchema,
383
+ },
384
+ {
385
+ name: 'renderSequenceDiagram',
386
+ description: SEQUENCE_DIAGRAM_DESCRIPTION,
387
+ schema: singleDiagramSchema,
388
+ jsonSchema: singleDiagramJsonSchema,
389
+ },
390
+ {
391
+ name: 'renderEntityRelationshipDiagram',
392
+ description: ERD_DESCRIPTION,
393
+ schema: singleDiagramSchema,
394
+ jsonSchema: singleDiagramJsonSchema,
395
+ },
396
+ {
397
+ name: 'renderCloudArchitectureDiagram',
398
+ description: CLOUD_ARCHITECTURE_DESCRIPTION,
399
+ schema: singleDiagramSchema,
400
+ jsonSchema: singleDiagramJsonSchema,
401
+ },
402
+ {
403
+ name: 'renderFlowchart',
404
+ description: FLOWCHART_DESCRIPTION,
405
+ schema: singleDiagramSchema,
406
+ jsonSchema: singleDiagramJsonSchema,
407
+ },
408
+ {
409
+ name: 'renderBpmnDiagram',
410
+ description: BPMN_DESCRIPTION,
411
+ schema: singleDiagramSchema,
412
+ jsonSchema: singleDiagramJsonSchema,
413
+ },
414
+ ];
415
+ function isMcpToolName(name) {
416
+ return exports.mcpTools.some((tool) => tool.name === name);
417
+ }
418
+ exports.isMcpToolName = isMcpToolName;
419
+ exports.mcpToolMap = new Map(exports.mcpTools.map((tool) => [tool.name, tool]));
420
+ // Mapping from single diagram tool names to their diagram types
421
+ exports.singleDiagramTools = {
422
+ renderSequenceDiagram: DiagramTypes.SD,
423
+ renderEntityRelationshipDiagram: DiagramTypes.ERD,
424
+ renderCloudArchitectureDiagram: DiagramTypes.CAD,
425
+ renderFlowchart: DiagramTypes.FLOW,
426
+ renderBpmnDiagram: DiagramTypes.BPMN,
427
+ };
428
+ function isSingleDiagramTool(name) {
429
+ return name in exports.singleDiagramTools;
430
+ }
431
+ exports.isSingleDiagramTool = isSingleDiagramTool;
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@eraserlabs/eraser-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for generating diagrams with Eraser.io",
5
+ "private": false,
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "eraser-mcp": "dist/stdio.js"
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "typecheck": "tsc --noEmit",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "mcp",
21
+ "eraser",
22
+ "diagrams",
23
+ "model-context-protocol"
24
+ ],
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/eraserlabs/eraserio.git",
28
+ "directory": "packages/eraser-mcp"
29
+ },
30
+ "license": "MIT",
31
+ "dependencies": {
32
+ "zod": "^3.25.76"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^20.0.0",
36
+ "typescript": "^4.8.4"
37
+ },
38
+ "engines": {
39
+ "node": ">=18"
40
+ }
41
+ }