@dforge-core/dforge-mcp 0.2.16 → 0.2.17
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/CHANGELOG.md +36 -0
- package/dist/server.js +3 -3
- package/package.json +3 -3
- package/resources/schemas/deps.schema.json +1 -1
- package/resources/schemas/manifest.schema.json +1 -1
- package/resources/schemas/menus.schema.json +1 -1
- package/resources/schemas/triggers.schema.json +1 -1
- package/skills/dforge-module-build/SKILL.md +7 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,42 @@ All notable changes to `@dforge-core/dforge-mcp`. This project uses semver-ish
|
|
|
4
4
|
`0.1.0-rc.N` pre-release tags; the published version is set at publish time via
|
|
5
5
|
the release workflow, so committed `package.json` versions are placeholders.
|
|
6
6
|
|
|
7
|
+
## 0.2.17
|
|
8
|
+
|
|
9
|
+
A bridge module's `async: false` trigger **can** roll back its host module's write,
|
|
10
|
+
and that is intended (platform issue #999). It is the same coupling a mandatory
|
|
11
|
+
extension column already creates — mark one `M` and the host's own `data.insert`
|
|
12
|
+
is refused with *"Missing required columns: …"* for a field the host knows nothing
|
|
13
|
+
about. A bridge that can add a required field can add a required *condition*, and
|
|
14
|
+
cross-module invariants are what bridge modules exist to enforce.
|
|
15
|
+
|
|
16
|
+
What changed platform-side is that the veto now says who issued it, so the guidance
|
|
17
|
+
here says what to point it at.
|
|
18
|
+
|
|
19
|
+
### Changed — `async: false` is a refusal mechanism, not a reaction
|
|
20
|
+
|
|
21
|
+
`dforge_trigger_add`'s `async` description and `dforge-module-build` Phase 2b both
|
|
22
|
+
said only that a failure "rolls back the original change" — true, and read by an
|
|
23
|
+
authoring agent as a footnote about transactions rather than as the decision it is.
|
|
24
|
+
Both now state that the rollback reaches **another module's** entity, and the rule
|
|
25
|
+
that follows: pick `async: false` to enforce an invariant, not merely to react,
|
|
26
|
+
because a bug in the action blocks the host's workflow exactly as effectively as a
|
|
27
|
+
real violation. Keep such an action to the check and put the rest behind
|
|
28
|
+
`try`/`catch` or in an `async: true` trigger.
|
|
29
|
+
|
|
30
|
+
### Changed — a blocked write names the module that blocked it
|
|
31
|
+
|
|
32
|
+
Trigger failures now report error code **`TRIGGER_ACTION_FAIL`** (the other action
|
|
33
|
+
phases keep `ACTION_EXECUTION_FAIL`), rendered as *Blocked by "crm-fin" (automation
|
|
34
|
+
"on_invoice_posted"): Credit limit exceeded.* in the caller's locale, with the
|
|
35
|
+
action's own `error()` text passed through. The module named is the one that
|
|
36
|
+
**declared the trigger**, not the one owning the action — a bridge routinely
|
|
37
|
+
subscribes its own trigger to an action living elsewhere, and what refused the write
|
|
38
|
+
is the subscription. Nothing to change in a module for this; it is what an author
|
|
39
|
+
should expect to see when their trigger fires.
|
|
40
|
+
|
|
41
|
+
Nothing is rejected that was accepted before — `dforge_module_validate` is unchanged.
|
|
42
|
+
|
|
7
43
|
## 0.2.16
|
|
8
44
|
|
|
9
45
|
Entity views — the platform's **column-level security** — became reachable from a
|
package/dist/server.js
CHANGED
|
@@ -31002,7 +31002,7 @@ var import_node_crypto = require("crypto");
|
|
|
31002
31002
|
var fs2 = __toESM(require("fs"));
|
|
31003
31003
|
var path2 = __toESM(require("path"));
|
|
31004
31004
|
|
|
31005
|
-
// node_modules/.pnpm/@dforge-core+dforge-cli@0.2.
|
|
31005
|
+
// node_modules/.pnpm/@dforge-core+dforge-cli@0.2.15/node_modules/@dforge-core/dforge-cli/dist/lib.mjs
|
|
31006
31006
|
function buildManifest(opts, moduleId) {
|
|
31007
31007
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
31008
31008
|
const manifest = {
|
|
@@ -31214,7 +31214,7 @@ function buildZedSettings() {
|
|
|
31214
31214
|
var fs = __toESM(require("fs"));
|
|
31215
31215
|
var path = __toESM(require("path"));
|
|
31216
31216
|
|
|
31217
|
-
// node_modules/.pnpm/@dforge-core+metadata@0.0.
|
|
31217
|
+
// node_modules/.pnpm/@dforge-core+metadata@0.0.20/node_modules/@dforge-core/metadata/dist/index.js
|
|
31218
31218
|
var AggType = {
|
|
31219
31219
|
Sum: "sum",
|
|
31220
31220
|
Count: "count",
|
|
@@ -36248,7 +36248,7 @@ var triggerAddSchema = {
|
|
|
36248
36248
|
"STATIC params only \u2014 the literal values given here. Nothing is injected at runtime; there is no record_id param. The affected record arrives as record context ([field]), its pre-change values as old[field]."
|
|
36249
36249
|
),
|
|
36250
36250
|
async: external_exports.boolean().default(true).describe(
|
|
36251
|
-
"When true (the platform default), the action runs in background \u2014 recommended for slow actions. When false, it runs in the caller's transaction, so a failing action ROLLS BACK the user's original write."
|
|
36251
|
+
"When true (the platform default), the action runs in background \u2014 recommended for slow actions. When false, it runs in the caller's transaction, so a failing action ROLLS BACK the user's original write. That applies to a trigger on ANOTHER module's entity too: a bridge module CAN veto its host's write, the same way a mandatory extension column already refuses one. So async:false is a refusal mechanism \u2014 pick it to enforce an invariant, not merely to react, because a bug in the action blocks the host's workflow just as effectively as a real violation. The rollback is reported to the user as TRIGGER_ACTION_FAIL, naming the blocking module and trigger."
|
|
36252
36252
|
)
|
|
36253
36253
|
};
|
|
36254
36254
|
function triggerAdd(args) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dforge-core/dforge-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.17",
|
|
4
4
|
"description": "MCP server for dForge module authoring. Exposes 34 scaffold/patch/validate/pack/install tools and schema resources so AI agents (Claude Code, Cursor, Zed) can create and ship dForge modules.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/iash44/dForge-core",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"install-skills": "node scripts/install-skills.mjs"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@dforge-core/dforge-cli": "^0.2.
|
|
39
|
-
"@dforge-core/metadata": "^0.0.
|
|
38
|
+
"@dforge-core/dforge-cli": "^0.2.15",
|
|
39
|
+
"@dforge-core/metadata": "^0.0.20",
|
|
40
40
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
41
41
|
"zod": "^4.4.3"
|
|
42
42
|
},
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"columns": {
|
|
42
42
|
"type": "object",
|
|
43
|
-
"description": "Provider columns the consumer actually reads, writes, or joins on. Omit entirely for extension-only targets that touch no existing provider column.",
|
|
43
|
+
"description": "Provider columns the consumer actually reads, writes, or joins on. Omit entirely for extension-only targets that touch no existing provider column. A column your OWN 'extends' file adds to the provider needs no entry either — the platform exempts those and validates them separately — and a VIRTUAL extension field (columnType R/S/F: the lookup beside an FK, a reverse set, a formula) must never be listed at all: it is metadata, never a provider storage column, so no storage class can ever match and the install-time satisfaction check rejects the declaration. Declare the FK you add (e.g. trip_id), never the lookup beside it (trip).",
|
|
44
44
|
"additionalProperties": { "$ref": "#/definitions/columnContract" }
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"schemaName": {
|
|
51
51
|
"type": "string",
|
|
52
|
-
"description": "Database schema name override (defaults to module code, system modules use 'dForge')"
|
|
52
|
+
"description": "Database schema name override (defaults to module code, system modules use 'dForge'). Do NOT share one schema between modules that extend other modules' entities: an extension's columns live in \"{schemaName}\".\"{entity}_ext\", named for the schema rather than the module, and nothing records which module wrote a column — so install rejects a module that both shares its schema with another installed module and declares 'extends'. Leave unset (the module code, which is unique) for bridge modules."
|
|
53
53
|
},
|
|
54
54
|
"system": {
|
|
55
55
|
"type": "boolean",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"folders": {
|
|
24
24
|
"type": "array",
|
|
25
25
|
"items": { "type": "string" },
|
|
26
|
-
"description": "
|
|
26
|
+
"description": "Flat folder codes this menu is linked to (module code for the root, module code + '_' + each nesting segment for children, e.g. 'logistics_all_trips'; the full path, the module-relative path, and a folder's own key when unique in the tree also resolve). Omit to link the menu to every folder the module declares. Scopes the folder-to-menu link only — every folder in ui/folders.json is created either way. A code matching no folder, or a key two branches share, fails the install."
|
|
27
27
|
},
|
|
28
28
|
"items": {
|
|
29
29
|
"type": "object",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"async": {
|
|
51
51
|
"type": "boolean",
|
|
52
52
|
"default": true,
|
|
53
|
-
"description": "When true (the default), the action runs in the background — the triggering transaction commits before the action starts. Recommended for slow actions (emails, external API calls); background failures are logged, not surfaced to the caller. When false, the action runs synchronously inside the same transaction as the triggering insert/update/delete: if the action fails (e.g. calls error()), the whole mutation rolls back and the error is returned to the caller — so an async:false trigger can undo the user's write. Keep async:false actions fast; they run on the request's transaction."
|
|
53
|
+
"description": "When true (the default), the action runs in the background — the triggering transaction commits before the action starts. Recommended for slow actions (emails, external API calls); background failures are logged, not surfaced to the caller. When false, the action runs synchronously inside the same transaction as the triggering insert/update/delete: if the action fails (e.g. calls error()), the whole mutation rolls back and the error is returned to the caller — so an async:false trigger can undo the user's write. That holds for a trigger on ANOTHER module's entity too (the bridge shape): a bridge can veto its host's write, the same way a mandatory extension column already refuses one. Use it to enforce an invariant, not merely to react — the error names the blocking module and trigger (code TRIGGER_ACTION_FAIL), and a bug in the action blocks the host's workflow just as effectively as a real violation. Keep async:false actions fast; they run on the request's transaction."
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
56
|
"additionalProperties": false
|
|
@@ -187,7 +187,13 @@ before committing, then `dforge_action_add`. One action per turn.
|
|
|
187
187
|
|
|
188
188
|
**2b Triggers** reference actions that must already exist. `async: true` runs
|
|
189
189
|
after the transaction commits (use for anything slow); `async: false` runs
|
|
190
|
-
in-transaction, so a failure rolls back the original change
|
|
190
|
+
in-transaction, so a failure rolls back the original change — **including on
|
|
191
|
+
another module's entity**. A bridge's `async: false` trigger vetoes its host's
|
|
192
|
+
write, exactly as a mandatory extension column already refuses one, so pick it
|
|
193
|
+
to enforce an invariant, not merely to react: a bug in the action blocks the
|
|
194
|
+
host's workflow as effectively as a real violation. Keep such an action to the
|
|
195
|
+
check itself and put everything else behind `try`/`catch` or in an
|
|
196
|
+
`async: true` trigger.
|
|
191
197
|
|
|
192
198
|
**2c Jobs** run as the system user with **no current record** — the action must
|
|
193
199
|
not use `[field]` syntax. Wrap a record-context action in a thin job action that
|