worsoft-frontend-codegen-local-mcp 0.1.3 → 0.1.4
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
CHANGED
|
@@ -24,6 +24,7 @@ This MCP generates Worsoft frontend files from a local Markdown design document.
|
|
|
24
24
|
- `mainField` when `style=master_child_jump`: required
|
|
25
25
|
- `childField` when `style=master_child_jump`: required
|
|
26
26
|
- `relationType` when `style=master_child_jump`: optional
|
|
27
|
+
- `children` when `style=master_child_jump`: optional array of direct child relations; when provided it overrides the legacy single-child fields
|
|
27
28
|
|
|
28
29
|
## Recommended MCP Arguments
|
|
29
30
|
|
|
@@ -57,6 +58,33 @@ Master-child:
|
|
|
57
58
|
}
|
|
58
59
|
```
|
|
59
60
|
|
|
61
|
+
Multi direct children on one main form:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"tableName": "iwm_sys_trade_level",
|
|
66
|
+
"style": "master_child_jump",
|
|
67
|
+
"children": [
|
|
68
|
+
{
|
|
69
|
+
"childTableName": "iwm_sys_trade_level_standard",
|
|
70
|
+
"mainField": "id",
|
|
71
|
+
"childField": "trade_level_id",
|
|
72
|
+
"relationType": "1:N"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"childTableName": "iwm_sys_trade_level_competency",
|
|
76
|
+
"mainField": "id",
|
|
77
|
+
"childField": "trade_level_id",
|
|
78
|
+
"relationType": "1:N"
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
"designFile": "plugins/sql/SQL 璁捐璇存槑.md",
|
|
82
|
+
"frontendPath": "E:/own-worker-platform/trunk/worsoft-ui",
|
|
83
|
+
"moduleName": "admin/test",
|
|
84
|
+
"writeToDisk": false
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
60
88
|
## Smoke Test
|
|
61
89
|
|
|
62
90
|
PowerShell:
|
|
@@ -65,6 +93,7 @@ PowerShell:
|
|
|
65
93
|
powershell -ExecutionPolicy Bypass -File plugins/worsoft-codegen-local/smoke_test_mcp.ps1 -Scenario single
|
|
66
94
|
powershell -ExecutionPolicy Bypass -File plugins/worsoft-codegen-local/smoke_test_mcp.ps1 -Scenario master
|
|
67
95
|
powershell -ExecutionPolicy Bypass -File plugins/worsoft-codegen-local/smoke_test_mcp.ps1 -Scenario child
|
|
96
|
+
powershell -ExecutionPolicy Bypass -File plugins/worsoft-codegen-local/smoke_test_mcp.ps1 -Scenario multi
|
|
68
97
|
powershell -ExecutionPolicy Bypass -File plugins/worsoft-codegen-local/smoke_test_mcp.ps1 -Scenario missing_relation
|
|
69
98
|
```
|
|
70
99
|
|
|
@@ -74,6 +103,7 @@ Node:
|
|
|
74
103
|
node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario single
|
|
75
104
|
node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario master
|
|
76
105
|
node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario child
|
|
106
|
+
node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario multi
|
|
77
107
|
node plugins/worsoft-codegen-local/smoke_test_mcp.js --scenario missing_relation
|
|
78
108
|
```
|
|
79
109
|
|
|
@@ -95,9 +125,8 @@ For `master_child_jump`, MCP does not infer relations from the design file anymo
|
|
|
95
125
|
|
|
96
126
|
The caller must provide:
|
|
97
127
|
|
|
98
|
-
- `
|
|
99
|
-
- `mainField`
|
|
100
|
-
- `childField`
|
|
128
|
+
- either `children[]`
|
|
129
|
+
- or the legacy single-child fields: `childTableName`, `mainField`, `childField`
|
|
101
130
|
|
|
102
131
|
If these fields are missing, MCP returns `relation_input_required`.
|
|
103
132
|
|
|
@@ -40,10 +40,10 @@ export function putObj(obj?: object) {
|
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export function delChildObj(ids?: object) {
|
|
43
|
+
export function delChildObj(ids?: object, childTableName?: string) {
|
|
44
44
|
return request({
|
|
45
45
|
url: '/{{API_PATH}}/child',
|
|
46
46
|
method: 'delete',
|
|
47
|
-
data: ids,
|
|
47
|
+
data: childTableName ? { ids, childTableName } : ids,
|
|
48
48
|
});
|
|
49
49
|
}
|
|
@@ -9,9 +9,7 @@
|
|
|
9
9
|
{{FORM_FIELDS}}
|
|
10
10
|
</el-row>
|
|
11
11
|
<el-row :gutter="24">
|
|
12
|
-
|
|
13
|
-
{{CHILD_TABLE_COLUMNS}}
|
|
14
|
-
</sc-form-table>
|
|
12
|
+
{{CHILD_SECTIONS}}
|
|
15
13
|
</el-row>
|
|
16
14
|
</el-form>
|
|
17
15
|
<div class="dialog-footer" style="text-align: right; margin-top: 18px;">
|
|
@@ -38,12 +36,10 @@ const detail = ref(false);
|
|
|
38
36
|
|
|
39
37
|
const form = reactive({
|
|
40
38
|
{{FORM_DEFAULTS}}
|
|
41
|
-
|
|
39
|
+
{{CHILD_FORM_LIST_DEFAULTS}}
|
|
42
40
|
});
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
{{CHILD_TEMP_DEFAULTS}}
|
|
46
|
-
});
|
|
42
|
+
{{CHILD_TEMP_DECLARATIONS}}
|
|
47
43
|
|
|
48
44
|
const dataRules = ref({
|
|
49
45
|
{{FORM_RULES}}
|
|
@@ -64,11 +60,11 @@ const get{{CLASS_NAME}}Data = async (id: string) => {
|
|
|
64
60
|
const resetFormState = () => {
|
|
65
61
|
Object.assign(form, {
|
|
66
62
|
{{FORM_DEFAULTS}}
|
|
67
|
-
|
|
63
|
+
{{CHILD_FORM_LIST_DEFAULTS}}
|
|
68
64
|
});
|
|
69
65
|
nextTick(() => {
|
|
70
66
|
dataFormRef.value?.resetFields();
|
|
71
|
-
|
|
67
|
+
{{CHILD_RESET_LISTS}}
|
|
72
68
|
});
|
|
73
69
|
};
|
|
74
70
|
|
|
@@ -113,10 +109,10 @@ const onSubmit = async () => {
|
|
|
113
109
|
}
|
|
114
110
|
};
|
|
115
111
|
|
|
116
|
-
const deleteChild = async (obj:
|
|
117
|
-
if (obj
|
|
112
|
+
const deleteChild = async (obj: Record<string, any>, childPkAttr: string, childTableName?: string) => {
|
|
113
|
+
if (obj[childPkAttr]) {
|
|
118
114
|
try {
|
|
119
|
-
await delChildObj([obj
|
|
115
|
+
await delChildObj([obj[childPkAttr]], childTableName);
|
|
120
116
|
useMessage().success('删除成功');
|
|
121
117
|
} catch (err: any) {
|
|
122
118
|
useMessage().error(err.msg || '删除失败');
|
package/mcp_server.js
CHANGED
|
@@ -5,7 +5,7 @@ const fs = require('fs');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
|
|
7
7
|
const SERVER_NAME = 'worsoft-codegen-local';
|
|
8
|
-
const SERVER_VERSION = '0.1.
|
|
8
|
+
const SERVER_VERSION = '0.1.4';
|
|
9
9
|
const PROTOCOL_VERSION = '2024-11-05';
|
|
10
10
|
const TOOL_NAME = 'worsoft_codegen_local_generate_frontend';
|
|
11
11
|
const TEMPLATE_LIBRARY_ROOT = path.resolve(__dirname, '..', 'template');
|
|
@@ -19,6 +19,21 @@ const TOOL_SCHEMA = {
|
|
|
19
19
|
designFile: { type: 'string', description: 'Absolute or relative Markdown design file path. Defaults to ../sql/SQL 设计说明.md when omitted.' },
|
|
20
20
|
tableName: { type: 'string', description: 'Target main table name from the design file.' },
|
|
21
21
|
style: { type: 'string', enum: Object.keys(STYLE_CATALOG), description: 'Style id from assets/style-catalog.json.' },
|
|
22
|
+
children: {
|
|
23
|
+
type: 'array',
|
|
24
|
+
description: 'Optional direct child-table relations for master_child_jump. When provided, MCP renders all listed direct child tables on the same main form.',
|
|
25
|
+
items: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
childTableName: { type: 'string', description: 'Child table name.' },
|
|
29
|
+
mainField: { type: 'string', description: 'Main table relation field.' },
|
|
30
|
+
childField: { type: 'string', description: 'Child table relation field.' },
|
|
31
|
+
relationType: { type: 'string', description: 'Optional relation type label, for example 1:N.' },
|
|
32
|
+
},
|
|
33
|
+
required: ['childTableName', 'mainField', 'childField'],
|
|
34
|
+
additionalProperties: false,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
22
37
|
childTableName: { type: 'string', description: 'Child table name. Required when style=master_child_jump.' },
|
|
23
38
|
mainField: { type: 'string', description: 'Main table relation field. Required when style=master_child_jump.' },
|
|
24
39
|
childField: { type: 'string', description: 'Child table relation field. Required when style=master_child_jump.' },
|
|
@@ -474,6 +489,15 @@ function buildRetryArguments(safeArgs, sourceFile, childTableName) {
|
|
|
474
489
|
retryArguments.designFile = sourceFile;
|
|
475
490
|
}
|
|
476
491
|
|
|
492
|
+
if (safeArgs.children && safeArgs.children.length) {
|
|
493
|
+
retryArguments.children = safeArgs.children.map((relation) => ({
|
|
494
|
+
childTableName: relation.childTableName,
|
|
495
|
+
mainField: relation.mainField,
|
|
496
|
+
childField: relation.childField,
|
|
497
|
+
relationType: relation.relationType || '',
|
|
498
|
+
}));
|
|
499
|
+
}
|
|
500
|
+
|
|
477
501
|
if (childTableName) {
|
|
478
502
|
retryArguments.childTableName = childTableName;
|
|
479
503
|
}
|
|
@@ -536,13 +560,43 @@ function loadSourceDocument(safeArgs) {
|
|
|
536
560
|
};
|
|
537
561
|
}
|
|
538
562
|
|
|
539
|
-
function
|
|
563
|
+
function normalizeChildrenInput(inputChildren) {
|
|
564
|
+
if (inputChildren === undefined || inputChildren === null) {
|
|
565
|
+
return [];
|
|
566
|
+
}
|
|
567
|
+
if (!Array.isArray(inputChildren)) {
|
|
568
|
+
throw new Error('children must be an array');
|
|
569
|
+
}
|
|
570
|
+
return inputChildren.map((item, index) => {
|
|
571
|
+
if (!item || typeof item !== 'object') {
|
|
572
|
+
throw new Error('children[' + index + '] must be an object');
|
|
573
|
+
}
|
|
574
|
+
const childTableName = item.childTableName ? String(item.childTableName) : '';
|
|
575
|
+
const mainField = item.mainField ? String(item.mainField) : '';
|
|
576
|
+
const childField = item.childField ? String(item.childField) : '';
|
|
577
|
+
const relationType = item.relationType ? String(item.relationType) : '';
|
|
578
|
+
const missingFields = ['childTableName', 'mainField', 'childField'].filter((field) => !({ childTableName, mainField, childField })[field]);
|
|
579
|
+
if (missingFields.length) {
|
|
580
|
+
throw new Error('children[' + index + '] is missing required fields: ' + missingFields.join(', '));
|
|
581
|
+
}
|
|
582
|
+
return { childTableName, mainField, childField, relationType };
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function resolveMarkdownChildRelations(sourceDocument, safeArgs) {
|
|
587
|
+
if (safeArgs.children && safeArgs.children.length) {
|
|
588
|
+
return safeArgs.children.map((relation) => ({
|
|
589
|
+
mainTableName: safeArgs.tableName,
|
|
590
|
+
childTableName: relation.childTableName,
|
|
591
|
+
mainField: relation.mainField,
|
|
592
|
+
childField: relation.childField,
|
|
593
|
+
relationType: relation.relationType || '',
|
|
594
|
+
}));
|
|
595
|
+
}
|
|
596
|
+
|
|
540
597
|
const missingFields = ['childTableName', 'mainField', 'childField'].filter((field) => !safeArgs[field]);
|
|
541
598
|
if (missingFields.length) {
|
|
542
|
-
const message =
|
|
543
|
-
'master_child_jump requires externally provided relation fields. Missing: ' +
|
|
544
|
-
missingFields.join(', ') +
|
|
545
|
-
'.';
|
|
599
|
+
const message = 'master_child_jump requires either children[] or legacy relation fields. Missing: ' + missingFields.join(', ') + '.';
|
|
546
600
|
const error = new Error(message);
|
|
547
601
|
error.details = {
|
|
548
602
|
type: 'relation_input_required',
|
|
@@ -550,11 +604,19 @@ function resolveMarkdownChildRelation(sourceDocument, safeArgs) {
|
|
|
550
604
|
tableName: safeArgs.tableName,
|
|
551
605
|
designFile: sourceDocument.path,
|
|
552
606
|
message,
|
|
553
|
-
requiredFields: ['childTableName
|
|
607
|
+
requiredFields: ['children[] or childTableName/mainField/childField'],
|
|
554
608
|
correctionEntry: {
|
|
555
|
-
fields: ['childTableName', 'mainField', 'childField'],
|
|
609
|
+
fields: ['children', 'childTableName', 'mainField', 'childField'],
|
|
556
610
|
example: {
|
|
557
611
|
...buildRetryArguments(safeArgs, sourceDocument.path, safeArgs.childTableName || '<child_table_name>'),
|
|
612
|
+
children: [
|
|
613
|
+
{
|
|
614
|
+
childTableName: safeArgs.childTableName || '<child_table_name>',
|
|
615
|
+
mainField: safeArgs.mainField || '<main_field>',
|
|
616
|
+
childField: safeArgs.childField || '<child_field>',
|
|
617
|
+
relationType: safeArgs.relationType || '1:N',
|
|
618
|
+
},
|
|
619
|
+
],
|
|
558
620
|
mainField: safeArgs.mainField || '<main_field>',
|
|
559
621
|
childField: safeArgs.childField || '<child_field>',
|
|
560
622
|
},
|
|
@@ -563,13 +625,15 @@ function resolveMarkdownChildRelation(sourceDocument, safeArgs) {
|
|
|
563
625
|
throw error;
|
|
564
626
|
}
|
|
565
627
|
|
|
566
|
-
return
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
628
|
+
return [
|
|
629
|
+
{
|
|
630
|
+
mainTableName: safeArgs.tableName,
|
|
631
|
+
childTableName: safeArgs.childTableName,
|
|
632
|
+
mainField: safeArgs.mainField,
|
|
633
|
+
childField: safeArgs.childField,
|
|
634
|
+
relationType: safeArgs.relationType || '',
|
|
635
|
+
},
|
|
636
|
+
];
|
|
573
637
|
}
|
|
574
638
|
|
|
575
639
|
function getStylePreset(styleId) {
|
|
@@ -601,31 +665,33 @@ function ensureFieldExists(fields, fieldName, tableName, role) {
|
|
|
601
665
|
return field;
|
|
602
666
|
}
|
|
603
667
|
|
|
604
|
-
function
|
|
605
|
-
if (safeArgs.style !== 'master_child_jump') return
|
|
606
|
-
|
|
607
|
-
const
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
668
|
+
function buildChildModels(sourceDocument, safeArgs, mainParsed) {
|
|
669
|
+
if (safeArgs.style !== 'master_child_jump') return [];
|
|
670
|
+
|
|
671
|
+
const relations = resolveMarkdownChildRelations(sourceDocument, safeArgs);
|
|
672
|
+
return relations.map((relation) => {
|
|
673
|
+
const childParsed = parseTableFromMarkdownDesign(sourceDocument.designDoc, relation.childTableName);
|
|
674
|
+
const childFields = normalizeFields(childParsed);
|
|
675
|
+
const mainRelationField = ensureFieldExists(mainParsed.fields, relation.mainField, safeArgs.tableName, 'Main relation');
|
|
676
|
+
const childRelationField = ensureFieldExists(childParsed.fields, relation.childField, relation.childTableName, 'Child relation');
|
|
677
|
+
const childVisibleFields = childFields.filter(
|
|
678
|
+
(field) => field.fieldName !== childParsed.pkField.fieldName && !field.isAudit && field.fieldName !== relation.childField
|
|
679
|
+
);
|
|
680
|
+
|
|
681
|
+
return {
|
|
682
|
+
tableName: relation.childTableName,
|
|
683
|
+
tableComment: childParsed.tableComment,
|
|
684
|
+
className: toPascalCase(relation.childTableName),
|
|
685
|
+
functionName: toCamelCase(relation.childTableName),
|
|
686
|
+
listName: toCamelCase(relation.childTableName) + 'List',
|
|
687
|
+
pk: childParsed.pkField,
|
|
688
|
+
fields: childFields,
|
|
689
|
+
visibleFields: childVisibleFields,
|
|
690
|
+
mainField: mainRelationField,
|
|
691
|
+
childField: childRelationField,
|
|
692
|
+
relationType: relation.relationType,
|
|
693
|
+
};
|
|
694
|
+
});
|
|
629
695
|
}
|
|
630
696
|
|
|
631
697
|
function buildModel(safeArgs) {
|
|
@@ -634,8 +700,8 @@ function buildModel(safeArgs) {
|
|
|
634
700
|
const fields = normalizeFields(mainParsed);
|
|
635
701
|
const visibleFields = fields.filter((field) => field.fieldName !== mainParsed.pkField.fieldName && !field.isAudit);
|
|
636
702
|
const gridFields = visibleFields.slice(0, 8);
|
|
637
|
-
const
|
|
638
|
-
const childDictTypes = child
|
|
703
|
+
const children = buildChildModels(sourceDocument, safeArgs, mainParsed);
|
|
704
|
+
const childDictTypes = children.flatMap((child) => child.visibleFields.map((field) => field.dictType).filter(Boolean));
|
|
639
705
|
const dictTypes = [...new Set([...visibleFields.map((field) => field.dictType).filter(Boolean), ...childDictTypes])];
|
|
640
706
|
|
|
641
707
|
return {
|
|
@@ -653,7 +719,7 @@ function buildModel(safeArgs) {
|
|
|
653
719
|
dictTypes,
|
|
654
720
|
frontendPath: path.resolve(safeArgs.frontendPath),
|
|
655
721
|
style: safeArgs.style,
|
|
656
|
-
|
|
722
|
+
children,
|
|
657
723
|
};
|
|
658
724
|
}
|
|
659
725
|
|
|
@@ -792,6 +858,50 @@ function renderChildTempDefaults(childModel) {
|
|
|
792
858
|
return childModel.fields.map(renderDefaultLine).join('\n');
|
|
793
859
|
}
|
|
794
860
|
|
|
861
|
+
function renderChildListDefaultLine(childModel) {
|
|
862
|
+
return ` ${childModel.listName}: [],`;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
function renderChildTempDeclaration(childModel) {
|
|
866
|
+
return [
|
|
867
|
+
`const childTemp${childModel.className} = reactive({`,
|
|
868
|
+
renderChildTempDefaults(childModel),
|
|
869
|
+
'});',
|
|
870
|
+
].join('\n');
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
function renderChildSection(childModel, childCount) {
|
|
874
|
+
const title = childModel.tableComment.replace(/'/g, "\\'");
|
|
875
|
+
const deleteExpression =
|
|
876
|
+
childCount > 1
|
|
877
|
+
? `deleteChild(obj, '${childModel.pk.attrName}', '${childModel.tableName}')`
|
|
878
|
+
: `deleteChild(obj, '${childModel.pk.attrName}')`;
|
|
879
|
+
|
|
880
|
+
return [
|
|
881
|
+
' <el-col :span="24" class="mb20">',
|
|
882
|
+
` <div class="mb10" style="font-weight: 600;">${title}</div>`,
|
|
883
|
+
` <sc-form-table v-model="form.${childModel.listName}" :addTemplate="childTemp${childModel.className}" @delete="(obj) => ${deleteExpression}" placeholder="暂无数据">`,
|
|
884
|
+
childModel.visibleFields.map((field) => renderChildTableColumn(field, childModel.listName)).join('\n'),
|
|
885
|
+
' </sc-form-table>',
|
|
886
|
+
' </el-col>',
|
|
887
|
+
].join('\n');
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
function renderChildFormListDefaults(children) {
|
|
891
|
+
if (!children.length) return '';
|
|
892
|
+
return children.map(renderChildListDefaultLine).join('\n');
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function renderChildTempDeclarations(children) {
|
|
896
|
+
if (!children.length) return '';
|
|
897
|
+
return children.map(renderChildTempDeclaration).join('\n\n');
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
function renderChildResetListLines(children) {
|
|
901
|
+
if (!children.length) return '';
|
|
902
|
+
return children.map((childModel) => ` form.${childModel.listName} = [];`).join('\n');
|
|
903
|
+
}
|
|
904
|
+
|
|
795
905
|
function renderDictImportBlock(dictTypes) {
|
|
796
906
|
if (!dictTypes.length) return '';
|
|
797
907
|
return [
|
|
@@ -846,12 +956,10 @@ function buildReplacements(model) {
|
|
|
846
956
|
OPTIONS_FIELDS: model.visibleFields.map(renderOptionField).join('\n'),
|
|
847
957
|
FILTER_TYPES: model.visibleFields.map(renderFilterType).filter(Boolean).join('\n'),
|
|
848
958
|
FORM_RULES: renderFormRules(model.visibleFields),
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
CHILD_TABLE_COLUMNS: model.child ? model.child.visibleFields.map((field) => renderChildTableColumn(field, model.child.listName)).join('\n') : '',
|
|
854
|
-
CHILD_TEMP_DEFAULTS: model.child ? renderChildTempDefaults(model.child) : '',
|
|
959
|
+
CHILD_FORM_LIST_DEFAULTS: renderChildFormListDefaults(model.children),
|
|
960
|
+
CHILD_TEMP_DECLARATIONS: renderChildTempDeclarations(model.children),
|
|
961
|
+
CHILD_RESET_LISTS: renderChildResetListLines(model.children),
|
|
962
|
+
CHILD_SECTIONS: model.children.map((childModel) => renderChildSection(childModel, model.children.length)).join('\n'),
|
|
855
963
|
};
|
|
856
964
|
}
|
|
857
965
|
|
|
@@ -897,6 +1005,7 @@ function ensureArguments(input) {
|
|
|
897
1005
|
designFile: input.designFile ? String(input.designFile) : null,
|
|
898
1006
|
tableName: String(input.tableName),
|
|
899
1007
|
style,
|
|
1008
|
+
children: normalizeChildrenInput(input.children),
|
|
900
1009
|
childTableName: input.childTableName ? String(input.childTableName) : null,
|
|
901
1010
|
mainField: input.mainField ? String(input.mainField) : null,
|
|
902
1011
|
childField: input.childField ? String(input.childField) : null,
|
|
@@ -922,6 +1031,16 @@ function maybeWriteFiles(files, writeToDisk, overwrite) {
|
|
|
922
1031
|
}
|
|
923
1032
|
|
|
924
1033
|
function buildManifest(model, safeArgs, stylePreset, sharedTemplates, files, note) {
|
|
1034
|
+
const relations = model.children.map((childModel) => ({
|
|
1035
|
+
childTableName: childModel.tableName,
|
|
1036
|
+
childTableComment: childModel.tableComment,
|
|
1037
|
+
mainField: childModel.mainField.fieldName,
|
|
1038
|
+
childField: childModel.childField.fieldName,
|
|
1039
|
+
childListName: childModel.listName,
|
|
1040
|
+
relationType: childModel.relationType || '',
|
|
1041
|
+
}));
|
|
1042
|
+
const selectedList = relations.map((relation) => formatRelationCandidate(relation));
|
|
1043
|
+
|
|
925
1044
|
return {
|
|
926
1045
|
mode: 'local-template',
|
|
927
1046
|
style: safeArgs.style,
|
|
@@ -935,32 +1054,23 @@ function buildManifest(model, safeArgs, stylePreset, sharedTemplates, files, not
|
|
|
935
1054
|
writeToDisk: safeArgs.writeToDisk,
|
|
936
1055
|
selectedTemplates: sharedTemplates,
|
|
937
1056
|
files: files.map((file) => ({ type: file.type, path: file.path, bytes: Buffer.byteLength(file.content, 'utf8'), status: file.status || (safeArgs.writeToDisk ? 'success' : 'rendered') })),
|
|
938
|
-
relation:
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
childTableComment: model.child.tableComment,
|
|
942
|
-
mainField: model.child.mainField.fieldName,
|
|
943
|
-
childField: model.child.childField.fieldName,
|
|
944
|
-
childListName: model.child.listName,
|
|
945
|
-
relationType: model.child.relationType || '',
|
|
946
|
-
}
|
|
947
|
-
: null,
|
|
948
|
-
relationResolution: model.child
|
|
1057
|
+
relation: relations.length === 1 ? relations[0] : null,
|
|
1058
|
+
relations,
|
|
1059
|
+
relationResolution: model.children.length
|
|
949
1060
|
? {
|
|
950
1061
|
status: 'provided',
|
|
951
1062
|
tableName: safeArgs.tableName,
|
|
952
1063
|
designFile: model.designFile,
|
|
953
1064
|
source: 'arguments',
|
|
954
|
-
message:
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
}),
|
|
1065
|
+
message:
|
|
1066
|
+
model.children.length > 1
|
|
1067
|
+
? 'Direct child relations were provided by the caller. MCP skipped design-doc relation inference and generated a single main form with multiple child tables.'
|
|
1068
|
+
: 'The relation was provided by the caller. MCP skipped design-doc relation inference and generated files directly.',
|
|
1069
|
+
selected: selectedList.length === 1 ? selectedList[0] : null,
|
|
1070
|
+
selectedList,
|
|
961
1071
|
correctionEntry: {
|
|
962
|
-
fields: ['childTableName', 'mainField', 'childField'],
|
|
963
|
-
example: buildRetryArguments(safeArgs, model.designFile, model.
|
|
1072
|
+
fields: ['children', 'childTableName', 'mainField', 'childField'],
|
|
1073
|
+
example: buildRetryArguments(safeArgs, model.designFile, model.children[0].tableName),
|
|
964
1074
|
},
|
|
965
1075
|
}
|
|
966
1076
|
: null,
|
|
@@ -969,7 +1079,9 @@ function buildManifest(model, safeArgs, stylePreset, sharedTemplates, files, not
|
|
|
969
1079
|
visibleFields: model.visibleFields.length,
|
|
970
1080
|
dictFields: model.visibleFields.filter((field) => field.dictType).map((field) => field.attrName),
|
|
971
1081
|
skippedAuditFields: model.fields.filter((field) => field.isAudit).map((field) => field.fieldName),
|
|
972
|
-
|
|
1082
|
+
childCount: model.children.length,
|
|
1083
|
+
childTables: model.children.map((childModel) => childModel.tableName),
|
|
1084
|
+
childVisibleFields: model.children.reduce((sum, childModel) => sum + childModel.visibleFields.length, 0),
|
|
973
1085
|
},
|
|
974
1086
|
note,
|
|
975
1087
|
};
|
|
@@ -1012,7 +1124,7 @@ async function onMessage(message) {
|
|
|
1012
1124
|
}
|
|
1013
1125
|
|
|
1014
1126
|
if (method === 'tools/list') {
|
|
1015
|
-
writeMessage(successResponse(id, { tools: [{ name: TOOL_NAME, description: 'Generate Worsoft frontend files and menu SQL from a local Markdown design file with style-based local templates. In master_child_jump mode, the caller must provide
|
|
1127
|
+
writeMessage(successResponse(id, { tools: [{ name: TOOL_NAME, description: 'Generate Worsoft frontend files and menu SQL from a local Markdown design file with style-based local templates. In master_child_jump mode, the caller must provide either children[] or childTableName/mainField/childField.', inputSchema: TOOL_SCHEMA }] }));
|
|
1016
1128
|
return;
|
|
1017
1129
|
}
|
|
1018
1130
|
|
package/package.json
CHANGED