@endiagram/mcp 0.1.5 → 0.1.6

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 +14 -14
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -32,7 +32,7 @@ const server = new McpServer({
32
32
  version: "0.1.0",
33
33
  });
34
34
  // --- 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.", {
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
36
  source: z.string().describe("EN source code describing the system"),
37
37
  invariants: z
38
38
  .string()
@@ -41,7 +41,7 @@ server.tool("analyze_system", "Structural signal. You describe the system, the t
41
41
  detect_antipatterns: z
42
42
  .string()
43
43
  .optional()
44
- .describe("Antipatterns to detect in the structure"),
44
+ .describe("Set to 'true' to detect structural antipatterns"),
45
45
  }, async ({ source, invariants, detect_antipatterns }) => {
46
46
  const result = await callApi("analyze_system", {
47
47
  source,
@@ -54,7 +54,7 @@ server.tool("analyze_system", "Structural signal. You describe the system, the t
54
54
  };
55
55
  });
56
56
  // --- render ---
57
- server.tool("render", "Render an EN dependency graph as a publication-quality SVG image.", {
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.", {
58
58
  source: z.string().describe("EN source code describing the system"),
59
59
  theme: z
60
60
  .enum(["dark", "light"])
@@ -72,7 +72,7 @@ server.tool("render", "Render an EN dependency graph as a publication-quality SV
72
72
  };
73
73
  });
74
74
  // --- detail ---
75
- server.tool("detail", "Deep structural analysis concurrency, flow landmarks, resilience, dominator tree, min-cuts.", {
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.", {
76
76
  source: z.string().describe("EN source code describing the system"),
77
77
  }, async ({ source }) => {
78
78
  const result = await callApi("detail", { source });
@@ -82,7 +82,7 @@ server.tool("detail", "Deep structural analysis — concurrency, flow landmarks,
82
82
  };
83
83
  });
84
84
  // --- distance ---
85
- server.tool("distance", "Shortest path between two nodes with subsystem crossing annotations.", {
85
+ server.tool("distance", "Structural ruler. Computes shortest path between any two nodes (actions or entities) with annotations at every step. Returns: edge count, subsystem boundary crossings (how many module boundaries the path crosses), bridge edges on path (fragile links), and the full path with subsystem labels. Use to answer: 'how coupled are these two things?' If distance is 1-2 edges, they're tightly coupled. If it crosses 2+ subsystem boundaries, a change to one will ripple far.", {
86
86
  source: z.string().describe("EN source code describing the system"),
87
87
  from: z.string().describe("Starting node name"),
88
88
  to: z.string().describe("Target node name"),
@@ -94,7 +94,7 @@ server.tool("distance", "Shortest path between two nodes with subsystem crossing
94
94
  };
95
95
  });
96
96
  // --- diff ---
97
- server.tool("diff", "Structural diff between two systems topology, role, and subsystem changes.", {
97
+ server.tool("diff", "Structural diff between two systems. Computes both graphs independently, then reports the delta: nodes/entities present in one but not the other, role changes (a node that was PIPELINE in A became HUB in B -- that's a coupling regression), subsystem membership changes (node migrated between clusters), topology classification changes, and stage count differences. Use for: spec vs implementation (does the code match the design?), version 1 vs version 2 (did the refactor improve or worsen structure?), intended vs actual (model what you think exists, model what does exist, diff them).", {
98
98
  source_a: z.string().describe("EN source code for the first system"),
99
99
  source_b: z.string().describe("EN source code for the second system"),
100
100
  }, async ({ source_a, source_b }) => {
@@ -105,14 +105,14 @@ server.tool("diff", "Structural diff between two systems — topology, role, and
105
105
  };
106
106
  });
107
107
  // --- trace ---
108
- server.tool("trace", "Follow directed flow from node A to node B with role and subsystem annotations.", {
108
+ server.tool("trace", "Follow the flow -- data, materials, authority, money, risk. Computes directed shortest path from node A to node B, respecting the yields->needs flow direction. Every node on the path is annotated with its structural role and subsystem membership, so you can see role transitions along the flow (e.g., SOURCE -> PIPELINE -> HUB -> SINK). If no directed path exists, falls back to undirected and flags which edges are traversed backwards -- reverse edges often indicate missing abstractions or circular dependencies. Optional defense_nodes parameter checks whether specified nodes cover all source-to-sink paths.", {
109
109
  source: z.string().describe("EN source code describing the system"),
110
110
  from: z.string().describe("Starting node name"),
111
111
  to: z.string().describe("Target node name"),
112
112
  defense_nodes: z
113
113
  .string()
114
114
  .optional()
115
- .describe("Comma-separated list of defense nodes to annotate"),
115
+ .describe("Comma-separated list of defense nodes to check coverage"),
116
116
  }, async ({ source, from, to, defense_nodes }) => {
117
117
  const result = await callApi("trace", {
118
118
  source,
@@ -126,7 +126,7 @@ server.tool("trace", "Follow directed flow from node A to node B with role and s
126
126
  };
127
127
  });
128
128
  // --- extract ---
129
- server.tool("extract", "Extract a named subsystem as standalone EN source code.", {
129
+ 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
130
  source: z.string().describe("EN source code describing the system"),
131
131
  subsystem: z.string().describe("Name of the subsystem to extract"),
132
132
  }, async ({ source, subsystem }) => {
@@ -137,7 +137,7 @@ server.tool("extract", "Extract a named subsystem as standalone EN source code."
137
137
  };
138
138
  });
139
139
  // --- impact ---
140
- server.tool("impact", "Blast radius remove a node and see what disconnects.", {
140
+ server.tool("impact", "Blast radius calculator. Remove a node and see what breaks. Returns which nodes become disconnected (unreachable), whether the overall topology classification changes, and connected component count before vs after. Use to answer: 'what breaks if this node goes down?' If removing a node disconnects 0 others, it's safely removable. If it splits the graph into multiple components, it's load-bearing. Run this on every bridge node from analyze_system to Works for any domain -- remove a team from an org, a control from a compliance flow, a step from a clinical pathway. Same math.", {
141
141
  source: z.string().describe("EN source code describing the system"),
142
142
  node: z.string().describe("Node to remove for impact analysis"),
143
143
  }, async ({ source, node }) => {
@@ -148,7 +148,7 @@ server.tool("impact", "Blast radius — remove a node and see what disconnects."
148
148
  };
149
149
  });
150
150
  // --- evolve ---
151
- server.tool("evolve", "Dry-run architectural changes apply a patch and see the structural delta.", {
151
+ server.tool("evolve", "Dry-run for architectural changes. Apply a patch to a system and see the structural delta before writing any code. Patch nodes with the same name replace originals; new names are additions. Returns the full diff (topology change, role changes, subsystem shifts) plus new bridge nodes created and bridge nodes eliminated. Use to test: 'What happens if I add a validation step here?' 'Does adding a cache layer create a new single point of failure?' 'Will splitting this service improve or worsen coupling?' Answer these questions in seconds, not hours.", {
152
152
  source: z.string().describe("EN source code describing the current system"),
153
153
  patch: z.string().describe("EN source code patch to apply"),
154
154
  }, async ({ source, patch }) => {
@@ -159,7 +159,7 @@ server.tool("evolve", "Dry-run architectural changes — apply a patch and see t
159
159
  };
160
160
  });
161
161
  // --- between ---
162
- server.tool("between", "Betweenness centrality for a node what fraction of all shortest paths flow through it.", {
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
163
  source: z.string().describe("EN source code describing the system"),
164
164
  node: z.string().describe("Node to compute betweenness centrality for"),
165
165
  }, async ({ source, node }) => {
@@ -170,7 +170,7 @@ server.tool("between", "Betweenness centrality for a node — what fraction of a
170
170
  };
171
171
  });
172
172
  // --- categorize ---
173
- server.tool("categorize", "Auto-discover subsystem boundaries from dependency structure.", {
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
174
  source: z.string().describe("EN source code describing the system"),
175
175
  }, async ({ source }) => {
176
176
  const result = await callApi("categorize", { source });
@@ -180,7 +180,7 @@ server.tool("categorize", "Auto-discover subsystem boundaries from dependency st
180
180
  };
181
181
  });
182
182
  // --- compose ---
183
- server.tool("compose", "Merge two EN graphs into one with entity linking.", {
183
+ 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
184
  source_a: z.string().describe("EN source code for the first system"),
185
185
  source_b: z.string().describe("EN source code for the second system"),
186
186
  links: z
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@endiagram/mcp",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "MCP server for EN Diagram — structural analysis for any system",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",