@miragon/design-iq-mcp 5.9.0 → 5.11.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/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-DizBhl75.js";
2
+ import { n as createMcpServer, r as todosConfigFromEnv, t as DEFAULT_ROOT } from "./tools-CszDSqzk.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-DizBhl75.js";
2
+ import { n as createMcpServer, r as todosConfigFromEnv, t as DEFAULT_ROOT } from "./tools-CszDSqzk.js";
3
3
  import { existsSync } from "node:fs";
4
4
  import { resolve } from "node:path";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
@@ -68,6 +68,7 @@ const NOTATIONS = [
68
68
  mediaKind: "xml",
69
69
  docShape: "text",
70
70
  monacoLanguage: "xml",
71
+ todos: true,
71
72
  graphHints: {
72
73
  flowEdgeKinds: ["sequenceFlow"],
73
74
  entryNodeTypes: ["startEvent"]
@@ -100,6 +101,7 @@ const NOTATIONS = [
100
101
  mediaKind: "dsl",
101
102
  docShape: "text",
102
103
  monacoLanguage: "plaintext",
104
+ todos: true,
103
105
  graphHints: {
104
106
  flowEdgeKinds: ["dependency"],
105
107
  entryNodeTypes: []
@@ -115,7 +117,8 @@ const NOTATIONS = [
115
117
  extensions: [".tt", ".ttm.json"],
116
118
  mediaKind: "json",
117
119
  docShape: "text",
118
- monacoLanguage: "json"
120
+ monacoLanguage: "json",
121
+ todos: true
119
122
  },
120
123
  {
121
124
  id: "event-storming",
@@ -128,6 +131,7 @@ const NOTATIONS = [
128
131
  mediaKind: "dsl",
129
132
  docShape: "text",
130
133
  monacoLanguage: "plaintext",
134
+ todos: true,
131
135
  graphHints: {
132
136
  flowEdgeKinds: ["arrow"],
133
137
  entryNodeTypes: []
@@ -144,6 +148,7 @@ const NOTATIONS = [
144
148
  mediaKind: "json",
145
149
  docShape: "text",
146
150
  monacoLanguage: "json",
151
+ todos: true,
147
152
  graphHints: {
148
153
  flowEdgeKinds: [
149
154
  "partnership",
@@ -177,7 +182,8 @@ const NOTATIONS = [
177
182
  extensions: [".md"],
178
183
  mediaKind: "markdown",
179
184
  docShape: "text",
180
- monacoLanguage: "markdown"
185
+ monacoLanguage: "markdown",
186
+ todos: true
181
187
  }
182
188
  ];
183
189
  function byId(id) {
@@ -542,6 +548,8 @@ const TT_EDGE_INTERACTIONS_VERSION = 1;
542
548
  function extractTeamTopology(raw) {
543
549
  const data = JSON.parse(raw);
544
550
  const list = (v) => Array.isArray(v) ? v : [];
551
+ const objects = (v) => list(v).filter((i) => i !== null && typeof i === "object");
552
+ const text = (v) => typeof v === "string" && v !== "" ? v : void 0;
545
553
  const teams = list(data.nodes).map((n) => ({
546
554
  id: n.id,
547
555
  type: n.type ?? "team",
@@ -549,16 +557,38 @@ function extractTeamTopology(raw) {
549
557
  extra: n.description ? { description: n.description } : void 0
550
558
  }));
551
559
  const placed = (i) => "position" in i && "size" in i;
552
- const interactions = list(data.interactions).filter((i) => i !== null && typeof i === "object").filter((i) => data.version !== TT_EDGE_INTERACTIONS_VERSION || placed(i)).map((i) => ({
560
+ const interactions = objects(data.interactions).filter((i) => data.version !== TT_EDGE_INTERACTIONS_VERSION || placed(i)).map((i) => ({
553
561
  id: i.id,
554
562
  type: "interaction",
555
563
  name: i.label,
556
564
  extra: { mode: i.mode }
557
565
  }));
566
+ const flows = objects(data.flows).map((f) => ({
567
+ id: f.id,
568
+ type: "flow",
569
+ name: text(f.label)
570
+ }));
571
+ const attachable = new Set([
572
+ ...teams,
573
+ ...interactions,
574
+ ...flows
575
+ ].map((n) => n.id));
576
+ const annotations = objects(data.annotations).map((a) => ({
577
+ id: a.id,
578
+ type: "annotation",
579
+ name: text(a.text),
580
+ extra: typeof a.attachedTo === "string" && attachable.has(a.attachedTo) ? { attachedTo: a.attachedTo } : void 0
581
+ }));
558
582
  return {
559
583
  notation: "team-topology",
560
- nodes: [...teams, ...interactions],
561
- edges: []
584
+ nodes: [
585
+ ...teams,
586
+ ...interactions,
587
+ ...flows,
588
+ ...annotations
589
+ ],
590
+ edges: [],
591
+ meta: { title: text(data.title) ?? null }
562
592
  };
563
593
  }
564
594
  const STORM_ID_PREFIX = {
@@ -1033,7 +1063,8 @@ function parseAnchor(text) {
1033
1063
  const end = text.indexOf(CLOSE, start);
1034
1064
  if (end === -1) return null;
1035
1065
  const anchor = {
1036
- process: "",
1066
+ notation: "",
1067
+ model: "",
1037
1068
  file: null,
1038
1069
  elements: [],
1039
1070
  processVersion: null
@@ -1044,7 +1075,8 @@ function parseAnchor(text) {
1044
1075
  if (sep === -1) continue;
1045
1076
  const key = line.slice(0, sep);
1046
1077
  const value = line.slice(sep + 2).trim();
1047
- if (key === "process") anchor.process = value;
1078
+ if (key === "model") anchor.model = value;
1079
+ else if (key === "notation") anchor.notation = value;
1048
1080
  else if (key === "file") anchor.file = value;
1049
1081
  else if (key === "version") anchor.processVersion = value;
1050
1082
  else if (key === "type") {
@@ -1067,7 +1099,7 @@ function parseAnchor(text) {
1067
1099
  });
1068
1100
  }
1069
1101
  }
1070
- return anchor.process ? anchor : null;
1102
+ return anchor.notation && anchor.model ? anchor : null;
1071
1103
  }
1072
1104
  //#endregion
1073
1105
  //#region ../github-app/src/todos.ts
@@ -1083,10 +1115,12 @@ function parseAnchor(text) {
1083
1115
  */
1084
1116
  /** the label every platform-managed todo carries */
1085
1117
  const TODO_LABEL = "todo";
1086
- /** the per-process label that makes the tracker-side filter cheap */
1087
- const processLabel = (processId) => `process:${processId}`;
1088
- /** the `labels=` filter for listing: every todo, or one process's */
1089
- const todoLabelQuery = (processId) => processId ? `${TODO_LABEL},${processLabel(processId)}` : TODO_LABEL;
1118
+ /** the per-model label that makes the tracker-side filter cheap:
1119
+ * `<notation>:<id>` (`bpmn:order-to-cash`, `wardley:tea-shop`) — ids are
1120
+ * unique per notation only */
1121
+ const modelLabel = (model) => `${model.notation}:${model.id}`;
1122
+ /** the `labels=` filter for listing: every todo, or one model's */
1123
+ const todoLabelQuery = (model) => model ? `${TODO_LABEL},${modelLabel(model)}` : TODO_LABEL;
1090
1124
  const isPullRequestRow = (row) => row.pull_request !== void 0;
1091
1125
  //#endregion
1092
1126
  //#region ../mcp-kit/index.ts
@@ -1369,16 +1403,55 @@ const DERIVERS = {
1369
1403
  };
1370
1404
  },
1371
1405
  "team-topology": (graph) => {
1372
- const interactions = graph.nodes.filter((n) => n.type === "interaction").length;
1406
+ const ofType = (type) => graph.nodes.filter((n) => n.type === type);
1407
+ const interactions = ofType("interaction");
1408
+ const flows = ofType("flow");
1409
+ const annotations = ofType("annotation");
1410
+ const teams = graph.nodes.filter((n) => ![
1411
+ "interaction",
1412
+ "flow",
1413
+ "annotation"
1414
+ ].includes(n.type));
1373
1415
  const stats = {
1374
- teams: graph.nodes.length - interactions,
1375
- interactions
1416
+ teams: teams.length,
1417
+ interactions: interactions.length,
1418
+ flows: flows.length,
1419
+ annotations: annotations.length
1420
+ };
1421
+ const shown = {
1422
+ teams: stats.teams,
1423
+ interactions: stats.interactions,
1424
+ ...stats.flows > 0 ? { flows: stats.flows } : {},
1425
+ ...stats.annotations > 0 ? { annotations: stats.annotations } : {}
1376
1426
  };
1427
+ const title = graph.meta?.title;
1377
1428
  return {
1378
1429
  notation: "team-topology",
1379
- name: null,
1380
- summary: `Team Topology with ${counted(stats)}`,
1381
- stats
1430
+ name: typeof title === "string" ? title : null,
1431
+ summary: `Team Topology with ${counted(shown)}`,
1432
+ stats,
1433
+ detail: {
1434
+ teams: teams.map((n) => ({
1435
+ id: n.id,
1436
+ label: n.name ?? null,
1437
+ type: n.type,
1438
+ description: n.extra?.description ?? null
1439
+ })),
1440
+ interactions: interactions.map((n) => ({
1441
+ id: n.id,
1442
+ label: n.name ?? null,
1443
+ mode: n.extra?.mode ?? null
1444
+ })),
1445
+ flows: flows.map((n) => ({
1446
+ id: n.id,
1447
+ label: n.name ?? null
1448
+ })),
1449
+ annotations: annotations.map((n) => ({
1450
+ id: n.id,
1451
+ text: n.name ?? null,
1452
+ attachedTo: n.extra?.attachedTo ?? null
1453
+ }))
1454
+ }
1382
1455
  };
1383
1456
  },
1384
1457
  "event-storming": (graph) => {
@@ -1617,7 +1690,7 @@ function readOnlyInstructions(on) {
1617
1690
  DIAGRAMS_IN_DOCUMENTS,
1618
1691
  "Start with `list_models` to see which models it holds.",
1619
1692
  "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).",
1620
- "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." : "."),
1693
+ "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 filed on models and their elements." : "."),
1621
1694
  "Live editing, the modeler widgets and release as a pull request are on the designIQ Live Host, not here."
1622
1695
  ].join(" ");
1623
1696
  }
@@ -1908,14 +1981,21 @@ function createMcpServer(root = DEFAULT_ROOT, todos, contributions = []) {
1908
1981
  const api = (todos.apiUrl ?? "https://api.github.com").replace(/\/$/, "");
1909
1982
  server.registerTool("list_todos", {
1910
1983
  title: "List open todos",
1911
- 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.",
1912
- inputSchema: z.object({ process: z.string().optional().describe("Only todos anchored to this process id, e.g. order-to-cash") }),
1984
+ 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 (notation, model id, model file, anchored elements). Optional filter on one model: `id` (and `notation` for a stem shared across notations; without it a process wins). Use for 'what is open on process X' or to cross-check a model answer against known open discrepancies.",
1985
+ inputSchema: z.object({
1986
+ id: z.string().optional().describe("Only todos of this model id (any notation, from list_models)"),
1987
+ notation: z.string().optional().describe("registry notation id — disambiguates a stem shared across notations")
1988
+ }),
1913
1989
  annotations: {
1914
1990
  readOnlyHint: true,
1915
1991
  openWorldHint: true
1916
1992
  }
1917
- }, safe(async ({ process: processId }) => {
1918
- const res = await fetch(`${api}/repos/${todosRepo}/issues?state=open&labels=${encodeURIComponent(todoLabelQuery(processId))}&per_page=100`, { headers: {
1993
+ }, safe(async ({ id, notation }) => {
1994
+ const model = id ? {
1995
+ notation: notation ?? (await findModel(id))?.notation ?? "bpmn",
1996
+ id
1997
+ } : void 0;
1998
+ const res = await fetch(`${api}/repos/${todosRepo}/issues?state=open&labels=${encodeURIComponent(todoLabelQuery(model))}&per_page=100`, { headers: {
1919
1999
  accept: "application/vnd.github+json",
1920
2000
  authorization: `Bearer ${todosToken}`,
1921
2001
  "user-agent": "designiq-mcp"
@@ -1929,7 +2009,10 @@ function createMcpServer(root = DEFAULT_ROOT, todos, contributions = []) {
1929
2009
  assignees: (row.assignees ?? []).map((a) => a.login),
1930
2010
  createdAt: row.created_at
1931
2011
  }));
1932
- if (todos.length === 0) return ok(`No open todos${processId ? ` for process '${processId}'` : ""} in ${todosRepo}.`);
2012
+ if (todos.length === 0) {
2013
+ const of = model ? ` for ${model.notation === "bpmn" ? "process" : model.notation} '${model.id}'` : "";
2014
+ return ok(`No open todos${of} in ${todosRepo}.`);
2015
+ }
1933
2016
  return ok(todos);
1934
2017
  }));
1935
2018
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miragon/design-iq-mcp",
3
- "version": "5.9.0",
3
+ "version": "5.11.0",
4
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",