@endiagram/mcp 0.2.28 → 0.2.30

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 CHANGED
@@ -3,11 +3,31 @@ 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
5
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
6
- import { join, dirname } from "node:path";
6
+ import { join, dirname, resolve } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
  const __dirname = dirname(fileURLToPath(import.meta.url));
9
9
  const toolsConfig = JSON.parse(readFileSync(join(__dirname, "../tools.json"), "utf-8"));
10
10
  const EN_API_URL = process.env.EN_API_URL ?? "https://api.endiagram.com";
11
+ /**
12
+ * Resolve the `source` parameter: if it looks like a file path (.en, .txt,
13
+ * or starts with / or ~), read the file and return its contents.
14
+ * Otherwise return the string as-is (inline source).
15
+ */
16
+ function resolveSource(source) {
17
+ const trimmed = source.trim();
18
+ const isPath = trimmed.endsWith(".en") ||
19
+ trimmed.endsWith(".txt") ||
20
+ trimmed.startsWith("/") ||
21
+ trimmed.startsWith("~") ||
22
+ trimmed.startsWith("./") ||
23
+ trimmed.startsWith("../");
24
+ if (!isPath)
25
+ return source;
26
+ const resolved = trimmed.startsWith("~")
27
+ ? join(process.env.HOME ?? "", trimmed.slice(1))
28
+ : resolve(trimmed);
29
+ return readFileSync(resolved, "utf-8");
30
+ }
11
31
  async function callApi(toolName, args) {
12
32
  try {
13
33
  const response = await fetch(`${EN_API_URL}/mcp`, {
@@ -65,6 +85,19 @@ for (const tool of toolsConfig.tools) {
65
85
  : z.string().optional().describe(param.description);
66
86
  }
67
87
  server.tool(tool.name, tool.description, schemaProps, async (args) => {
88
+ // Resolve source fields from file paths if needed
89
+ try {
90
+ if (args.source)
91
+ args.source = resolveSource(args.source);
92
+ if (args.source_a)
93
+ args.source_a = resolveSource(args.source_a);
94
+ if (args.source_b)
95
+ args.source_b = resolveSource(args.source_b);
96
+ }
97
+ catch (e) {
98
+ const msg = e instanceof Error ? e.message : String(e);
99
+ return { content: [{ type: "text", text: `Failed to read source file: ${msg}` }], isError: true };
100
+ }
68
101
  // Special handling for render (save SVG to file)
69
102
  if (tool.name === "render") {
70
103
  const result = await callApi("render", args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@endiagram/mcp",
3
- "version": "0.2.28",
3
+ "version": "0.2.30",
4
4
  "description": "MCP server for EN Diagram — deterministic structural analysis backed by named theorems",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/tools.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "structure",
6
6
  "description": "What is this system? Complete structural overview: shape (topology), stages with roles, bridge nodes, cycles, parallelism, critical path, dominator tree, min-cuts, subsystems, interface nodes. Includes actors (who does what, workload entropy) and locations (where work happens, boundary crossings). Optional: pass node for per-node centrality, detect_findings for structural pattern detection.",
7
7
  "parameters": [
8
- {"name": "source", "type": "string", "description": "EN source code describing the system", "required": true},
8
+ {"name": "source", "type": "string", "description": "EN source code, or path to .en/.txt file", "required": true},
9
9
  {"name": "node", "type": "string", "description": "Node name for centrality query", "required": false},
10
10
  {"name": "detect_findings", "type": "string", "description": "Set to 'true' to detect structural findings", "required": false}
11
11
  ]
@@ -14,7 +14,7 @@
14
14
  "name": "invariant",
15
15
  "description": "What's always true? conservationLaws are weighted entity sums constant across all executions. sustainableCycles are action sequences that return the system to its starting state (T-invariants). depletableSets are entity groups where simultaneous depletion is irreversible. behavioral.deficiency 0 means structure fully determines dynamics. behavioral.isReversible and behavioral.hasUniqueEquilibrium describe convergence properties.",
16
16
  "parameters": [
17
- {"name": "source", "type": "string", "description": "EN source code describing the system", "required": true},
17
+ {"name": "source", "type": "string", "description": "EN source code, or path to .en/.txt file", "required": true},
18
18
  {"name": "rules", "type": "string", "description": "Structural rules to check, one per line", "required": false}
19
19
  ]
20
20
  },
@@ -22,7 +22,7 @@
22
22
  "name": "live",
23
23
  "description": "Can it deadlock? Can entities overflow? isStructurallyLive means every siphon contains a trap — no structural deadlock possible. uncoveredSiphons are entity groups that can drain permanently, with the actors and locations affected. isStructurallyBounded means no entity can accumulate without limit. unboundedCycles are action sequences that could cause overflow.",
24
24
  "parameters": [
25
- {"name": "source", "type": "string", "description": "EN source code describing the system", "required": true}
25
+ {"name": "source", "type": "string", "description": "EN source code, or path to .en/.txt file", "required": true}
26
26
  ]
27
27
  },
28
28
  {
@@ -39,8 +39,8 @@
39
39
  "name": "equivalent",
40
40
  "description": "Are two systems the same? Compare mode (source_a + source_b): shows structural differences, edit distance, and spectral equivalence — isCospectral true means identical structure despite different names. Evolve mode (source + patch): dry-run a change, shows diff plus new/lost bridge nodes. Prefix action name with - in patch to remove it.",
41
41
  "parameters": [
42
- {"name": "source_a", "type": "string", "description": "EN source code for the first system", "required": false},
43
- {"name": "source_b", "type": "string", "description": "EN source code for the second system", "required": false},
42
+ {"name": "source_a", "type": "string", "description": "EN source code or path to .en/.txt file for the first system", "required": false},
43
+ {"name": "source_b", "type": "string", "description": "EN source code or path to .en/.txt file for the second system", "required": false},
44
44
  {"name": "source", "type": "string", "description": "EN source code for evolve mode", "required": false},
45
45
  {"name": "patch", "type": "string", "description": "EN patch for evolve mode", "required": false}
46
46
  ]
@@ -49,8 +49,8 @@
49
49
  "name": "compose",
50
50
  "description": "How do parts combine? Merge mode (source_a + source_b + links): merge two systems by linking shared entities. Extract mode (source + subsystem): extract a subsystem as standalone EN with boundary inputs/outputs, actors, and locations.",
51
51
  "parameters": [
52
- {"name": "source_a", "type": "string", "description": "EN source code for the first system", "required": false},
53
- {"name": "source_b", "type": "string", "description": "EN source code for the second system", "required": false},
52
+ {"name": "source_a", "type": "string", "description": "EN source code or path to .en/.txt file for the first system", "required": false},
53
+ {"name": "source_b", "type": "string", "description": "EN source code or path to .en/.txt file for the second system", "required": false},
54
54
  {"name": "links", "type": "string", "description": "Entity links e.g. 'a.node1=b.node2'", "required": false},
55
55
  {"name": "source", "type": "string", "description": "EN source code for extract mode", "required": false},
56
56
  {"name": "subsystem", "type": "string", "description": "Subsystem name for extract mode", "required": false}
@@ -60,11 +60,11 @@
60
60
  "name": "render",
61
61
  "description": "SVG diagram. Only call when user explicitly asks to visualize.",
62
62
  "parameters": [
63
- {"name": "source", "type": "string", "description": "EN source code describing the system", "required": true},
63
+ {"name": "source", "type": "string", "description": "EN source code, or path to .en/.txt file", "required": true},
64
64
  {"name": "theme", "type": "string", "description": "Color theme: dark or light", "required": false},
65
65
  {"name": "quality", "type": "string", "description": "Output quality: small, mid, or max", "required": false},
66
66
  {"name": "view", "type": "string", "description": "View mode: flow (default), actors (swimlane by actor), locations (grouped by context), summary (one node per subsystem)", "required": false},
67
- {"name": "show_regions", "type": "string", "description": "Show structural regions like fork, pipeline, join. Default true", "required": false},
67
+ {"name": "structure_layers", "type": "string", "description": "Bitmask for structure overlays. Bits: 1=subsystems, 2=pipelines, 4=cycles, 8=forks, 16=joins, 32=hubs. Default 63 (all on). Pass 0 to hide all.", "required": false},
68
68
  {"name": "output", "type": "string", "description": "File path to save the SVG", "required": false}
69
69
  ]
70
70
  }