@endiagram/mcp 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +32 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
5
7
|
const EN_API_URL = process.env.EN_API_URL ?? "https://api.endiagram.com";
|
|
6
8
|
async function callApi(toolName, args) {
|
|
7
9
|
try {
|
|
@@ -10,14 +12,24 @@ async function callApi(toolName, args) {
|
|
|
10
12
|
headers: { "Content-Type": "application/json" },
|
|
11
13
|
body: JSON.stringify(args),
|
|
12
14
|
});
|
|
13
|
-
const
|
|
15
|
+
const body = await response.text();
|
|
14
16
|
if (!response.ok) {
|
|
15
17
|
return {
|
|
16
|
-
text: `API error (${response.status}): ${
|
|
18
|
+
text: `API error (${response.status}): ${body}`,
|
|
17
19
|
isError: true,
|
|
18
20
|
};
|
|
19
21
|
}
|
|
20
|
-
|
|
22
|
+
try {
|
|
23
|
+
const parsed = JSON.parse(body);
|
|
24
|
+
return {
|
|
25
|
+
text: parsed.text ?? body,
|
|
26
|
+
isError: parsed.isError ?? false,
|
|
27
|
+
svg: parsed.svg ?? undefined,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return { text: body, isError: false };
|
|
32
|
+
}
|
|
21
33
|
}
|
|
22
34
|
catch (error) {
|
|
23
35
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -36,7 +48,9 @@ Shared names between yields and needs create connections automatically. Multi-wo
|
|
|
36
48
|
|
|
37
49
|
The engine computes structural truth from the description — the tool outputs are raw mathematical findings. Translate them into clear, practical insights relevant to what the user is trying to achieve.
|
|
38
50
|
|
|
39
|
-
How to use these tools effectively: model the system first, then explore. The first tool call reveals the structure — but the real insights come from following up. A node labeled HUB that should be simple? Dig into it with between or impact. A surprising subsystem boundary? Extract it and analyze deeper. An unexpected dependency chain? Trace it. A proposed change? Evolve it and diff. The tools are designed to chain — each finding opens a question that another tool can answer. Don't stop at one call. Explore, tinker, compare, and let the math surface what no one expected
|
|
51
|
+
How to use these tools effectively: model the system first, then explore. The first tool call reveals the structure — but the real insights come from following up. A node labeled HUB that should be simple? Dig into it with between or impact. A surprising subsystem boundary? Extract it and analyze deeper. An unexpected dependency chain? Trace it. A proposed change? Evolve it and diff. The tools are designed to chain — each finding opens a question that another tool can answer. Don't stop at one call. Explore, tinker, compare, and let the math surface what no one expected.
|
|
52
|
+
|
|
53
|
+
Go deep, not wide. When a finding catches your attention — a surprising hub, an unexpected bottleneck, a structural anomaly — lock onto it. Use one tool to surface it, then chain the next tool to explain it, then the next to stress-test it. Keep narrowing until you reach the root. One thread, followed to the end, beats ten shallow observations. Only call render when the user explicitly asks to visualize.`;
|
|
40
54
|
const server = new McpServer({
|
|
41
55
|
name: "endiagram",
|
|
42
56
|
version: "0.2.0",
|
|
@@ -196,7 +210,7 @@ server.tool("conserve", "Structural invariants, deadlock analysis, complexity, a
|
|
|
196
210
|
};
|
|
197
211
|
});
|
|
198
212
|
// --- render ---
|
|
199
|
-
server.tool("render", "SVG diagram. Only when user asks to visualize.", {
|
|
213
|
+
server.tool("render", "SVG diagram. Only call when user explicitly asks to visualize. Saves SVG to a local file — no SVG content enters the conversation.", {
|
|
200
214
|
source: z.string().describe("EN source code describing the system"),
|
|
201
215
|
theme: z
|
|
202
216
|
.enum(["dark", "light"])
|
|
@@ -208,9 +222,20 @@ server.tool("render", "SVG diagram. Only when user asks to visualize.", {
|
|
|
208
222
|
.describe("Output quality"),
|
|
209
223
|
}, async ({ source, theme, quality }) => {
|
|
210
224
|
const result = await callApi("render", { source, theme, quality });
|
|
225
|
+
if (result.isError || !result.svg) {
|
|
226
|
+
return {
|
|
227
|
+
content: [{ type: "text", text: result.text }],
|
|
228
|
+
isError: result.isError,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
const outDir = join(process.cwd(), ".endiagram");
|
|
232
|
+
if (!existsSync(outDir))
|
|
233
|
+
mkdirSync(outDir, { recursive: true });
|
|
234
|
+
const filePath = join(outDir, `en-${Date.now()}.svg`);
|
|
235
|
+
writeFileSync(filePath, result.svg);
|
|
211
236
|
return {
|
|
212
|
-
content: [{ type: "text", text:
|
|
213
|
-
isError:
|
|
237
|
+
content: [{ type: "text", text: `SVG saved: ${filePath}` }],
|
|
238
|
+
isError: false,
|
|
214
239
|
};
|
|
215
240
|
});
|
|
216
241
|
// --- start server ---
|