@dforge-core/dforge-mcp 0.1.0-rc.1

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.
@@ -0,0 +1,444 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://dforge.dev/schemas/entity.schema.json",
4
+ "title": "dForge Entity Definition",
5
+ "description": "Defines an entity (table/view mapping) for a dForge module",
6
+ "type": "object",
7
+ "required": ["fields"],
8
+ "properties": {
9
+ "extends": {
10
+ "type": "string",
11
+ "pattern": "^[a-z][a-z0-9_]*\\.[a-z][a-z0-9_]*$",
12
+ "description": "Qualified entity code of the target entity to extend (e.g. 'fin.invoice'). When set, this entity definition adds columns to an existing entity owned by another module instead of creating a new entity. Physical columns are stored in a 1:1 extension table."
13
+ },
14
+ "description": {
15
+ "type": "string",
16
+ "description": "Human-readable description of the entity"
17
+ },
18
+ "schema": {
19
+ "type": "string",
20
+ "description": "Database schema name override (defaults to module schema)"
21
+ },
22
+ "dbObject": {
23
+ "type": "string",
24
+ "description": "Database table or view name (defaults to entity code)"
25
+ },
26
+ "isView": {
27
+ "type": "boolean",
28
+ "description": "When true, entity maps to a SQL view (read-only, no DDL generation). Default: false (table-backed).",
29
+ "default": false
30
+ },
31
+ "viewSql": {
32
+ "type": "string",
33
+ "description": "SQL SELECT statement for view-backed entities (isView=true). Executed as CREATE OR REPLACE VIEW during module install."
34
+ },
35
+ "toString": {
36
+ "type": "string",
37
+ "description": "Display pattern using column placeholders, e.g. \"{first_name} {last_name}\""
38
+ },
39
+ "traits": {
40
+ "type": "array",
41
+ "description": "Entity traits expanded during module install (authoring shortcut). Trait definitions loaded from DB (seeded by metadata module).",
42
+ "items": {
43
+ "type": "string",
44
+ "enum": ["identity", "audit", "audit-full", "soft-delete", "sorting", "postable", "accumulation", "ledger", "period", "file-library"]
45
+ },
46
+ "uniqueItems": true
47
+ },
48
+ "fields": {
49
+ "type": "object",
50
+ "description": "Column definitions keyed by column code",
51
+ "additionalProperties": {
52
+ "$ref": "#/$defs/field"
53
+ }
54
+ },
55
+ "constraints": {
56
+ "type": "object",
57
+ "description": "Database constraints (unique, check, etc.)"
58
+ },
59
+ "references": {
60
+ "type": "object",
61
+ "description": "Foreign key reference definitions (legacy format, prefer 'link' on field)",
62
+ "additionalProperties": {
63
+ "$ref": "#/$defs/entityReference"
64
+ }
65
+ },
66
+ "indexes": {
67
+ "type": "object",
68
+ "description": "Database index definitions"
69
+ },
70
+ "numberSequence": {
71
+ "$ref": "#/$defs/numberSequence",
72
+ "description": "Automatic document numbering configuration for this entity"
73
+ },
74
+ "period": {
75
+ "$ref": "#/$defs/periodConfig",
76
+ "description": "Period entity configuration. Requires 'period' trait."
77
+ },
78
+ "accumulation": {
79
+ "$ref": "#/$defs/accumulationConfig",
80
+ "description": "Accumulation registry configuration (simple document without lines). Requires 'accumulation' trait."
81
+ },
82
+ "params": {
83
+ "type": "object",
84
+ "description": "Entity-level parameters for platform behavior flags",
85
+ "properties": {
86
+ "restricted": {
87
+ "type": "boolean",
88
+ "description": "When true, blocks generic data.get/data.set access. Entity must be accessed via dedicated API methods.",
89
+ "default": false
90
+ }
91
+ },
92
+ "additionalProperties": true
93
+ }
94
+ },
95
+ "additionalProperties": false,
96
+ "$defs": {
97
+ "field": {
98
+ "type": "object",
99
+ "description": "Entity column/field definition",
100
+ "properties": {
101
+ "dbDatatype": {
102
+ "type": "string",
103
+ "description": "PostgreSQL data type (e.g. uuid, varchar, int4, bool, text, json, numeric, date, timestamp, timestamptz, bpchar)"
104
+ },
105
+ "baseDatatypeCd": {
106
+ "type": "string",
107
+ "enum": ["binary", "bool", "cuid", "culture", "date", "guid", "link", "number", "project", "string", "time", "timestamp", "xml", "json"],
108
+ "description": "Base data type code from field_type table (e.g. string, number, boolean, date, time, timestamp, json, binary)"
109
+ },
110
+ "columnType": {
111
+ "type": "string",
112
+ "enum": ["D", "R", "S", "F", "A", "L", "G"],
113
+ "description": "Column type: D=Data (physical column), R=Reference (N:1 lookup), S=Set (1:N collection), F=Formula (virtual calculated), G=Generated/Computed (physical, auto-maintained), A=Accumulation registry, L=Ledger registry"
114
+ },
115
+ "fieldTypeCd": {
116
+ "type": "string",
117
+ "enum": [
118
+ "text", "email", "phone", "url", "textarea", "number", "currency", "percent",
119
+ "checkbox", "date", "datetime", "time",
120
+ "dropdown", "flags", "tags", "json", "hidden", "color", "image", "file",
121
+ "richtext", "lookup", "user", "grid", "entitylink", "code"
122
+ ],
123
+ "description": "UI field type code from field_type table"
124
+ },
125
+ "flags": {
126
+ "type": "string",
127
+ "pattern": "^[VIEMH]*$",
128
+ "description": "Display flags: V=Visible, I=Identity, E=Editable, M=Mandatory, H=Hidden"
129
+ },
130
+ "isPk": {
131
+ "type": "boolean",
132
+ "description": "Whether this column is part of the primary key"
133
+ },
134
+ "isNullable": {
135
+ "type": "boolean",
136
+ "description": "Whether the column allows NULL values"
137
+ },
138
+ "isIdentity": {
139
+ "type": "boolean",
140
+ "description": "Whether the column value is auto-generated (e.g. CUID primary key). Identity columns are not editable and excluded from required validation."
141
+ },
142
+ "formula": {
143
+ "type": "string",
144
+ "description": "Formula expression (for columnType 'F' or default values)"
145
+ },
146
+ "orderNum": {
147
+ "type": "integer",
148
+ "description": "Display order number"
149
+ },
150
+ "description": {
151
+ "type": "string",
152
+ "description": "Human-readable column description"
153
+ },
154
+ "maxLen": {
155
+ "type": "integer",
156
+ "description": "Maximum length for varchar/text columns"
157
+ },
158
+ "precision": {
159
+ "type": "integer",
160
+ "description": "Numeric precision for decimal/currency columns"
161
+ },
162
+ "editMask": {
163
+ "type": "string",
164
+ "description": "Input mask pattern for editing"
165
+ },
166
+ "displayFmt": {
167
+ "type": "string",
168
+ "description": "Display format pattern"
169
+ },
170
+ "params": {
171
+ "type": "object",
172
+ "description": "Field-type-specific configuration (e.g. dropdown options, currency settings)",
173
+ "properties": {
174
+ "options": {
175
+ "type": "array",
176
+ "items": {
177
+ "oneOf": [
178
+ { "type": "string" },
179
+ {
180
+ "type": "object",
181
+ "required": ["value", "label"],
182
+ "properties": {
183
+ "value": { "type": "string" },
184
+ "label": { "type": "string" },
185
+ "icon": { "type": "string" },
186
+ "color": { "type": "string" }
187
+ },
188
+ "additionalProperties": false
189
+ }
190
+ ]
191
+ },
192
+ "description": "Options for dropdown/flags field types"
193
+ }
194
+ }
195
+ },
196
+ "link": {
197
+ "$ref": "#/$defs/fieldLink",
198
+ "description": "Relationship link for Reference (R) and Set (S) columns"
199
+ },
200
+ "refFilter": {
201
+ "description": "Filter criteria for reference lookups"
202
+ }
203
+ },
204
+ "oneOf": [
205
+ {
206
+ "title": "Data column",
207
+ "description": "Physical DB column (columnType 'D' or unspecified). Requires 'dbDatatype'. Must NOT have 'link'.",
208
+ "required": ["dbDatatype"],
209
+ "properties": {
210
+ "columnType": { "enum": ["D"] },
211
+ "link": false
212
+ }
213
+ },
214
+ {
215
+ "title": "Inferred-type column",
216
+ "description": "Physical column where dbDatatype is inferred from fieldTypeCd (e.g. user, color)",
217
+ "required": ["fieldTypeCd"],
218
+ "properties": {
219
+ "columnType": { "enum": ["D"] },
220
+ "link": false,
221
+ "dbDatatype": false
222
+ }
223
+ },
224
+ {
225
+ "title": "Reference column (R)",
226
+ "description": "N:1 lookup (columnType 'R'). Requires 'link' and 'fieldTypeCd'. Must NOT have 'dbDatatype'.",
227
+ "required": ["columnType", "fieldTypeCd", "link"],
228
+ "properties": {
229
+ "columnType": { "const": "R" },
230
+ "dbDatatype": false
231
+ }
232
+ },
233
+ {
234
+ "title": "Set column (S)",
235
+ "description": "1:N collection (columnType 'S'). Requires 'link' and 'fieldTypeCd'. Must NOT have 'dbDatatype'.",
236
+ "required": ["columnType", "fieldTypeCd", "link"],
237
+ "properties": {
238
+ "columnType": { "const": "S" },
239
+ "dbDatatype": false
240
+ }
241
+ },
242
+ {
243
+ "title": "Formula column (F)",
244
+ "description": "Calculated virtual column (columnType 'F'). Requires 'formula'. Must NOT have 'link' or 'dbDatatype'.",
245
+ "required": ["columnType", "formula", "baseDatatypeCd"],
246
+ "properties": {
247
+ "columnType": { "const": "F" },
248
+ "link": false,
249
+ "baseDatatypeCd": true,
250
+ "dbDatatype": false
251
+ }
252
+ },
253
+ {
254
+ "title": "Accumulation registry column (A)",
255
+ "description": "Accumulation registry state (bool) + config. Physical column storing posted state. Config in params defines balance entity, dimensions, resources.",
256
+ "required": ["columnType", "dbDatatype", "params"],
257
+ "properties": {
258
+ "columnType": { "const": "A" },
259
+ "dbDatatype": { "const": "bool" },
260
+ "link": false
261
+ }
262
+ },
263
+ {
264
+ "title": "Ledger registry column (L)",
265
+ "description": "Ledger registry state (bool) + config. Physical column storing posted state. Config in params defines lines, movement entity, balance entity.",
266
+ "required": ["columnType", "dbDatatype", "params"],
267
+ "properties": {
268
+ "columnType": { "const": "L" },
269
+ "dbDatatype": { "const": "bool" },
270
+ "link": false
271
+ }
272
+ },
273
+ {
274
+ "title": "Generated/Computed column (G)",
275
+ "description": "Physical column auto-computed by DB trigger (aggregate over child set) or PostgreSQL GENERATED ALWAYS AS (same-row expression). Requires 'dbDatatype' and 'formula'. Formula syntax: SUM([setCol].[childField]) for aggregates, or plain SQL expression for same-row generated columns.",
276
+ "required": ["columnType", "dbDatatype", "formula"],
277
+ "properties": {
278
+ "columnType": { "const": "G" },
279
+ "link": false
280
+ }
281
+ }
282
+ ],
283
+ "required": ["orderNum", "description"],
284
+ "additionalProperties": false
285
+ },
286
+ "fieldLink": {
287
+ "type": "object",
288
+ "description": "Relationship definition for Reference and Set columns",
289
+ "required": ["entity", "thisKey", "otherKey"],
290
+ "properties": {
291
+ "entity": {
292
+ "type": "string",
293
+ "description": "Target entity code (the entity being referenced or collected)"
294
+ },
295
+ "thisKey": {
296
+ "type": "string",
297
+ "description": "Column(s) in this entity (comma-separated for compound keys)"
298
+ },
299
+ "otherKey": {
300
+ "type": "string",
301
+ "description": "Column(s) in the target entity (comma-separated for compound keys)"
302
+ }
303
+ },
304
+ "additionalProperties": false
305
+ },
306
+ "numberSequence": {
307
+ "type": "object",
308
+ "description": "Automatic document numbering. Auto-generates a formatted number on INSERT when the target column is empty.",
309
+ "required": ["column", "defaultPrefix"],
310
+ "properties": {
311
+ "column": {
312
+ "type": "string",
313
+ "description": "Column code that receives the auto-generated number"
314
+ },
315
+ "prefixSettingCd": {
316
+ "type": "string",
317
+ "description": "Module setting code to resolve the prefix from (folder-scoped inheritance)"
318
+ },
319
+ "defaultPrefix": {
320
+ "type": "string",
321
+ "description": "Fallback prefix when no setting is configured"
322
+ },
323
+ "pattern": {
324
+ "type": "string",
325
+ "description": "Number format pattern. Placeholders: {prefix}, {yyyy}, {yy}, {mm}, {dd}, {seq:N}",
326
+ "default": "{prefix}{yyyy}-{seq:3}"
327
+ },
328
+ "resetPeriod": {
329
+ "type": "string",
330
+ "enum": ["year", "month", "day", "never"],
331
+ "description": "Counter reset period",
332
+ "default": "year"
333
+ }
334
+ },
335
+ "additionalProperties": false
336
+ },
337
+ "periodConfig": {
338
+ "type": "object",
339
+ "description": "Configuration for entities with the 'period' trait. Defines how date values map to period keys.",
340
+ "required": ["keyFormat"],
341
+ "properties": {
342
+ "keyFormat": {
343
+ "type": "string",
344
+ "description": "Format string for deriving period key from a date. Placeholders: {yyyy}, {yy}, {MM}, {dd}, {q} (quarter 1-4).",
345
+ "examples": ["{yyyy}-{MM}", "{yyyy}-Q{q}", "FY{yyyy}"]
346
+ }
347
+ },
348
+ "additionalProperties": false
349
+ },
350
+ "accumulationConfig": {
351
+ "type": "object",
352
+ "description": "Configuration for simple accumulation (document without lines, e.g. receipt → stock).",
353
+ "required": ["balanceEntity", "dimensions", "resources"],
354
+ "properties": {
355
+ "balanceEntity": { "type": "string", "description": "Balance entity code (e.g. 'stock')" },
356
+ "dimensions": {
357
+ "description": "Balance row lookup dimensions. Array of field names (same name on both entities) or object mapping balance_field → document_field.",
358
+ "oneOf": [
359
+ { "type": "array", "items": { "type": "string" } },
360
+ { "type": "object", "additionalProperties": { "type": "string" } }
361
+ ]
362
+ },
363
+ "resources": {
364
+ "type": "object",
365
+ "description": "Document field → balance field resource mappings",
366
+ "additionalProperties": {
367
+ "type": "object",
368
+ "required": ["balanceField"],
369
+ "properties": {
370
+ "balanceField": { "type": "string" }
371
+ }
372
+ }
373
+ },
374
+ "direction": { "type": "string", "enum": ["increase", "decrease"], "description": "Fixed direction for all postings" },
375
+ "sign": { "$ref": "#/$defs/signConfig", "description": "Dynamic sign based on a field value (alternative to direction)" },
376
+ "autoCreateBalance": { "type": "boolean", "default": true },
377
+ "allowNegative": { "type": "boolean", "default": true },
378
+ "lockedFields": { "type": "array", "items": { "type": "string" }, "description": "Document fields locked when posted" }
379
+ },
380
+ "additionalProperties": false
381
+ },
382
+ "signConfig": {
383
+ "type": "object",
384
+ "description": "Dynamic sign determination based on a field value",
385
+ "required": ["field", "positive", "negative"],
386
+ "properties": {
387
+ "field": { "type": "string", "description": "Field code that determines direction" },
388
+ "positive": { "type": "array", "items": { "type": "string" }, "description": "Values that mean increase" },
389
+ "negative": { "type": "array", "items": { "type": "string" }, "description": "Values that mean decrease" }
390
+ },
391
+ "additionalProperties": false
392
+ },
393
+ "ledgerRegistryEntry": {
394
+ "type": "object",
395
+ "description": "Single registry target within an L-column's registries array",
396
+ "required": ["movementEntity", "movementMapping", "balanceEntity", "balanceMapping"],
397
+ "properties": {
398
+ "movementEntity": { "type": "string", "description": "Movement entity code (e.g. 'posting_record')" },
399
+ "movementMapping": {
400
+ "type": "object",
401
+ "description": "Line field → movement field mapping",
402
+ "additionalProperties": { "type": "string" }
403
+ },
404
+ "balanceEntity": { "type": "string", "description": "Balance entity code (e.g. 'balance_register')" },
405
+ "balanceMapping": {
406
+ "type": "object",
407
+ "required": ["dimensions", "debitResource", "creditResource"],
408
+ "properties": {
409
+ "dimensions": { "type": "array", "items": { "type": "string" }, "description": "Balance row dimension columns" },
410
+ "debitResource": { "type": "string", "description": "Balance column for debit turnover" },
411
+ "creditResource": { "type": "string", "description": "Balance column for credit turnover" }
412
+ },
413
+ "additionalProperties": false
414
+ }
415
+ },
416
+ "additionalProperties": false
417
+ },
418
+ "entityReference": {
419
+ "type": "object",
420
+ "description": "Foreign key reference definition (legacy format)",
421
+ "required": ["from", "to"],
422
+ "properties": {
423
+ "from": {
424
+ "type": "object",
425
+ "required": ["field"],
426
+ "properties": {
427
+ "field": { "type": "string" }
428
+ },
429
+ "additionalProperties": false
430
+ },
431
+ "to": {
432
+ "type": "object",
433
+ "required": ["entity", "field"],
434
+ "properties": {
435
+ "entity": { "type": "string" },
436
+ "field": { "type": "string" }
437
+ },
438
+ "additionalProperties": false
439
+ }
440
+ },
441
+ "additionalProperties": false
442
+ }
443
+ }
444
+ }
@@ -0,0 +1,100 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://dforge.dev/schemas/manifest.schema.json",
4
+ "title": "dForge Module Manifest",
5
+ "description": "Module package manifest defining metadata, dependencies, and entity references",
6
+ "type": "object",
7
+ "required": ["packageFormat", "moduleId", "code", "version", "dbSchemaVersion", "displayName"],
8
+ "properties": {
9
+ "packageFormat": {
10
+ "type": "integer",
11
+ "minimum": 1,
12
+ "description": "Package format version (currently 1)"
13
+ },
14
+ "moduleId": {
15
+ "type": "string",
16
+ "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
17
+ "description": "Globally unique module UUID"
18
+ },
19
+ "code": {
20
+ "type": "string",
21
+ "pattern": "^[a-z][a-z0-9_-]*$",
22
+ "description": "Human-readable module code (becomes schema name and module_cd in DB)"
23
+ },
24
+ "version": {
25
+ "type": "string",
26
+ "pattern": "^\\d+\\.\\d+\\.\\d+",
27
+ "description": "Module version (semver)"
28
+ },
29
+ "dbSchemaVersion": {
30
+ "type": "string",
31
+ "pattern": "^\\d+\\.\\d+\\.\\d+",
32
+ "description": "Database schema version (semver)"
33
+ },
34
+ "displayName": {
35
+ "type": "string",
36
+ "description": "Human-readable module name"
37
+ },
38
+ "description": {
39
+ "type": "string",
40
+ "description": "Module description"
41
+ },
42
+ "schemaName": {
43
+ "type": "string",
44
+ "description": "Database schema name override (defaults to module code, system modules use 'dForge')"
45
+ },
46
+ "system": {
47
+ "type": "boolean",
48
+ "description": "System module flag (cannot be uninstalled, admin-only). Omit for regular modules."
49
+ },
50
+ "author": {
51
+ "type": "object",
52
+ "properties": {
53
+ "name": {
54
+ "type": "string",
55
+ "description": "Author name"
56
+ },
57
+ "url": {
58
+ "type": "string",
59
+ "description": "Author URL"
60
+ }
61
+ },
62
+ "required": ["name"],
63
+ "additionalProperties": false
64
+ },
65
+ "license": {
66
+ "type": "string",
67
+ "description": "License identifier (e.g. 'proprietary', 'MIT')"
68
+ },
69
+ "dependencies": {
70
+ "type": "object",
71
+ "description": "Module dependencies: module code → semver range",
72
+ "additionalProperties": {
73
+ "type": "string",
74
+ "description": "Semver version range (e.g. '>=1.0.0')"
75
+ }
76
+ },
77
+ "entities": {
78
+ "type": "object",
79
+ "description": "Entity definitions and extensions: entity code → relative path to entity JSON file. Dotted keys (e.g. 'fin.invoice') indicate extensions of other modules' entities (the entity file must have an 'extends' property).",
80
+ "additionalProperties": {
81
+ "type": "string",
82
+ "pattern": "^(\\./)?entities/.+\\.json$"
83
+ }
84
+ },
85
+ "category": {
86
+ "type": "string",
87
+ "description": "Module category for display (e.g. 'Integration', 'Finance')"
88
+ },
89
+ "icon": {
90
+ "type": "string",
91
+ "description": "Bootstrap icon class (e.g. 'bi-link-45deg')"
92
+ },
93
+ "supportedLocales": {
94
+ "type": "array",
95
+ "items": { "type": "string", "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" },
96
+ "description": "Non-English locales the package promises to translate. Each entry MUST match a translations/<locale>.json file, and every translatable resource (entity/field labels, folders, views, menus + items, actions + params, reports + datasets + params, settings) MUST have a label in that file. English is the default — don't list 'en' or 'en-US'."
97
+ }
98
+ },
99
+ "additionalProperties": false
100
+ }
@@ -0,0 +1,107 @@
1
+ ---
2
+ name: dforge-mcp-author
3
+ description: Author dForge modules using the dforge-mcp tool surface. Use when the user has the @dforge-core/dforge-mcp MCP server connected and asks to scaffold, extend, pack, or install a dForge module. Replaces hand-writing JSON files with structured tool calls that produce schema-valid output on the first try.
4
+ ---
5
+
6
+ # dForge Module Author (MCP-driven)
7
+
8
+ You're authoring a dForge module via the `dforge-mcp` MCP server. dForge is a metadata-driven, multi-tenant business platform — modules are JSON metadata + DSL scripts.
9
+
10
+ **Your edge here** is that you have *tools*, not just text generation. Always prefer a tool call over writing a file by hand. Every tool either returns a file map (so the user can preview before commit) or shells out to the validated native CLI.
11
+
12
+ ---
13
+
14
+ ## Tool surface
15
+
16
+ | Tool | What it does | Returns |
17
+ |---|---|---|
18
+ | `dforge_module_create` | Build a brand-new module's file map | `{ files: {...} }` — client writes |
19
+ | `dforge_entity_add` | Add an entity to an existing module | `{ files: {...}, warning?: ... }` — client writes |
20
+ | `dforge_module_pack` | Pack a module dir into a `.dforge` tarball | `{ tarballPath, sizeBytes }` (writes the tarball) |
21
+ | `dforge_module_install` | Install a module to a tenant | `{ ok, output }` (live action) |
22
+ | `dforge_dbml_import` | Generate from DBML (stub) | not yet implemented |
23
+
24
+ ## Resources
25
+
26
+ Read these *before* generating any module content so your output matches the canonical schema:
27
+
28
+ - `dforge://schema/manifest` — manifest.json shape (required fields, patterns)
29
+ - `dforge://schema/entity` — entity JSON shape (fields, traits, refs)
30
+ - `dforge://schema/data-view` — data view shape (viewType, dataSources)
31
+ - `dforge://docs/conventions` — naming, FK+Reference pattern, traits, security model
32
+
33
+ Load them when you start a session, not after a wrong guess.
34
+
35
+ ---
36
+
37
+ ## Standard workflow
38
+
39
+ ### 1. Gather requirements (one short turn)
40
+
41
+ Ask the user, in order:
42
+ 1. **What's the module for?** One sentence is enough.
43
+ 2. **What's the module code?** Lowercase, hyphen-or-underscore, e.g. `crm`, `pm`, `hr-admin`.
44
+ 3. **What entities does it own?** Rough list with one-line descriptions each.
45
+ 4. **Greenfield or extending an existing module?** If extending, ask which.
46
+
47
+ Don't ask about field types, view layouts, or DSL actions yet — those come *after* a working skeleton.
48
+
49
+ ### 2. Read the schemas + conventions
50
+
51
+ Pull `dforge://schema/manifest`, `dforge://schema/entity`, and `dforge://docs/conventions` into context. Skim, don't memorise.
52
+
53
+ ### 3. Call `dforge_module_create`
54
+
55
+ Pass:
56
+ - `code`, `displayName`, `description` from the user's answers
57
+ - `entities`: array of `{ name, label, traits }` — default traits to `"identity+audit"`
58
+ - `preset`: `"minimal"` unless the user wants full template (settings/translations/seed)
59
+ - `dependencies`: usually `["admin", "metadata"]` — both are required for typical modules
60
+
61
+ You'll get back `{ summary, files: { "<relPath>": "<contents>", ... } }`.
62
+
63
+ ### 4. Preview the file map with the user
64
+
65
+ Show the file list (paths only) + the manifest contents. Ask "write these to `./<code>`?". Don't write without confirmation.
66
+
67
+ ### 5. Write the files
68
+
69
+ Use your filesystem tool (Write / bash heredoc / fs.writeFileSync, whatever your client offers). Each value in `files` is the literal file contents — including JSON indentation. Don't re-format.
70
+
71
+ ### 6. Iterate
72
+
73
+ Use `dforge_entity_add` to add more entities incrementally — it reads the existing manifest, regenerates the dependent UI/security files, and returns ONLY the files that change. Re-preview each time before writing.
74
+
75
+ For fields, ref columns, actions, formulas, settings, reports — write those directly into the entity JSON / new files under `logic/`, `ui/`, etc. The schemas are your guide; the tools don't (yet) cover field-level changes.
76
+
77
+ ### 7. Pack + install
78
+
79
+ Once the module is shaped right:
80
+ 1. `dforge_module_pack` with `moduleDir: "./<code>"` → returns the tarball path.
81
+ 2. (Optional) `dforge_module_install` with `pathOrTarball: <tarballPath or moduleDir>` and a `tenantUrl` + `token` (or rely on `DFORGE_URL`/`DFORGE_TOKEN` env). This runs the *full* server-side validator — the only validator available. Surface its output verbatim; if it fails, fix and re-pack.
82
+
83
+ ---
84
+
85
+ ## Hard rules
86
+
87
+ - **Always preview file maps before writing.** Tools return — the user decides.
88
+ - **Use `dforge_entity_add`, not regenerate-from-scratch.** It preserves the existing manifest's UUID, version, dependencies, etc.
89
+ - **Tabs in JSON.** All emitted files use `\t` indentation. Don't re-pretty-print with spaces.
90
+ - **Don't invent `code` or `moduleId`.** `code` comes from the user; `moduleId` is auto-generated by the tool (UUID). Never hand-write a UUID.
91
+ - **Refer to the conventions doc for FK+Reference, traits, flags.** The MCP server doesn't enforce these — your output does. The first install will catch violations, but a clean first install is the goal.
92
+
93
+ ---
94
+
95
+ ## When NOT to use the tools
96
+
97
+ - **Modifying a single existing field in an entity JSON.** Just edit the file.
98
+ - **Writing an action DSL script.** No tool for that — write to `logic/actions/<name>.dsl` directly. Reference the `dforge://docs/conventions` doc for DSL syntax.
99
+ - **Querying live tenant state.** No tool for that either — shell out to `dforge-cli` via your bash tool if the user has it installed.
100
+
101
+ ---
102
+
103
+ ## Sanity check before declaring done
104
+
105
+ - `dforge_module_pack` succeeded → archive size is non-trivial (>100 KB usually)
106
+ - `dforge_module_install --code <tenant>` exited 0 → server validated everything
107
+ - The user confirms the install log looks right (entities created, no warnings about missing translations / orphan rights)