@miadi/stateloom-mcp 0.2.5 → 0.2.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.
- package/README.md +21 -1
- package/dist/erd.d.ts +7 -0
- package/dist/erd.js +17 -1
- package/dist/server.d.ts +3 -0
- package/dist/server.js +41 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -116,8 +116,28 @@ outside the root. `generate_code` writes only to a temporary directory. Note tha
|
|
|
116
116
|
| `generate_code` | Emit Python or TypeScript from the definition |
|
|
117
117
|
| `generate_rispec` | Emit a RISE rispec in markdown, folding in the source PDE's intent when the SMDF carries one |
|
|
118
118
|
| `render_diagram` | Draw the machine as `png`, `svg`, `mermaid` or `ascii`, write it beside the document, and hand a `png` back inside the tool result so the agent sees what it designed |
|
|
119
|
-
| `set_project_file` | Re-point the loom at another `.smdf.json`
|
|
119
|
+
| `set_project_file` | Re-point the loom at another document mid-session — a `.smdf.json` or a `.erdf.json`; disk target and live room both move |
|
|
120
120
|
| `get_project_file` | The active document path, whether it exists, and bridge status |
|
|
121
|
+
| `get_notes` | Every note in the active document in one call — the diagram's own first, then each shape that has one |
|
|
122
|
+
| `set_notes` | Save (or, with an empty string, clear) working notes on the diagram or on one state / entity. Kept in the document for whoever opens it next; ignored by engines and codegen |
|
|
123
|
+
|
|
124
|
+
### Entity-relationship diagrams
|
|
125
|
+
|
|
126
|
+
The same server designs the **data** beside the machines, in a sibling `.erdf.json`. The
|
|
127
|
+
active document's type is its extension: while an ERD is active, `get_definition`,
|
|
128
|
+
`load_definition` and `render_diagram` (mermaid `erDiagram`) answer for it, and the
|
|
129
|
+
state-machine tools refuse to write a machine over it.
|
|
130
|
+
|
|
131
|
+
| Tool | What it does |
|
|
132
|
+
|---|---|
|
|
133
|
+
| `create_erd` | Start an ERD beside the active document and make it the active one |
|
|
134
|
+
| `add_entity` / `update_entity` / `remove_entity` | An entity, optionally `weak`; removing one takes its relationships with it |
|
|
135
|
+
| `add_attribute` / `remove_attribute` | `type`, `key` (`pk` / `uk`), `references` (the foreign key), `nullable`, and `stateOf` — the state machine whose state this attribute stores |
|
|
136
|
+
| `add_relationship` / `remove_relationship` | `1:1`, `1:N`, `N:1`, `N:M`, read left to right, with a verb as the label |
|
|
137
|
+
| `validate_erd` | Rules E001–E005 |
|
|
138
|
+
| `check_links` | Rules L001–L004: what a state machine names — an object's class, a field in a guard, a `stateOf` — that the ERD does not carry |
|
|
139
|
+
|
|
140
|
+
Format and rules: [Spec 80 — ERDF](https://github.com/jgwill/smcraft/blob/main/rispecs/80-erdf-format.spec.md).
|
|
121
141
|
|
|
122
142
|
Also exposed: the resource `smcraft://definition` (the current definition), and the prompt `design-state-machine` — a guided conversation for building one from a domain description.
|
|
123
143
|
|
package/dist/erd.d.ts
CHANGED
|
@@ -43,5 +43,12 @@ export declare function erdRender(host: ErdHost, opts: {
|
|
|
43
43
|
path?: string;
|
|
44
44
|
stamp?: boolean;
|
|
45
45
|
}): ToolResult;
|
|
46
|
+
/** One block per note, the diagram's own first. What `get_notes` answers with for either document type. */
|
|
47
|
+
export declare function formatNotes(name: string, path: string, entries: {
|
|
48
|
+
target: string | null;
|
|
49
|
+
notes: string;
|
|
50
|
+
}[]): string;
|
|
51
|
+
export declare function erdGetNotes(host: ErdHost): ToolResult;
|
|
52
|
+
export declare function erdSetNotes(host: ErdHost, target: string | undefined, notes: string): ToolResult;
|
|
46
53
|
export declare function registerErdTools(server: McpServer, host: ErdHost): void;
|
|
47
54
|
export {};
|
package/dist/erd.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { existsSync, readFileSync, readdirSync, writeFileSync } from "fs";
|
|
3
3
|
import { dirname, join, resolve } from "path";
|
|
4
|
-
import { addAttribute, addEntity, addRelationship, checkLinks, checkStateOf, emptyErd, ERD_CARDINALITIES, isErdDefinition, isErdfPath, removeAttribute, removeEntity, removeRelationship, renderMermaidEr, updateEntity, summarizeErd, validateErd, } from "@miadi/stateloom-protocol";
|
|
4
|
+
import { addAttribute, addEntity, addRelationship, checkLinks, checkStateOf, collectNotes, emptyErd, ERD_CARDINALITIES, isErdDefinition, isErdfPath, removeAttribute, removeEntity, removeRelationship, renderMermaidEr, updateEntity, updateErdSettings, summarizeErd, validateErd, } from "@miadi/stateloom-protocol";
|
|
5
5
|
import { stampedOutputPath } from "@miadi/stateloom-cli/render";
|
|
6
6
|
const ok = (text) => ({ content: [{ type: "text", text }] });
|
|
7
7
|
const fail = (text) => ({ content: [{ type: "text", text }], isError: true });
|
|
@@ -101,6 +101,22 @@ export function erdRender(host, opts) {
|
|
|
101
101
|
],
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
|
+
/** One block per note, the diagram's own first. What `get_notes` answers with for either document type. */
|
|
105
|
+
export function formatNotes(name, path, entries) {
|
|
106
|
+
if (entries.length === 0)
|
|
107
|
+
return `No notes in '${name}' (${path}).`;
|
|
108
|
+
const blocks = entries.map((n) => `[${n.target ?? "diagram"}]\n${n.notes}`);
|
|
109
|
+
return `${entries.length} note(s) in '${name}' (${path}):\n\n${blocks.join("\n\n")}`;
|
|
110
|
+
}
|
|
111
|
+
export function erdGetNotes(host) {
|
|
112
|
+
const def = readErd(host.projectFile());
|
|
113
|
+
if (!def)
|
|
114
|
+
return fail(`No ERD at ${host.projectFile()}.`);
|
|
115
|
+
return ok(formatNotes(def.settings?.name ?? "", host.projectFile(), collectNotes(def)));
|
|
116
|
+
}
|
|
117
|
+
export function erdSetNotes(host, target, notes) {
|
|
118
|
+
return mutate(host, (def) => (target ? updateEntity(def, target, { notes }) : updateErdSettings(def, { notes })), () => `${notes.trim() ? "Saved" : "Cleared"} the notes on ${target ? `entity '${target}'` : "the diagram"}.`);
|
|
119
|
+
}
|
|
104
120
|
const formatErrors = (errors) => errors.map((e) => `[${e.ruleId}] ${e.message}`).join("\n");
|
|
105
121
|
function readSmdf(path) {
|
|
106
122
|
if (!existsSync(path))
|
package/dist/server.d.ts
CHANGED
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
* - create_erd, add_entity, update_entity, add_attribute, add_relationship
|
|
26
26
|
* - remove_entity, remove_attribute, remove_relationship
|
|
27
27
|
* - validate_erd, check_links
|
|
28
|
+
*
|
|
29
|
+
* Notes (either document type): set_notes, get_notes — what a person or an
|
|
30
|
+
* agent wrote down about the diagram or one of its shapes, saved in the file.
|
|
28
31
|
* get_definition, load_definition and render_diagram answer for an ERD when
|
|
29
32
|
* the active document is one.
|
|
30
33
|
*
|
package/dist/server.js
CHANGED
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
* - create_erd, add_entity, update_entity, add_attribute, add_relationship
|
|
26
26
|
* - remove_entity, remove_attribute, remove_relationship
|
|
27
27
|
* - validate_erd, check_links
|
|
28
|
+
*
|
|
29
|
+
* Notes (either document type): set_notes, get_notes — what a person or an
|
|
30
|
+
* agent wrote down about the diagram or one of its shapes, saved in the file.
|
|
28
31
|
* get_definition, load_definition and render_diagram answer for an ERD when
|
|
29
32
|
* the active document is one.
|
|
30
33
|
*
|
|
@@ -46,10 +49,10 @@ import { basename, dirname, join, resolve } from "path";
|
|
|
46
49
|
import { fileURLToPath } from "node:url";
|
|
47
50
|
import { tmpdir } from "os";
|
|
48
51
|
import { createBridgeClient } from "@miadi/stateloom-client";
|
|
49
|
-
import { envAlias, isErdfPath, } from "@miadi/stateloom-protocol";
|
|
52
|
+
import { collectNotes, envAlias, isErdfPath, } from "@miadi/stateloom-protocol";
|
|
50
53
|
import { renderDiagramToFile, defaultOutputPath } from "@miadi/stateloom-cli/render";
|
|
51
54
|
import { resolveProjectSwitch } from "./projectSwitch.js";
|
|
52
|
-
import { registerErdTools, erdSummary, erdGetDefinition, erdLoadDefinition, erdRender, } from "./erd.js";
|
|
55
|
+
import { registerErdTools, erdSummary, erdGetDefinition, erdLoadDefinition, erdRender, erdGetNotes, erdSetNotes, formatNotes, } from "./erd.js";
|
|
53
56
|
// STATELOOM_* read first, SMCRAFT_* legacy twin honored — the live MCP
|
|
54
57
|
// registration bakes SMCRAFT_PROJECT_FILE and must keep working (2026-07-27
|
|
55
58
|
// rename brief). Resolved once at boot, like the direct reads they replace.
|
|
@@ -1006,6 +1009,42 @@ function buildServer() {
|
|
|
1006
1009
|
}
|
|
1007
1010
|
});
|
|
1008
1011
|
registerErdTools(server, erdHost);
|
|
1012
|
+
// Notes — the same two tools for either document type.
|
|
1013
|
+
server.tool("set_notes", "Save working notes on the active diagram or on one of its shapes — what was said about it, an open question, a decision — so whoever opens the document next (a person or an agent) finds them. Omit `target` for the whole diagram; otherwise `target` is a state name (state machine) or an entity name (ERD). The text replaces what was there; an empty string clears it. Notes are kept in the document and are not part of the model: engines and code generation ignore them.", { target: z.string().optional(), notes: z.string() }, async ({ target, notes }) => {
|
|
1014
|
+
if (isErdfPath(PROJECT_FILE))
|
|
1015
|
+
return erdSetNotes(erdHost, target, notes);
|
|
1016
|
+
const def = readDef();
|
|
1017
|
+
if (!def)
|
|
1018
|
+
return { content: [{ type: "text", text: `No state machine at ${PROJECT_FILE}.` }], isError: true };
|
|
1019
|
+
const holder = target ? findState(def.state, target) : def.settings;
|
|
1020
|
+
if (!holder)
|
|
1021
|
+
return { content: [{ type: "text", text: `State '${target}' not found.` }], isError: true };
|
|
1022
|
+
if (notes.trim())
|
|
1023
|
+
holder.notes = notes;
|
|
1024
|
+
else
|
|
1025
|
+
delete holder.notes;
|
|
1026
|
+
writeDef(def);
|
|
1027
|
+
// "" is how an erased note travels; the far side removes the key.
|
|
1028
|
+
const patch = { notes: notes.trim() ? notes : "" };
|
|
1029
|
+
bridgeEmitPatch([target ? { op: "state.update", name: target, patch } : { op: "settings.update", patch }]);
|
|
1030
|
+
return {
|
|
1031
|
+
content: [
|
|
1032
|
+
{
|
|
1033
|
+
type: "text",
|
|
1034
|
+
text: `${notes.trim() ? "Saved" : "Cleared"} the notes on ${target ? `state '${target}'` : "the diagram"}.`,
|
|
1035
|
+
},
|
|
1036
|
+
],
|
|
1037
|
+
};
|
|
1038
|
+
});
|
|
1039
|
+
server.tool("get_notes", "Read every note in the active document in one call — the diagram's own first, then each shape that has one. Read them before changing a diagram somebody else has been working on: they are where a person leaves what they were thinking.", {}, async () => {
|
|
1040
|
+
if (isErdfPath(PROJECT_FILE))
|
|
1041
|
+
return erdGetNotes(erdHost);
|
|
1042
|
+
const def = readDef();
|
|
1043
|
+
if (!def)
|
|
1044
|
+
return { content: [{ type: "text", text: `No state machine at ${PROJECT_FILE}.` }], isError: true };
|
|
1045
|
+
const entries = collectNotes(def);
|
|
1046
|
+
return { content: [{ type: "text", text: formatNotes(def.settings.name, PROJECT_FILE, entries) }] };
|
|
1047
|
+
});
|
|
1009
1048
|
// Resources
|
|
1010
1049
|
server.resource("definition", "smcraft://definition", async () => {
|
|
1011
1050
|
const def = readDef();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miadi/stateloom-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "MCP server for miadi-stateloom (formerly smcraft-mcp) — lets LLM agents design state machines through conversation",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@miadi/stateloom-cli": "^0.1.3",
|
|
42
42
|
"@miadi/stateloom-client": "^0.1.2",
|
|
43
|
-
"@miadi/stateloom-protocol": "^0.1.
|
|
43
|
+
"@miadi/stateloom-protocol": "^0.1.8",
|
|
44
44
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
45
45
|
"zod": "^4.3.6"
|
|
46
46
|
},
|