@dforge-core/dforge-mcp 0.1.8 → 0.1.9
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/server.js +298 -291
- package/package.json +4 -4
- package/resources/docs/dsl-reference.md +16 -3
- package/resources/schemas/entity.schema.json +10 -0
- package/resources/schemas/triggers.schema.json +1 -1
- package/skills/dforge-mcp-author/references/action-dsl.md +2 -1
- package/skills/dforge-mcp-author/references/field-types.md +6 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dforge-core/dforge-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "MCP server for dForge module authoring. Exposes scaffold/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",
|
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@dforge-core/dforge-cli": "^0.2.7",
|
|
35
|
-
"@dforge-core/metadata": "^0.0.
|
|
35
|
+
"@dforge-core/metadata": "^0.0.8",
|
|
36
36
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
37
37
|
"zod": "^4.4.3"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@types/node": "^22.
|
|
40
|
+
"@types/node": "^22.20.1",
|
|
41
41
|
"tsup": "^8.5.1",
|
|
42
42
|
"typescript": "^6.0.3",
|
|
43
|
-
"vitest": "^3.2.
|
|
43
|
+
"vitest": "^3.2.7"
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -168,15 +168,28 @@ var po = insert('purchase_order', { supplier_id: params[supplier].supplier_id })
|
|
|
168
168
|
insert('po_line', { po_id: po.purchase_order_id, product_id: [product_id], qty: 1 })
|
|
169
169
|
```
|
|
170
170
|
|
|
171
|
-
#### `getRecord(entityCd,
|
|
172
|
-
Fetch a single record by
|
|
171
|
+
#### `getRecord(entityCd, key) → record`
|
|
172
|
+
Fetch a single record by key. **Throws** a localized "not found" error if no row
|
|
173
|
+
matches — the safe default for a lookup that should always resolve (e.g. an FK you
|
|
174
|
+
just read). Fields are readable by dot notation (`rec.name`) or `rec.get('name')`.
|
|
175
|
+
`key` is a scalar PK, or an object for a compound key: `getRecord('gl.tag', { tag_group: 'REGION', tag_code: 'EU' })`.
|
|
176
|
+
The result is a **read-only** snapshot — writing to it (`rec.set(...)`) throws; use
|
|
177
|
+
`update()` to persist. A null key value can't match, so it counts as not-found.
|
|
173
178
|
|
|
174
179
|
```dsl
|
|
175
180
|
var rec = getRecord('crm.customer', [customer_id])
|
|
176
|
-
if (rec == null) { error('Customer not found') }
|
|
177
181
|
info('Customer: ' + rec.name + ' — credit limit $' + rec.credit_limit)
|
|
178
182
|
```
|
|
179
183
|
|
|
184
|
+
#### `getRecordOrNull(entityCd, key) → record | null`
|
|
185
|
+
Same as `getRecord`, but returns `null` instead of throwing when the row is absent —
|
|
186
|
+
use it when a missing record is an expected outcome (optional lookup, upsert probe).
|
|
187
|
+
|
|
188
|
+
```dsl
|
|
189
|
+
var existing = getRecordOrNull('crm.customer', [customer_id])
|
|
190
|
+
if (existing == null) { insert('crm.customer', { customer_id: [customer_id], name: [name] }) }
|
|
191
|
+
```
|
|
192
|
+
|
|
180
193
|
#### `callProc(name, args?) → record`
|
|
181
194
|
Call a stored procedure with named args. Returns the proc's result row (or rows if it RETURNS TABLE).
|
|
182
195
|
|
|
@@ -472,6 +472,16 @@
|
|
|
472
472
|
"field": { "type": "string" }
|
|
473
473
|
},
|
|
474
474
|
"additionalProperties": false
|
|
475
|
+
},
|
|
476
|
+
"onDelete": {
|
|
477
|
+
"type": "string",
|
|
478
|
+
"enum": ["cascade", "setNull", "restrict", "noAction"],
|
|
479
|
+
"description": "FK ON DELETE behaviour. Default (omitted) = noAction (plain FK). 'cascade' deletes children with the parent; 'setNull' nulls the FK column (must be nullable)."
|
|
480
|
+
},
|
|
481
|
+
"onUpdate": {
|
|
482
|
+
"type": "string",
|
|
483
|
+
"enum": ["cascade", "setNull", "restrict", "noAction"],
|
|
484
|
+
"description": "FK ON UPDATE behaviour. Default (omitted) = noAction. Fires when a parent's key value changes — a no-op for immutable cuid PKs, but useful for entities keyed on a natural/mutable PK."
|
|
475
485
|
}
|
|
476
486
|
},
|
|
477
487
|
"additionalProperties": false
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"async": {
|
|
51
51
|
"type": "boolean",
|
|
52
52
|
"default": false,
|
|
53
|
-
"description": "When true, the action runs in the background — the triggering transaction commits before the action starts. Recommended for slow actions (emails, external API calls). When false, the action runs
|
|
53
|
+
"description": "When true, 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. Keep async:false actions fast — they run on the request's transaction."
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
56
|
"additionalProperties": false
|
|
@@ -107,7 +107,8 @@ var note = params[note] // no quotes around param name in this DSL
|
|
|
107
107
|
|---|---|---|
|
|
108
108
|
| `insert('entity', { field: value, … })` | Inserted row object | Insert a new record. Returns the full row including auto-filled columns (PK, audit, number sequences). Access returned fields with `result.field_name`. |
|
|
109
109
|
| `query('SQL', { arg: value })` | Array of row objects | Execute parameterized SQL. Use `@argName` placeholders. Schema-qualified table names (`crm.contact`). Access row fields with `row.field_name` or `row['field_name']`. |
|
|
110
|
-
| `getRecord('entity',
|
|
110
|
+
| `getRecord('entity', key)` | Row object | Fetch a single record by key (qualified `module.entity` supported). `key` is a scalar PK or an object for a compound key (`{ col_a: 1, col_b: 'x' }`). Access fields with `rec.field_name` (or `rec.get('field_name')`). **Throws** a localized "not found" error if absent. |
|
|
111
|
+
| `getRecordOrNull('entity', key)` | Row object or `null` | Like `getRecord`, but returns `null` instead of throwing when the row is absent — use for an expected-absence lookup, guarded with `if (rec == null)`. |
|
|
111
112
|
| `preloadRef('fk_column')` | Row object | Load the referenced record for a FK column. Access its fields with `ref.get('field')`. |
|
|
112
113
|
|
|
113
114
|
### Messaging
|
|
@@ -53,10 +53,12 @@ Source of truth: `server/database/system-modules/metadata/seed-data/field_types.
|
|
|
53
53
|
|
|
54
54
|
## Binary / file
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
| `
|
|
59
|
-
|
|
56
|
+
> **Stored as `jsonb`, not `bytea`.** Despite the `binary` base datatype, a `file`/`image` column holds a JSON metadata reference (`{ storagePath, fileName, … }`) — the bytes live in file storage. The field tools derive `jsonb`; do **not** set `dbDatatype: "bytea"`.
|
|
57
|
+
|
|
58
|
+
| `fieldTypeCd` | `baseDatatypeCd` | Description | Common `dbDatatype` | Params |
|
|
59
|
+
|---|---|---|---|---|
|
|
60
|
+
| `file` | `binary` | Arbitrary file upload. | `jsonb` | `accept`, `maxSize` |
|
|
61
|
+
| `image` | `binary` | Image upload with preview. | `jsonb` | `maxSize`, `dimensions` |
|
|
60
62
|
|
|
61
63
|
## Reference / lookup / set
|
|
62
64
|
|