@endiagram/mcp 0.1.7 → 0.1.9

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.
Files changed (2) hide show
  1. package/dist/index.js +38 -37
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -27,12 +27,13 @@ async function callApi(toolName, args) {
27
27
  };
28
28
  }
29
29
  }
30
+ const PLAIN_LANG = " When presenting findings to the user, use plain everyday language. Never use jargon like 'betweenness centrality', 'min-cut', 'bridge node', 'dominator tree', 'vertex-disjoint paths', or 'topology classification'. Instead say 'bottleneck', 'single point of failure', 'no backup path', 'what controls what'. The raw data helps your analysis -- give the user clear, simple insights.";
30
31
  const server = new McpServer({
31
32
  name: "en-diagram",
32
33
  version: "0.1.0",
33
34
  });
34
35
  // --- analyze_system ---
35
- server.tool("analyze_system", "Structural signal. You describe the system, the tool computes structural facts. All computation is deterministic -- no AI inside. EN syntax: subject do: action needs: inputs yields: outputs. Returns topology classification (Pipeline, Tree, Fork-Join, Series-Parallel, State Machine, Disconnected, or Unknown), node roles (SOURCE, SINK, FORK, JOIN, HUB, PIPELINE, CYCLE), and bridges (single points of failure). A node labeled HUB that you expected to be PIPELINE is a real finding.", {
36
+ server.tool("analyze_system", "Structural signal. You describe the system, the tool computes structural facts. All computation is deterministic -- no AI inside. EN syntax: subject do: action needs: inputs yields: outputs. Returns topology classification (Pipeline, Tree, Fork-Join, Series-Parallel, State Machine, Disconnected, or Unknown), node roles (SOURCE, SINK, FORK, JOIN, HUB, PIPELINE, CYCLE), and bridges (single points of failure). A node labeled HUB that you expected to be PIPELINE is a real finding." + PLAIN_LANG, {
36
37
  source: z.string().describe("EN source code describing the system"),
37
38
  invariants: z
38
39
  .string()
@@ -53,29 +54,21 @@ server.tool("analyze_system", "Structural signal. You describe the system, the t
53
54
  isError: result.isError,
54
55
  };
55
56
  });
56
- // --- render ---
57
- server.tool("render", "Render an EN dependency graph as a publication-quality SVG image. Nodes are colored by structural role (source, sink, hub, etc.) and grouped by auto-detected subsystem. Use when the user wants to SEE the structure -- the visual often reveals patterns (clusters, isolated subgraphs, fan-out imbalance) that text output alone misses.", {
57
+ // --- detail ---
58
+ server.tool("detail", "The depth layer. Run after analyze_system to get the full picture Returns: concurrency metrics (max parallelism, critical path length, parallel paths per depth level), flow landmarks (exact depths where the graph diverges/converges -- these are your bottleneck boundaries), full resilience analysis (bridge implications with which subsystems disconnect if each bridge fails), and structural dependency chains (what feeds what, what must complete first). analyze_system tells you WHERE to look. detail tells you WHY it matters. Use categorize -> extract to isolate a subsystem first, then detail on the extracted source for focused depth.", {
58
59
  source: z.string().describe("EN source code describing the system"),
59
- theme: z
60
- .enum(["dark", "light"])
61
- .optional()
62
- .describe("Color theme for the rendered image"),
63
- quality: z
64
- .enum(["small", "mid", "max"])
65
- .optional()
66
- .describe("Output quality / resolution"),
67
- }, async ({ source, theme, quality }) => {
68
- const result = await callApi("render", { source, theme, quality });
60
+ }, async ({ source }) => {
61
+ const result = await callApi("detail", { source });
69
62
  return {
70
63
  content: [{ type: "text", text: result.text }],
71
64
  isError: result.isError,
72
65
  };
73
66
  });
74
- // --- detail ---
75
- server.tool("detail", "The depth layer. Run after analyze_system to get the full picture Returns: concurrency metrics (max parallelism, critical path length, parallel paths per depth level), flow landmarks (exact depths where the graph diverges/converges -- these are your bottleneck boundaries), full resilience analysis (bridge implications with which subsystems disconnect if each bridge fails), and structural dependency chains (what feeds what, what must complete first). analyze_system tells you WHERE to look. detail tells you WHY it matters. Use categorize -> extract to isolate a subsystem first, then detail on the extracted source for focused depth.", {
67
+ // --- categorize ---
68
+ server.tool("categorize", "Auto-organize a flat list into named groups. You give it ungrouped actions with inputs and outputs -- the tool discovers subsystem boundaries from the dependency structure and names them. 25 nodes become 5-6 named subsystems. You don't define the groups. The structure does. When the discovered boundaries differ from your module structure, that difference is a finding. Use after analyze_system to see how the system organizes itself. Then feed subsystem names into extract for fractal zoom.", {
76
69
  source: z.string().describe("EN source code describing the system"),
77
70
  }, async ({ source }) => {
78
- const result = await callApi("detail", { source });
71
+ const result = await callApi("categorize", { source });
79
72
  return {
80
73
  content: [{ type: "text", text: result.text }],
81
74
  isError: result.isError,
@@ -125,6 +118,17 @@ server.tool("trace", "Follow the flow -- data, materials, authority, money, risk
125
118
  isError: result.isError,
126
119
  };
127
120
  });
121
+ // --- between ---
122
+ server.tool("between", "Quantify coupling. Computes betweenness centrality for a node: what fraction of all shortest paths in the system flow through it. Returns normalized score [0-1], absolute shortest-paths-through count, and total paths. A score of 0.25 means one quarter of all communication in the system passes through this node -- it's a coupling hotspot. Use on nodes flagged as HUB or FORK by analyze_system to get a precise number. Compare centrality scores across nodes to find the true bottleneck vs nodes that just look important.", {
123
+ source: z.string().describe("EN source code describing the system"),
124
+ node: z.string().describe("Node to compute betweenness centrality for"),
125
+ }, async ({ source, node }) => {
126
+ const result = await callApi("between", { source, node });
127
+ return {
128
+ content: [{ type: "text", text: result.text }],
129
+ isError: result.isError,
130
+ };
131
+ });
128
132
  // --- extract ---
129
133
  server.tool("extract", "Fractal zoom. Extract a named subsystem as standalone EN source code you can feed back into analyze_system for a deeper look. Reports boundary inputs (dependencies from outside the subsystem), boundary outputs (consumed by other subsystems), and internal entities. This is how you go from surface findings to root causes: categorize gives you subsystem names -> extract gives you the subsystem as its own graph -> analyze_system on that graph reveals internal structure invisible at the top level.", {
130
134
  source: z.string().describe("EN source code describing the system"),
@@ -158,27 +162,6 @@ server.tool("evolve", "Dry-run for architectural changes. Apply a patch to a sys
158
162
  isError: result.isError,
159
163
  };
160
164
  });
161
- // --- between ---
162
- server.tool("between", "Quantify coupling. Computes betweenness centrality for a node: what fraction of all shortest paths in the system flow through it. Returns normalized score [0-1], absolute shortest-paths-through count, and total paths. A score of 0.25 means one quarter of all communication in the system passes through this node -- it's a coupling hotspot. Use on nodes flagged as HUB or FORK by analyze_system to get a precise number. Compare centrality scores across nodes to find the true bottleneck vs nodes that just look important.", {
163
- source: z.string().describe("EN source code describing the system"),
164
- node: z.string().describe("Node to compute betweenness centrality for"),
165
- }, async ({ source, node }) => {
166
- const result = await callApi("between", { source, node });
167
- return {
168
- content: [{ type: "text", text: result.text }],
169
- isError: result.isError,
170
- };
171
- });
172
- // --- categorize ---
173
- server.tool("categorize", "Auto-organize a flat list into named groups. You give it ungrouped actions with inputs and outputs -- the tool discovers subsystem boundaries from the dependency structure and names them. 25 nodes become 5-6 named subsystems. You don't define the groups. The structure does. When the discovered boundaries differ from your module structure, that difference is a finding. Use after analyze_system to see how the system organizes itself. Then feed subsystem names into extract for fractal zoom.", {
174
- source: z.string().describe("EN source code describing the system"),
175
- }, async ({ source }) => {
176
- const result = await callApi("categorize", { source });
177
- return {
178
- content: [{ type: "text", text: result.text }],
179
- isError: result.isError,
180
- };
181
- });
182
165
  // --- compose ---
183
166
  server.tool("compose", "Merge two EN graphs into one. Takes two separate system descriptions and a list of entity links that connect them. Linked entities are merged under source A's name. Unlinked entities that share a name are automatically disambiguated. Returns combined EN source that you feed into any other tool -- analyze_system sees cross-graph bridges, impact shows blast radius across both graphs, distance measures paths crossing graph boundaries. Use when modeling two interacting systems (client/server, producer/consumer, spec/implementation) that share specific data points.", {
184
167
  source_a: z.string().describe("EN source code for the first system"),
@@ -193,6 +176,24 @@ server.tool("compose", "Merge two EN graphs into one. Takes two separate system
193
176
  isError: result.isError,
194
177
  };
195
178
  });
179
+ // --- render (last — only use when user explicitly asks to visualize) ---
180
+ server.tool("render", "Render an EN dependency graph as a publication-quality SVG image. Only call this when the user explicitly asks to visualize or render. Nodes are colored by structural role (source, sink, hub, etc.) and grouped by auto-detected subsystem. The visual reveals patterns (clusters, isolated subgraphs, fan-out imbalance) that text output alone misses.", {
181
+ source: z.string().describe("EN source code describing the system"),
182
+ theme: z
183
+ .enum(["dark", "light"])
184
+ .optional()
185
+ .describe("Color theme for the rendered image"),
186
+ quality: z
187
+ .enum(["small", "mid", "max"])
188
+ .optional()
189
+ .describe("Output quality / resolution"),
190
+ }, async ({ source, theme, quality }) => {
191
+ const result = await callApi("render", { source, theme, quality });
192
+ return {
193
+ content: [{ type: "text", text: result.text }],
194
+ isError: result.isError,
195
+ };
196
+ });
196
197
  // --- start server ---
197
198
  async function main() {
198
199
  const transport = new StdioServerTransport();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@endiagram/mcp",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "MCP server for EN Diagram — structural analysis for any system",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",