@modudraft/mcp 0.4.0 → 0.5.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 +31 -0
- package/dist/index.js +146 -3
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -104,6 +104,9 @@ Copy the key — you'll only see it once.
|
|
|
104
104
|
| `add_sequence_participant` | Register a named participant |
|
|
105
105
|
| `clear_sequence` | Remove all sequence messages |
|
|
106
106
|
| `auto_sequence` | Derive sequence messages from the architecture edges |
|
|
107
|
+
| `add_sequence_fragment` | Wrap messages in a loop / alt / opt / par fragment |
|
|
108
|
+
| `update_sequence_fragment` | Change a fragment's kind, condition, or message list |
|
|
109
|
+
| `delete_sequence_fragment` | Remove a fragment |
|
|
107
110
|
|
|
108
111
|
### DB schema (ER diagram)
|
|
109
112
|
| Tool | Description |
|
|
@@ -121,6 +124,28 @@ Copy the key — you'll only see it once.
|
|
|
121
124
|
| `delete_api_endpoint` | Remove an endpoint |
|
|
122
125
|
| `export_openapi` | Export schema as OpenAPI 3.0 JSON |
|
|
123
126
|
|
|
127
|
+
### Cloud diagram
|
|
128
|
+
| Tool | Description |
|
|
129
|
+
|---|---|
|
|
130
|
+
| `add_cloud_resource` | Add a cloud resource (cluster, VPC, service, etc.) |
|
|
131
|
+
| `update_cloud_resource` | Rename or change the type of a cloud resource |
|
|
132
|
+
| `delete_cloud_resource` | Remove a cloud resource |
|
|
133
|
+
|
|
134
|
+
### Network diagram
|
|
135
|
+
| Tool | Description |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `add_network_device` | Add a network device (router, switch, firewall, etc.) |
|
|
138
|
+
| `update_network_device` | Rename or change the type of a network device |
|
|
139
|
+
| `delete_network_device` | Remove a network device |
|
|
140
|
+
|
|
141
|
+
### Data flow diagram
|
|
142
|
+
| Tool | Description |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `add_data_flow_stage` | Add a pipeline stage (source, transform, sink, etc.) |
|
|
145
|
+
| `update_data_flow_stage` | Rename or change the type of a data flow stage |
|
|
146
|
+
| `delete_data_flow_stage` | Remove a data flow stage |
|
|
147
|
+
| `reorder_data_flow_stages` | Set the order of all stages in a data flow diagram |
|
|
148
|
+
|
|
124
149
|
### Discovery (static, no API call)
|
|
125
150
|
| Tool | Description |
|
|
126
151
|
|---|---|
|
|
@@ -140,6 +165,12 @@ Copy the key — you'll only see it once.
|
|
|
140
165
|
|
|
141
166
|
> "What metadata is missing from the diagram nodes? Fill in the hosts and ports based on our .env file."
|
|
142
167
|
|
|
168
|
+
> "Add a data flow diagram showing how events move from our Kafka topics through a Flink transform into the data warehouse."
|
|
169
|
+
|
|
170
|
+
> "Draw the network topology — edge router, core switch, two firewalls, and the DMZ segment."
|
|
171
|
+
|
|
172
|
+
> "Wrap the retry loop in the checkout sequence in an alt fragment with condition 'payment fails'."
|
|
173
|
+
|
|
143
174
|
---
|
|
144
175
|
|
|
145
176
|
## How it works
|
package/dist/index.js
CHANGED
|
@@ -25149,6 +25149,108 @@ var ALL_TOOLS = [
|
|
|
25149
25149
|
return { ok: true, messagesCreated: count };
|
|
25150
25150
|
}
|
|
25151
25151
|
},
|
|
25152
|
+
// ── Sequence fragments ───────────────────────────────────────────────────
|
|
25153
|
+
{
|
|
25154
|
+
name: "add_sequence_fragment",
|
|
25155
|
+
description: 'Wrap a group of sequence messages in a fragment box (loop, alt, opt, or par). Use message_ids to list the message IDs that fall inside the fragment. kind: "loop" for repetition, "alt" for conditional branches, "opt" for optional, "par" for parallel.',
|
|
25156
|
+
inputSchema: {
|
|
25157
|
+
type: "object",
|
|
25158
|
+
properties: {
|
|
25159
|
+
diagram_id: { type: "string" },
|
|
25160
|
+
kind: {
|
|
25161
|
+
type: "string",
|
|
25162
|
+
enum: ["loop", "alt", "opt", "par"],
|
|
25163
|
+
description: "Fragment kind"
|
|
25164
|
+
},
|
|
25165
|
+
condition: {
|
|
25166
|
+
type: "string",
|
|
25167
|
+
description: 'Guard condition label shown on the fragment (e.g. "valid credentials", "for each item")'
|
|
25168
|
+
},
|
|
25169
|
+
message_ids: {
|
|
25170
|
+
type: "array",
|
|
25171
|
+
items: { type: "string" },
|
|
25172
|
+
description: "Ordered list of sequence message IDs to include in this fragment"
|
|
25173
|
+
}
|
|
25174
|
+
},
|
|
25175
|
+
required: ["diagram_id", "kind", "condition", "message_ids"]
|
|
25176
|
+
},
|
|
25177
|
+
async handler(args) {
|
|
25178
|
+
const id = diagramId(args);
|
|
25179
|
+
let frag;
|
|
25180
|
+
await mutateDiagram(id, (entry) => {
|
|
25181
|
+
if (!Array.isArray(entry.sequenceFragments)) entry.sequenceFragments = [];
|
|
25182
|
+
const f = {
|
|
25183
|
+
id: genId(),
|
|
25184
|
+
kind: str(args.kind, "opt"),
|
|
25185
|
+
condition: str(args.condition),
|
|
25186
|
+
messageIds: Array.isArray(args.message_ids) ? args.message_ids : []
|
|
25187
|
+
};
|
|
25188
|
+
entry.sequenceFragments.push(f);
|
|
25189
|
+
frag = f;
|
|
25190
|
+
});
|
|
25191
|
+
return frag;
|
|
25192
|
+
}
|
|
25193
|
+
},
|
|
25194
|
+
{
|
|
25195
|
+
name: "update_sequence_fragment",
|
|
25196
|
+
description: "Update an existing sequence fragment \u2014 change its kind, condition label, or the set of enclosed message IDs.",
|
|
25197
|
+
inputSchema: {
|
|
25198
|
+
type: "object",
|
|
25199
|
+
properties: {
|
|
25200
|
+
diagram_id: { type: "string" },
|
|
25201
|
+
fragment_id: { type: "string", description: "ID of the fragment to update" },
|
|
25202
|
+
kind: { type: "string", enum: ["loop", "alt", "opt", "par"] },
|
|
25203
|
+
condition: { type: "string" },
|
|
25204
|
+
message_ids: { type: "array", items: { type: "string" } }
|
|
25205
|
+
},
|
|
25206
|
+
required: ["diagram_id", "fragment_id"]
|
|
25207
|
+
},
|
|
25208
|
+
async handler(args) {
|
|
25209
|
+
const id = diagramId(args);
|
|
25210
|
+
let found = false;
|
|
25211
|
+
await mutateDiagram(id, (entry) => {
|
|
25212
|
+
if (!Array.isArray(entry.sequenceFragments)) return;
|
|
25213
|
+
entry.sequenceFragments = entry.sequenceFragments.map((f) => {
|
|
25214
|
+
const frag = f;
|
|
25215
|
+
if (frag.id !== str(args.fragment_id)) return frag;
|
|
25216
|
+
found = true;
|
|
25217
|
+
return {
|
|
25218
|
+
...frag,
|
|
25219
|
+
...args.kind !== void 0 && { kind: str(args.kind) },
|
|
25220
|
+
...args.condition !== void 0 && { condition: str(args.condition) },
|
|
25221
|
+
...Array.isArray(args.message_ids) && { messageIds: args.message_ids }
|
|
25222
|
+
};
|
|
25223
|
+
});
|
|
25224
|
+
});
|
|
25225
|
+
if (!found) throw new Error(`Fragment ${args.fragment_id} not found`);
|
|
25226
|
+
return { ok: true };
|
|
25227
|
+
}
|
|
25228
|
+
},
|
|
25229
|
+
{
|
|
25230
|
+
name: "delete_sequence_fragment",
|
|
25231
|
+
description: "Remove a sequence fragment box. The enclosed messages remain; only the fragment wrapper is deleted.",
|
|
25232
|
+
inputSchema: {
|
|
25233
|
+
type: "object",
|
|
25234
|
+
properties: {
|
|
25235
|
+
diagram_id: { type: "string" },
|
|
25236
|
+
fragment_id: { type: "string" }
|
|
25237
|
+
},
|
|
25238
|
+
required: ["diagram_id", "fragment_id"]
|
|
25239
|
+
},
|
|
25240
|
+
async handler(args) {
|
|
25241
|
+
const id = diagramId(args);
|
|
25242
|
+
let removed = false;
|
|
25243
|
+
await mutateDiagram(id, (entry) => {
|
|
25244
|
+
if (!Array.isArray(entry.sequenceFragments)) return;
|
|
25245
|
+
const before = entry.sequenceFragments.length;
|
|
25246
|
+
entry.sequenceFragments = entry.sequenceFragments.filter(
|
|
25247
|
+
(f) => f.id !== str(args.fragment_id)
|
|
25248
|
+
);
|
|
25249
|
+
removed = entry.sequenceFragments.length < before;
|
|
25250
|
+
});
|
|
25251
|
+
return { ok: removed };
|
|
25252
|
+
}
|
|
25253
|
+
},
|
|
25152
25254
|
// ── DB schema (ER diagram) ───────────────────────────────────────────────
|
|
25153
25255
|
{
|
|
25154
25256
|
name: "add_db_table",
|
|
@@ -25728,14 +25830,53 @@ var ALL_TOOLS = [
|
|
|
25728
25830
|
return { ok: true, id: updated.id };
|
|
25729
25831
|
}
|
|
25730
25832
|
},
|
|
25731
|
-
// ── Data Flow stages
|
|
25833
|
+
// ── Data Flow flows + stages ──────────────────────────────────────────────
|
|
25834
|
+
{
|
|
25835
|
+
name: "list_data_flows",
|
|
25836
|
+
description: "List all named data flows in the Data Flow tab of a diagram.",
|
|
25837
|
+
inputSchema: {
|
|
25838
|
+
type: "object",
|
|
25839
|
+
properties: {
|
|
25840
|
+
diagram_id: { type: "string" }
|
|
25841
|
+
},
|
|
25842
|
+
required: ["diagram_id"]
|
|
25843
|
+
},
|
|
25844
|
+
async handler(args) {
|
|
25845
|
+
const { data } = await fetchDiagram(diagramId(args));
|
|
25846
|
+
const tab = data.diagrams.find((d) => d.type === "data-flow");
|
|
25847
|
+
return tab?.dataFlows ?? [];
|
|
25848
|
+
}
|
|
25849
|
+
},
|
|
25850
|
+
{
|
|
25851
|
+
name: "add_data_flow",
|
|
25852
|
+
description: "Create a new named data flow in the Data Flow tab. Returns the flow id to use when adding stages.",
|
|
25853
|
+
inputSchema: {
|
|
25854
|
+
type: "object",
|
|
25855
|
+
properties: {
|
|
25856
|
+
diagram_id: { type: "string" },
|
|
25857
|
+
name: { type: "string", description: 'Name for the flow (e.g. "Ingestion pipeline", "ETL")' }
|
|
25858
|
+
},
|
|
25859
|
+
required: ["diagram_id", "name"]
|
|
25860
|
+
},
|
|
25861
|
+
async handler(args) {
|
|
25862
|
+
const id = diagramId(args);
|
|
25863
|
+
let flow;
|
|
25864
|
+
await mutateTypedTab(id, "data-flow", (entry) => {
|
|
25865
|
+
const order = (entry.dataFlows ?? []).length;
|
|
25866
|
+
flow = { id: genId(), name: str(args.name, "Untitled flow"), order };
|
|
25867
|
+
entry.dataFlows = [...entry.dataFlows ?? [], flow];
|
|
25868
|
+
});
|
|
25869
|
+
return flow;
|
|
25870
|
+
}
|
|
25871
|
+
},
|
|
25732
25872
|
{
|
|
25733
25873
|
name: "add_data_flow_stage",
|
|
25734
|
-
description: "Add a stage to
|
|
25874
|
+
description: "Add a stage to a data flow. Stages have a role (source \u2192 transform \u2192 sink) and an optional tool. Pass flow_id to add to a specific flow (use list_data_flows or add_data_flow to get one). Stages are ordered by insertion; use reorder_data_flow_stages to rearrange.",
|
|
25735
25875
|
inputSchema: {
|
|
25736
25876
|
type: "object",
|
|
25737
25877
|
properties: {
|
|
25738
25878
|
diagram_id: { type: "string" },
|
|
25879
|
+
flow_id: { type: "string", description: "ID of the data flow this stage belongs to (from add_data_flow or list_data_flows)" },
|
|
25739
25880
|
type: {
|
|
25740
25881
|
type: "string",
|
|
25741
25882
|
enum: ["source", "transform", "sink"],
|
|
@@ -25750,9 +25891,11 @@ var ALL_TOOLS = [
|
|
|
25750
25891
|
const id = diagramId(args);
|
|
25751
25892
|
let stage;
|
|
25752
25893
|
await mutateTypedTab(id, "data-flow", (entry) => {
|
|
25753
|
-
const
|
|
25894
|
+
const flowId = str(args.flow_id) || void 0;
|
|
25895
|
+
const order = (entry.dataFlowStages ?? []).filter((s) => s.flowId === flowId).length;
|
|
25754
25896
|
stage = {
|
|
25755
25897
|
id: genId(),
|
|
25898
|
+
...flowId ? { flowId } : {},
|
|
25756
25899
|
type: str(args.type, "source"),
|
|
25757
25900
|
label: str(args.label),
|
|
25758
25901
|
tool: str(args.tool),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modudraft/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "MCP server for Modudraft — create and edit system-design diagrams via AI (cloud edition)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsup && node -e \"const fs=require('fs');fs.chmodSync('dist/index.js','755')\"",
|
|
11
11
|
"typecheck": "tsc --noEmit",
|
|
12
|
-
"dev": "tsup --watch"
|
|
12
|
+
"dev": "tsup --watch",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"test:watch": "vitest"
|
|
13
15
|
},
|
|
14
16
|
"files": [
|
|
15
17
|
"dist",
|
|
@@ -20,8 +22,9 @@
|
|
|
20
22
|
},
|
|
21
23
|
"devDependencies": {
|
|
22
24
|
"@modudraft/core": "*",
|
|
23
|
-
"@types/node": "^
|
|
25
|
+
"@types/node": "^26.0.0",
|
|
24
26
|
"tsup": "^8.5.0",
|
|
25
|
-
"typescript": "~5.8.0"
|
|
27
|
+
"typescript": "~5.8.0",
|
|
28
|
+
"vitest": "^4.0.0"
|
|
26
29
|
}
|
|
27
30
|
}
|