@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dforge-core/dforge-mcp",
3
- "version": "0.1.8",
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.5",
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.10.2",
40
+ "@types/node": "^22.20.1",
41
41
  "tsup": "^8.5.1",
42
42
  "typescript": "^6.0.3",
43
- "vitest": "^3.2.0"
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, id) → record | null`
172
- Fetch a single record by PK. Returns null if not found.
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 in the same transaction; failures roll back the original DB change."
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', id)` | Row object | Fetch a single record by primary key. |
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
- | `fieldTypeCd` | `baseDatatypeCd` | Description | Params |
57
- |---|---|---|---|
58
- | `file` | `binary` | Arbitrary file upload. | `accept`, `maxSize` |
59
- | `image` | `binary` | Image upload with preview. | `maxSize`, `dimensions` |
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