@miragon/design-iq-mcp 5.0.0-rc.0 → 5.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 CHANGED
@@ -1,11 +1,13 @@
1
1
  # @miragon/design-iq-mcp
2
2
 
3
- designIQ's read-only MCP server: it exposes a BPM content repo's processes to any MCP client
4
- (Claude Code, Claude Desktop, IDEs, ...). A content repo is a root `designiq.yml` naming its
5
- models folder (`models:`, legacy alias `processes:`; the legacy file name `bpmiq.yml` is still
6
- read); a model IS a file with a registered notation extension there (a process its `.bpmn`),
7
- and the process view — name, roles (BPMN lanes), steps, gateways, flow, and sub-process
8
- calls — is **derived from the BPMN** on the fly. Tools:
3
+ designIQ's read-only MCP server: it exposes the models of a designIQ content repo — processes
4
+ (BPMN), decisions (DMN), Wardley Maps, Team Topologies, Event Storming boards, Context Maps,
5
+ Value Chains and Markdown — to any MCP client (Claude Code, Claude Desktop, IDEs, ...). A
6
+ content repo is a root `designiq.yml` naming its models folder (`models:`, legacy alias
7
+ `processes:`; the legacy file name `bpmiq.yml` is still read); a model IS a file with a
8
+ registered notation extension there (a process its `.bpmn`). Views are **derived from the
9
+ models** on the fly; BPMN gets the richest one — name, roles (BPMN lanes), steps, gateways,
10
+ flow, and sub-process calls. Tools:
9
11
  `list_models`, `list_processes`, `get_process`, `get_view`, `get_model`, `enumerate_paths`,
10
12
  `find_cycles`, `who_owns`, `which_processes_use`, `which_models_use`. Read-only by
11
13
  construction: the tools only ever read files, and all of them carry `readOnlyHint`.
package/dist/http.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as createMcpServer, r as todosConfigFromEnv, t as DEFAULT_ROOT } from "./tools-V_22sqM1.js";
2
+ import { n as createMcpServer, r as todosConfigFromEnv, t as DEFAULT_ROOT } from "./tools-8vgJc4aD.js";
3
3
  import { createServer } from "node:http";
4
4
  import { timingSafeEqual } from "node:crypto";
5
5
  import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
package/dist/server.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as createMcpServer, r as todosConfigFromEnv, t as DEFAULT_ROOT } from "./tools-V_22sqM1.js";
2
+ import { n as createMcpServer, r as todosConfigFromEnv, t as DEFAULT_ROOT } from "./tools-8vgJc4aD.js";
3
3
  import { existsSync } from "node:fs";
4
4
  import { resolve } from "node:path";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
@@ -93,8 +93,8 @@ const NOTATIONS = [
93
93
  id: "wardley",
94
94
  label: "Wardley Map",
95
95
  noun: {
96
- singular: "wardley map",
97
- plural: "wardley maps"
96
+ singular: "Wardley Map",
97
+ plural: "Wardley Maps"
98
98
  },
99
99
  extensions: [".owm", ".wmap"],
100
100
  mediaKind: "dsl",
@@ -109,8 +109,8 @@ const NOTATIONS = [
109
109
  id: "team-topology",
110
110
  label: "Team Topology",
111
111
  noun: {
112
- singular: "team topology",
113
- plural: "team topologies"
112
+ singular: "Team Topology",
113
+ plural: "Team Topologies"
114
114
  },
115
115
  extensions: [".tt", ".ttm.json"],
116
116
  mediaKind: "json",
@@ -121,8 +121,8 @@ const NOTATIONS = [
121
121
  id: "event-storming",
122
122
  label: "Event Storming",
123
123
  noun: {
124
- singular: "event storming board",
125
- plural: "event storming boards"
124
+ singular: "Event Storming board",
125
+ plural: "Event Storming boards"
126
126
  },
127
127
  extensions: [".storm"],
128
128
  mediaKind: "dsl",
@@ -137,8 +137,8 @@ const NOTATIONS = [
137
137
  id: "context-map",
138
138
  label: "Context Map",
139
139
  noun: {
140
- singular: "context map",
141
- plural: "context maps"
140
+ singular: "Context Map",
141
+ plural: "Context Maps"
142
142
  },
143
143
  extensions: [".cm.json"],
144
144
  mediaKind: "json",
@@ -159,8 +159,8 @@ const NOTATIONS = [
159
159
  id: "value-chain",
160
160
  label: "Value Chain",
161
161
  noun: {
162
- singular: "value chain",
163
- plural: "value chains"
162
+ singular: "Value Chain",
163
+ plural: "Value Chains"
164
164
  },
165
165
  extensions: [".vc.json"],
166
166
  mediaKind: "json",
@@ -1342,7 +1342,7 @@ const DERIVERS = {
1342
1342
  return {
1343
1343
  notation: "wardley",
1344
1344
  name: null,
1345
- summary: `Wardley map with ${counted(stats)}`,
1345
+ summary: `Wardley Map with ${counted(stats)}`,
1346
1346
  stats
1347
1347
  };
1348
1348
  },
@@ -1354,7 +1354,7 @@ const DERIVERS = {
1354
1354
  return {
1355
1355
  notation: "team-topology",
1356
1356
  name: null,
1357
- summary: `Team topology with ${counted(stats)}`,
1357
+ summary: `Team Topology with ${counted(stats)}`,
1358
1358
  stats
1359
1359
  };
1360
1360
  },
@@ -1378,7 +1378,7 @@ const DERIVERS = {
1378
1378
  return {
1379
1379
  notation: "event-storming",
1380
1380
  name: typeof title === "string" ? title : null,
1381
- summary: `Event storming board with ${Object.keys(present).length > 0 ? counted(present) : "no elements"}`,
1381
+ summary: `Event Storming board with ${Object.keys(present).length > 0 ? counted(present) : "no elements"}`,
1382
1382
  stats,
1383
1383
  detail: {
1384
1384
  level: graph.meta?.level ?? null,
@@ -1401,7 +1401,7 @@ const DERIVERS = {
1401
1401
  return {
1402
1402
  notation: "context-map",
1403
1403
  name: typeof title === "string" ? title : null,
1404
- summary: `Context map with ${counted({ contexts: stats.contexts })}${breakdown(CM_SUBDOMAIN_TYPES)}, ${counted({ relationships: stats.relationships })}${breakdown(CM_RELATIONSHIP_PATTERNS)}`,
1404
+ summary: `Context Map with ${counted({ contexts: stats.contexts })}${breakdown(CM_SUBDOMAIN_TYPES)}, ${counted({ relationships: stats.relationships })}${breakdown(CM_RELATIONSHIP_PATTERNS)}`,
1405
1405
  stats,
1406
1406
  detail: {
1407
1407
  contexts: graph.nodes.map((n) => ({
@@ -1432,7 +1432,7 @@ const DERIVERS = {
1432
1432
  return {
1433
1433
  notation: "value-chain",
1434
1434
  name: null,
1435
- summary: `Value chain with ${counted(stats)}`,
1435
+ summary: `Value Chain with ${counted(stats)}`,
1436
1436
  stats
1437
1437
  };
1438
1438
  }
@@ -1583,6 +1583,20 @@ function todosConfigFromEnv(env) {
1583
1583
  apiUrl: env.GITHUB_API_URL
1584
1584
  } : void 0;
1585
1585
  }
1586
+ /** "a, b and c" */
1587
+ const andList = (items) => items.length < 2 ? items.join("") : `${items.slice(0, -1).join(", ")} and ${items.at(-1)}`;
1588
+ /** every registered notation with its file extensions */
1589
+ const NOTATION_FILES = andList(NOTATIONS.map((n) => `${n.label} (${n.extensions.join("/")})`));
1590
+ function readOnlyInstructions(on) {
1591
+ return [
1592
+ "designIQ is a Git-native workspace where teams model their business and their architecture together, live; this server reads one checkout of such a repository and never writes.",
1593
+ `The repository keeps its models as files under the folder its designiq.yml names, one model per file with the file stem as its id, in these notations: ${NOTATION_FILES}.`,
1594
+ "Start with `list_models` to see which models it holds.",
1595
+ "These tools are notation-neutral: `get_view` (the derived structure), `get_model` (the raw graph of nodes and edges), `enumerate_paths` and `find_cycles` (flow analysis where the notation has flow semantics) and `which_models_use` (which models reference a given one).",
1596
+ "BPMN has deeper tooling: `list_processes`, `get_process` (the derived process view: roles, steps, flow, sub-process calls), `who_owns` (ownership from lanes) and `which_processes_use`" + (on.todos ? "; `list_todos` lists the open todos anchored to processes." : "."),
1597
+ "Live editing, the modeler widgets and release as a pull request are on the designIQ Live Host, not here."
1598
+ ].join(" ");
1599
+ }
1586
1600
  /** Build a fully configured, read-only MCP server over the repo at `root`. */
1587
1601
  function createMcpServer(root = DEFAULT_ROOT, todos, contributions = []) {
1588
1602
  const config = () => loadContentConfig(root);
@@ -1614,17 +1628,19 @@ function createMcpServer(root = DEFAULT_ROOT, todos, contributions = []) {
1614
1628
  return graph;
1615
1629
  };
1616
1630
  const isGraph = (g) => !("content" in g);
1617
- /** the registry nouns for user-facing copy ("wardley map" / "team topologies") */
1631
+ /** the registry nouns for user-facing copy ("Wardley Map" / "Team Topologies") */
1618
1632
  const nounOf = (notation) => byId(notation)?.noun.singular ?? notation;
1619
1633
  const pluralOf = (notation) => byId(notation)?.noun.plural ?? notation;
1620
1634
  const notAContentRepo = () => fail(`No usable designiq.yml (or legacy bpmiq.yml) at the content root — not a content repo. Expected a root designiq.yml naming the models folder.`);
1621
1635
  const unknownProcess = async (id) => fail(`Unknown process '${id}'. Available: ${(await processes()).map((p) => p.id).join(", ") || "(none)"}.`);
1622
1636
  const server = new McpServer({
1623
1637
  name: "designiq-mcp",
1638
+ title: "designIQ model repository (read-only)",
1624
1639
  version: "0.2.0"
1625
- });
1640
+ }, { instructions: readOnlyInstructions({ todos: todos !== void 0 }) });
1626
1641
  server.registerTool("list_models", {
1627
- description: "List EVERY model file of the repository, grouped by notation (bpmn, dmn, wardley, team-topology, …) — the registry-wide superset of list_processes. Each row: id (file stem), path, and — for notations with a registered deriver — the model's own name, summary and stats. Use to see which notations a repo contains before reaching for the notation-specific tools.",
1642
+ title: "List models",
1643
+ description: `List EVERY model file of the repository, grouped by notation (${NOTATIONS.map((n) => n.id).join(", ")}) — the starting point for any question. Each row: id (file stem), path, and — for notations with a registered deriver — the model's own name, summary and stats. Use to see which notations a repo contains before reaching for the notation-specific tools; list_processes is the BPMN-only listing.`,
1628
1644
  annotations: READ_ONLY
1629
1645
  }, safe(async () => {
1630
1646
  const cfg = config();
@@ -1654,7 +1670,8 @@ function createMcpServer(root = DEFAULT_ROOT, todos, contributions = []) {
1654
1670
  return ok({ models: grouped });
1655
1671
  }));
1656
1672
  server.registerTool("list_processes", {
1657
- description: "List all modeled business processes — every .bpmn file under the repo's designiq.yml models folder. Each row: id (file name without extension), derived name, the file path, and a count of steps/events/gateways/roles. Use to get a portfolio overview or to find a process id before calling get_process, get_model, who_owns or enumerate_paths.",
1673
+ title: "List BPMN processes",
1674
+ description: "BPMN only: list the processes — every .bpmn file under the repo's designiq.yml models folder. Each row: id (file name without extension), derived name, the file path, and a count of steps/events/gateways/roles. Use for a process overview or to find a process id before calling get_process, get_model, who_owns or enumerate_paths; list_models covers every notation.",
1658
1675
  annotations: READ_ONLY
1659
1676
  }, safe(async () => {
1660
1677
  if (!config()) return notAContentRepo();
@@ -1677,7 +1694,8 @@ function createMcpServer(root = DEFAULT_ROOT, todos, contributions = []) {
1677
1694
  }));
1678
1695
  }));
1679
1696
  server.registerTool("get_process", {
1680
- description: "Get one process in full: the process view DERIVED from its BPMN — name, roles (BPMN lanes = owning teams), steps (activities with their role), events, gateways, the sequence/message flow, and the sub-processes it calls (callActivity → calledElement). Use for walkthroughs and any deep question about a single process.",
1697
+ title: "Get BPMN process view",
1698
+ description: "BPMN only: get one process in full — the process view DERIVED from its BPMN: name, roles (BPMN lanes = owning teams), steps (activities with their role), events, gateways, the sequence/message flow, and the sub-processes it calls (callActivity → calledElement). Use for walkthroughs and any deep question about a single process; get_view covers every notation.",
1681
1699
  inputSchema: z.object({ id: z.string().describe("Process id = the .bpmn file name without extension, e.g. order-to-cash") }),
1682
1700
  annotations: READ_ONLY
1683
1701
  }, safe(async ({ id }) => {
@@ -1693,6 +1711,7 @@ function createMcpServer(root = DEFAULT_ROOT, todos, contributions = []) {
1693
1711
  });
1694
1712
  }));
1695
1713
  server.registerTool("get_model", {
1714
+ title: "Get model graph",
1696
1715
  description: "Parse ANY model file into its generic graph: nodes (id, type, name), edges and meta — BPMN flows and lanes, DMN requirements, Wardley components/dependencies, … Use to see the raw structure, or to ground any 'how does X work' answer in the actual model.",
1697
1716
  inputSchema: z.object({
1698
1717
  id: z.string().describe("Model id = file stem (from list_models), e.g. order-to-cash"),
@@ -1711,7 +1730,8 @@ function createMcpServer(root = DEFAULT_ROOT, todos, contributions = []) {
1711
1730
  }) : graph;
1712
1731
  }));
1713
1732
  server.registerTool("get_view", {
1714
- description: `The derived view of ANY model — its own name, a one-line summary, stats, and the rich notation payload in \`detail\` where one exists (process view, decision tables, …). The notation-agnostic sibling of get_process: works for every notation with extract+derive capabilities (${NOTATIONS.filter((n) => hasDeriver(n.id)).map((n) => n.id).join(", ")}).`,
1733
+ title: "Get model view",
1734
+ description: `The derived view of ANY model — its own name, a one-line summary, stats, and the rich notation payload in \`detail\` where one exists (the process view for BPMN, the decision tables for DMN). Works for every notation with extract+derive capabilities (${NOTATIONS.filter((n) => hasDeriver(n.id)).map((n) => n.id).join(", ")}).`,
1715
1735
  inputSchema: z.object({
1716
1736
  id: z.string().describe("Model id = file stem (from list_models), e.g. tea-shop"),
1717
1737
  notation: z.string().optional().describe("registry notation id — disambiguates a stem shared across notations")
@@ -1732,6 +1752,7 @@ function createMcpServer(root = DEFAULT_ROOT, todos, contributions = []) {
1732
1752
  });
1733
1753
  }));
1734
1754
  server.registerTool("enumerate_paths", {
1755
+ title: "Enumerate flow paths",
1735
1756
  description: "Enumerate the possible start→end paths through a model's flow (cycle-safe, capped) — BPMN sequence flows, Wardley dependencies, … per the notation's graph hints. Each path is the ordered list of element names. Use for walkthroughs ('what are the ways an order can go?'), test-case derivation, and spotting unexpected shortcuts or dead branches.",
1736
1757
  inputSchema: z.object({
1737
1758
  id: z.string().describe("Model id = file stem, e.g. order-to-cash"),
@@ -1757,6 +1778,7 @@ function createMcpServer(root = DEFAULT_ROOT, todos, contributions = []) {
1757
1778
  });
1758
1779
  }));
1759
1780
  server.registerTool("find_cycles", {
1781
+ title: "Find flow cycles",
1760
1782
  description: "Detect cycles (loops) in a model's flow — BPMN rework/retry loops, circular Wardley dependencies, circular DMN requirements — per the notation's graph hints. Returns each cycle as the ordered list of element names. Use when analyzing complexity, rework cost, or 'why does this case never finish'.",
1761
1783
  inputSchema: z.object({
1762
1784
  id: z.string().describe("Model id = file stem, e.g. order-to-cash"),
@@ -1778,7 +1800,8 @@ function createMcpServer(root = DEFAULT_ROOT, todos, contributions = []) {
1778
1800
  });
1779
1801
  }));
1780
1802
  server.registerTool("who_owns", {
1781
- description: "Resolve ownership of a process from its BPMN lanes — the roles/teams that own its steps (each lane, with the steps it contains) plus the pools (participants). Use for 'who owns X', 'who does what in X', or handoff questions. Note: on the slim contract ownership is whatever the model's lanes say; a process with no lanes has no modeled owner.",
1803
+ title: "Find who owns a BPMN process",
1804
+ description: "BPMN only: resolve ownership of a process from its lanes — the roles/teams that own its steps (each lane, with the steps it contains) plus the pools (participants). Use for 'who owns X', 'who does what in X', or handoff questions. Note: on the slim contract ownership is whatever the model's lanes say; a process with no lanes has no modeled owner.",
1782
1805
  inputSchema: z.object({ id: z.string().describe("Process id, e.g. order-to-cash") }),
1783
1806
  annotations: READ_ONLY
1784
1807
  }, safe(async ({ id }) => {
@@ -1800,7 +1823,8 @@ function createMcpServer(root = DEFAULT_ROOT, todos, contributions = []) {
1800
1823
  });
1801
1824
  }));
1802
1825
  server.registerTool("which_processes_use", {
1803
- description: "Impact analysis across the portfolio: find processes whose id, derived name, role/lane names, step names, or sub-process calls (calledElement) match a query — case-insensitive substring. Use for 'what calls invoice-handling', 'which processes have a Billing lane', 'what touches the credit check'.",
1826
+ title: "Search BPMN processes",
1827
+ description: "BPMN only: find processes whose id, derived name, role/lane names, step names, or sub-process calls (calledElement) match a query — case-insensitive substring. Use for 'what calls invoice-handling', 'which processes have a Billing lane', 'what touches the credit check'. which_models_use follows references across every notation.",
1804
1828
  inputSchema: z.object({ query: z.string().describe("Case-insensitive substring, e.g. 'invoice-handling', 'Billing', 'credit'") }),
1805
1829
  annotations: READ_ONLY
1806
1830
  }, safe(async ({ query }) => {
@@ -1834,7 +1858,8 @@ function createMcpServer(root = DEFAULT_ROOT, todos, contributions = []) {
1834
1858
  return ok(hits);
1835
1859
  }));
1836
1860
  server.registerTool("which_models_use", {
1837
- description: "Reference-level impact analysis across EVERY notation: all models whose typed cross-model references (callActivity calls, businessRuleTask decides, …) point at the given model id (file stem, exact match) — the repo-wide reference index behind it also flags dangling references. The registry-wide sibling of which_processes_use; use for 'what breaks if I change or delete this model?'.",
1861
+ title: "Find models that reference a model",
1862
+ description: "Reference-level impact analysis across EVERY notation: all models whose typed cross-model references (callActivity calls, businessRuleTask decides, …) point at the given model id (file stem, exact match) — the repo-wide reference index behind it also flags dangling references. Use for 'what breaks if I change or delete this model?'.",
1838
1863
  inputSchema: z.object({ id: z.string().describe("Target model id = file stem (from list_models), e.g. 'credit-check'") }),
1839
1864
  annotations: READ_ONLY
1840
1865
  }, safe(async ({ id }) => {
@@ -1858,6 +1883,7 @@ function createMcpServer(root = DEFAULT_ROOT, todos, contributions = []) {
1858
1883
  const { repo: todosRepo, token: todosToken } = todos;
1859
1884
  const api = (todos.apiUrl ?? "https://api.github.com").replace(/\/$/, "");
1860
1885
  server.registerTool("list_todos", {
1886
+ title: "List open todos",
1861
1887
  description: "List the OPEN model-anchored todos of this content repo — work items filed from the live model into the repo's issue tracker (label 'todo'). Each row carries the tracker id and URL, title, assignees, createdAt and the parsed anchor (process, model file, anchored BPMN elements). Optional filter on one process. Use for 'what is open on process X' or to cross-check a model answer against known open discrepancies.",
1862
1888
  inputSchema: z.object({ process: z.string().optional().describe("Only todos anchored to this process id, e.g. order-to-cash") }),
1863
1889
  annotations: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@miragon/design-iq-mcp",
3
- "version": "5.0.0-rc.0",
4
- "description": "Read-only MCP server exposing a BPM content repo's processes — discovered from designiq.yml, derived from the BPMN — to any MCP client.",
3
+ "version": "5.1.0",
4
+ "description": "Read-only MCP server exposing a designIQ content repo's models — BPMN processes, DMN decisions, Wardley Maps, Team Topologies, Event Storming boards, Context Maps and more — to any MCP client. Discovered from designiq.yml, views derived from the models.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -10,9 +10,15 @@
10
10
  "directory": "packages/mcp"
11
11
  },
12
12
  "keywords": [
13
+ "modeling",
14
+ "architecture",
13
15
  "bpm",
14
16
  "bpmn",
15
17
  "dmn",
18
+ "wardley-maps",
19
+ "team-topologies",
20
+ "event-storming",
21
+ "context-maps",
16
22
  "mcp",
17
23
  "model-context-protocol",
18
24
  "process-documentation"
@@ -35,17 +41,17 @@
35
41
  },
36
42
  "dependencies": {
37
43
  "@modelcontextprotocol/node": "2.1.0",
38
- "@modelcontextprotocol/server": "2.1.0",
39
- "fast-xml-parser": "5.11.1",
44
+ "@modelcontextprotocol/server": "2.2.0",
45
+ "fast-xml-parser": "5.11.2",
40
46
  "yaml": "2.9.1",
41
47
  "zod": "4.6.5"
42
48
  },
43
49
  "devDependencies": {
44
- "@modelcontextprotocol/client": "2.1.0",
50
+ "@modelcontextprotocol/client": "2.2.0",
45
51
  "tsdown": "0.23.0",
46
52
  "@designiq/contracts": "0.1.0",
47
- "@designiq/http-kit": "0.1.0",
48
53
  "@designiq/github-app": "0.1.0",
54
+ "@designiq/http-kit": "0.1.0",
49
55
  "@designiq/mcp-kit": "0.1.0",
50
56
  "@designiq/notations": "0.1.0"
51
57
  },