worsoft-frontend-codegen-local-mcp 0.1.32 → 0.1.35
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/mcp_server.js +127 -50
- package/package.json +1 -1
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.35';
|
|
9
9
|
const PROTOCOL_VERSION = '2024-11-05';
|
|
10
10
|
const TOOL_NAME = 'worsoft_codegen_local_generate_frontend';
|
|
11
11
|
const STYLE_CATALOG_PATH = path.join(__dirname, 'assets', 'style-catalog.json');
|
|
@@ -148,14 +148,19 @@ export function createCrudSchema(
|
|
|
148
148
|
const TOOL_SCHEMA = {
|
|
149
149
|
type: 'object',
|
|
150
150
|
properties: {
|
|
151
|
-
featureTitle: { type: 'string', description: 'Feature title from structured metadata.' },
|
|
152
|
-
tableName: { type: 'string', description: '
|
|
153
|
-
tableComment: { type: 'string', description: '
|
|
154
|
-
apiPath: { type: 'string', description: 'Backend API base path from structured metadata, for example iwmEmpOutsourcePerson.' },
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
description: 'Structured
|
|
151
|
+
featureTitle: { type: 'string', description: 'Feature title from pre-parsed structured metadata.' },
|
|
152
|
+
tableName: { type: 'string', description: 'Canonical main table name from PRD-aligned structured metadata.' },
|
|
153
|
+
tableComment: { type: 'string', description: 'Canonical main table comment or feature label from PRD-aligned structured metadata.' },
|
|
154
|
+
apiPath: { type: 'string', description: 'Backend API base path from pre-parsed structured metadata, for example iwmEmpOutsourcePerson.' },
|
|
155
|
+
pageType: {
|
|
156
|
+
type: 'string',
|
|
157
|
+
enum: ['feature', 'dictionary', 'non_standard', 'business', 'dict'],
|
|
158
|
+
description: 'Structured page type from parseResult. MCP consumes this value but does not derive it. Dictionary pages are restricted to dialog-based templates.',
|
|
159
|
+
},
|
|
160
|
+
style: { type: 'string', enum: Object.keys(STYLE_CATALOG), description: 'Final style id from parseResult or translated mcpPayload. MCP validates it but does not infer it.' },
|
|
161
|
+
fields: {
|
|
162
|
+
type: 'array',
|
|
163
|
+
description: 'Structured main-table field metadata already translated by the caller. MCP only consumes these low-level generation parameters.',
|
|
159
164
|
items: {
|
|
160
165
|
type: 'object',
|
|
161
166
|
properties: {
|
|
@@ -172,8 +177,10 @@ const TOOL_SCHEMA = {
|
|
|
172
177
|
queryType: { type: ['string', 'number'], description: 'Structured query type for list filtering, for example 20 for date ranges or 30 for dictionary filters.' },
|
|
173
178
|
dictType: { type: 'string', description: 'Dictionary type code from structured metadata.' },
|
|
174
179
|
defaultValue: { type: ['string', 'number', 'boolean'], description: 'Optional default value.' },
|
|
175
|
-
description: { type: 'string', description: 'Field description or notes.' },
|
|
176
|
-
|
|
180
|
+
description: { type: 'string', description: 'Field description or notes.' },
|
|
181
|
+
componentType: { type: 'string', description: 'Explicit component type from PRD structured metadata, for example text, textarea, select, number or datetime.' },
|
|
182
|
+
formType: { type: 'string', description: 'Explicit form control type translated by the caller. MCP consumes this value first and only falls back to legacy heuristics when omitted.' },
|
|
183
|
+
sourceKind: { type: 'string', enum: ['entity', 'display', 'virtual', 'common'], description: 'Field source kind.' },
|
|
177
184
|
primary: { type: ['boolean', 'string'], description: 'Whether the field is the primary key.' },
|
|
178
185
|
},
|
|
179
186
|
required: ['fieldName', 'label', 'type'],
|
|
@@ -182,7 +189,7 @@ const TOOL_SCHEMA = {
|
|
|
182
189
|
},
|
|
183
190
|
children: {
|
|
184
191
|
type: 'array',
|
|
185
|
-
description: 'Optional direct child-table structures for master_child_jump. When provided, MCP renders all listed direct child tables on the same main form.',
|
|
192
|
+
description: 'Optional direct child-table structures for master_child_jump. When provided, MCP renders all listed direct child tables on the same main form. The caller must determine semantic truth before passing them in.',
|
|
186
193
|
items: {
|
|
187
194
|
type: 'object',
|
|
188
195
|
properties: {
|
|
@@ -211,8 +218,10 @@ const TOOL_SCHEMA = {
|
|
|
211
218
|
queryType: { type: ['string', 'number'], description: 'Structured query type for list filtering, for example 20 for date ranges or 30 for dictionary filters.' },
|
|
212
219
|
dictType: { type: 'string', description: 'Dictionary type code from structured metadata.' },
|
|
213
220
|
defaultValue: { type: ['string', 'number', 'boolean'], description: 'Optional default value.' },
|
|
214
|
-
description: { type: 'string', description: 'Field description or notes.' },
|
|
215
|
-
|
|
221
|
+
description: { type: 'string', description: 'Field description or notes.' },
|
|
222
|
+
componentType: { type: 'string', description: 'Explicit component type from PRD structured metadata.' },
|
|
223
|
+
formType: { type: 'string', description: 'Explicit form control type translated by the caller.' },
|
|
224
|
+
sourceKind: { type: 'string', enum: ['entity', 'display', 'virtual', 'common'], description: 'Field source kind.' },
|
|
216
225
|
primary: { type: ['boolean', 'string'], description: 'Whether the field is the primary key.' },
|
|
217
226
|
},
|
|
218
227
|
required: ['fieldName', 'label', 'type'],
|
|
@@ -238,8 +247,8 @@ const TOOL_SCHEMA = {
|
|
|
238
247
|
items: {
|
|
239
248
|
type: 'object',
|
|
240
249
|
properties: {
|
|
241
|
-
tableName: { type: 'string', description: '
|
|
242
|
-
tableComment: { type: 'string', description: '
|
|
250
|
+
tableName: { type: 'string', description: 'Canonical module table name from PRD-aligned structured metadata.' },
|
|
251
|
+
tableComment: { type: 'string', description: 'Canonical module display label from PRD-aligned structured metadata.' },
|
|
243
252
|
apiPath: { type: 'string', description: 'Backend API base path for this module.' },
|
|
244
253
|
primaryKey: { type: 'string', description: 'Primary key field name. Defaults to id when omitted.' },
|
|
245
254
|
queryParentField: { type: 'string', description: 'Direct parent foreign key field used for page queries.' },
|
|
@@ -266,8 +275,10 @@ const TOOL_SCHEMA = {
|
|
|
266
275
|
queryType: { type: ['string', 'number'], description: 'Structured query type for list filtering, for example 20 for date ranges or 30 for dictionary filters.' },
|
|
267
276
|
dictType: { type: 'string', description: 'Dictionary type code from structured metadata.' },
|
|
268
277
|
defaultValue: { type: ['string', 'number', 'boolean'], description: 'Optional default value.' },
|
|
269
|
-
description: { type: 'string', description: 'Field description or notes.' },
|
|
270
|
-
|
|
278
|
+
description: { type: 'string', description: 'Field description or notes.' },
|
|
279
|
+
componentType: { type: 'string', description: 'Explicit component type from PRD structured metadata.' },
|
|
280
|
+
formType: { type: 'string', description: 'Explicit form control type translated by the caller.' },
|
|
281
|
+
sourceKind: { type: 'string', enum: ['entity', 'display', 'virtual', 'common'], description: 'Field source kind.' },
|
|
271
282
|
primary: { type: ['boolean', 'string'], description: 'Whether the field is the primary key.' }
|
|
272
283
|
},
|
|
273
284
|
required: ['fieldName', 'label', 'type'],
|
|
@@ -845,15 +856,34 @@ function findDictType(comment) {
|
|
|
845
856
|
return extractDictType(comment);
|
|
846
857
|
}
|
|
847
858
|
|
|
848
|
-
function mapFieldType(field) {
|
|
849
|
-
if (field.dictType) return 'select';
|
|
850
|
-
if (field.sqlType === 'DATETIME' || field.sqlType === 'TIMESTAMP') return 'datetime';
|
|
851
|
-
if (field.sqlType === 'DATE') return 'date';
|
|
852
|
-
if (['INT', 'BIGINT', 'DECIMAL', 'NUMERIC'].includes(field.sqlType)) return 'number';
|
|
853
|
-
if (field.sqlType === 'TEXT') return 'textarea';
|
|
854
|
-
if (field.sqlType === 'VARCHAR' && field.length && Number(field.length) > 64) return 'textarea';
|
|
855
|
-
return 'text';
|
|
856
|
-
}
|
|
859
|
+
function mapFieldType(field) {
|
|
860
|
+
if (field.dictType) return 'select';
|
|
861
|
+
if (field.sqlType === 'DATETIME' || field.sqlType === 'TIMESTAMP') return 'datetime';
|
|
862
|
+
if (field.sqlType === 'DATE') return 'date';
|
|
863
|
+
if (['INT', 'BIGINT', 'DECIMAL', 'NUMERIC'].includes(field.sqlType)) return 'number';
|
|
864
|
+
if (field.sqlType === 'TEXT') return 'textarea';
|
|
865
|
+
if (field.sqlType === 'VARCHAR' && field.length && Number(field.length) > 64) return 'textarea';
|
|
866
|
+
return 'text';
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
function normalizeStructuredFormType(value) {
|
|
870
|
+
const normalized = String(value || '').trim().toLowerCase();
|
|
871
|
+
if (!normalized) return '';
|
|
872
|
+
if (normalized === 'date') return 'date';
|
|
873
|
+
if (normalized === 'datetime') return 'datetime';
|
|
874
|
+
if (normalized === 'microme-operator') return 'number';
|
|
875
|
+
if (normalized === 'upload' || normalized === 'picker') {
|
|
876
|
+
throw new Error(
|
|
877
|
+
'Explicit component/form type "' +
|
|
878
|
+
normalized +
|
|
879
|
+
'" is not yet supported by worsoft-codegen-local templates. Please keep it in parseResult for downstream handling or extend MCP template support first.'
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
if (['text', 'select', 'textarea', 'number'].includes(normalized)) {
|
|
883
|
+
return normalized;
|
|
884
|
+
}
|
|
885
|
+
throw new Error('Unsupported explicit component/form type: ' + normalized);
|
|
886
|
+
}
|
|
857
887
|
|
|
858
888
|
function escapeForRegex(value) {
|
|
859
889
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
@@ -1008,6 +1038,7 @@ function normalizeStructuredField(inputField, index, contextLabel) {
|
|
|
1008
1038
|
}
|
|
1009
1039
|
|
|
1010
1040
|
const { length, scale } = normalizeStructuredLengthAndScale(inputField.length, inputField.scale);
|
|
1041
|
+
const explicitFormType = normalizeStructuredFormType(inputField.formType || inputField.componentType);
|
|
1011
1042
|
const explicitQueryType =
|
|
1012
1043
|
inputField.queryType === undefined || inputField.queryType === null || inputField.queryType === ''
|
|
1013
1044
|
? undefined
|
|
@@ -1023,6 +1054,7 @@ function normalizeStructuredField(inputField, index, contextLabel) {
|
|
|
1023
1054
|
comment: label,
|
|
1024
1055
|
label,
|
|
1025
1056
|
description: String(inputField.description || '').trim(),
|
|
1057
|
+
formType: explicitFormType,
|
|
1026
1058
|
dictType: normalizeDictType(inputField.dictType),
|
|
1027
1059
|
notNull: parseBooleanLike(inputField.required, false),
|
|
1028
1060
|
defaultValue: normalizeDefaultValue(inputField.defaultValue),
|
|
@@ -1207,19 +1239,56 @@ function normalizeLevelsInput(inputLevels) {
|
|
|
1207
1239
|
return levels;
|
|
1208
1240
|
}
|
|
1209
1241
|
|
|
1210
|
-
function getStylePreset(styleId) {
|
|
1211
|
-
const preset = STYLE_CATALOG[styleId];
|
|
1212
|
-
if (!preset) throw new Error('Unsupported style: ' + styleId);
|
|
1213
|
-
return preset;
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1242
|
+
function getStylePreset(styleId) {
|
|
1243
|
+
const preset = STYLE_CATALOG[styleId];
|
|
1244
|
+
if (!preset) throw new Error('Unsupported style: ' + styleId);
|
|
1245
|
+
return preset;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
function normalizePageTypeInput(pageType) {
|
|
1249
|
+
if (pageType === undefined || pageType === null || pageType === '') return '';
|
|
1250
|
+
const normalized = String(pageType).trim();
|
|
1251
|
+
if (normalized === 'dict' || normalized === 'business') {
|
|
1252
|
+
throw new Error(`Legacy pageType alias "${normalized}" is not allowed. Use canonical pageType "dictionary" or "feature" from parseResult.`);
|
|
1253
|
+
}
|
|
1254
|
+
if (['feature', 'dictionary', 'non_standard'].includes(normalized)) return normalized;
|
|
1255
|
+
throw new Error('Unsupported pageType: ' + normalized);
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
function rejectSemanticStageInputs(input) {
|
|
1259
|
+
const forbiddenKeys = [
|
|
1260
|
+
'parseResult',
|
|
1261
|
+
'prdFile',
|
|
1262
|
+
'apiDocFile',
|
|
1263
|
+
'fieldMappings',
|
|
1264
|
+
'dictionaryMeta',
|
|
1265
|
+
'listQueryMeta',
|
|
1266
|
+
'fieldUiMeta',
|
|
1267
|
+
];
|
|
1268
|
+
const present = forbiddenKeys.filter((key) => Object.prototype.hasOwnProperty.call(input, key));
|
|
1269
|
+
if (present.length) {
|
|
1270
|
+
throw new Error(`worsoft_codegen_local_generate_frontend only accepts translated low-level generation parameters. Unsupported semantic-stage keys: ${present.join(', ')}`);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
function validatePageTypeAndStyle(pageType, style) {
|
|
1275
|
+
if (!pageType) return;
|
|
1276
|
+
if (pageType === 'non_standard') {
|
|
1277
|
+
throw new Error('non_standard pages are not supported by worsoft_codegen_local_generate_frontend');
|
|
1278
|
+
}
|
|
1279
|
+
if (pageType !== 'dictionary') return;
|
|
1280
|
+
if (style === 'single_table_jump' || style === 'master_child_jump') {
|
|
1281
|
+
throw new Error(`Dictionary pages must use dialog-based styles. pageType=dictionary does not support style=${style}`);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1216
1285
|
function hasRuntimeSupport(stylePreset) {
|
|
1217
1286
|
return Boolean(stylePreset.runtime && stylePreset.runtime.supported && stylePreset.runtime.templateDir);
|
|
1218
1287
|
}
|
|
1219
1288
|
|
|
1220
|
-
function normalizeFields(parsed) {
|
|
1221
|
-
return parsed.fields.map((field) => ({ ...field, formType: mapFieldType(field), isAudit: isAuditField(field.fieldName) }));
|
|
1222
|
-
}
|
|
1289
|
+
function normalizeFields(parsed) {
|
|
1290
|
+
return parsed.fields.map((field) => ({ ...field, formType: field.formType || mapFieldType(field), isAudit: isAuditField(field.fieldName) }));
|
|
1291
|
+
}
|
|
1223
1292
|
|
|
1224
1293
|
function ensureFieldExists(fields, fieldName, tableName, role) {
|
|
1225
1294
|
const field = fields.find((item) => item.fieldName === fieldName);
|
|
@@ -1322,9 +1391,10 @@ function buildMultiLevelDictModel(safeArgs) {
|
|
|
1322
1391
|
|
|
1323
1392
|
return {
|
|
1324
1393
|
featureTitle: safeArgs.featureTitle || safeArgs.tableComment || parentModule.tableComment,
|
|
1325
|
-
tableName: safeArgs.tableName,
|
|
1394
|
+
tableName: safeArgs.tableName,
|
|
1326
1395
|
tableComment: safeArgs.tableComment || parentModule.tableComment,
|
|
1327
1396
|
apiPath: safeArgs.apiPath || parentModule.apiPath,
|
|
1397
|
+
pageType: safeArgs.pageType || 'dictionary',
|
|
1328
1398
|
className: toPascalCase(safeArgs.tableName || parentModule.tableName),
|
|
1329
1399
|
functionName: resolvedTargets.functionName,
|
|
1330
1400
|
moduleName: resolvedTargets.moduleName,
|
|
@@ -1419,6 +1489,7 @@ function buildModel(safeArgs) {
|
|
|
1419
1489
|
tableName: safeArgs.tableName,
|
|
1420
1490
|
tableComment: safeArgs.tableComment || safeArgs.featureTitle || safeArgs.tableName,
|
|
1421
1491
|
apiPath,
|
|
1492
|
+
pageType: safeArgs.pageType || '',
|
|
1422
1493
|
className: toPascalCase(safeArgs.tableName),
|
|
1423
1494
|
functionName: resolvedTargets.functionName,
|
|
1424
1495
|
moduleName: resolvedTargets.moduleName,
|
|
@@ -2591,15 +2662,18 @@ function renderFiles(model, stylePreset, sharedSupport, localeZhSupport) {
|
|
|
2591
2662
|
return files;
|
|
2592
2663
|
}
|
|
2593
2664
|
|
|
2594
|
-
function ensureArguments(input) {
|
|
2595
|
-
if (!input || typeof input !== 'object') throw new Error('Arguments must be an object');
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2665
|
+
function ensureArguments(input) {
|
|
2666
|
+
if (!input || typeof input !== 'object') throw new Error('Arguments must be an object');
|
|
2667
|
+
rejectSemanticStageInputs(input);
|
|
2668
|
+
for (const key of TOOL_SCHEMA.required) {
|
|
2669
|
+
if (input[key] === undefined || input[key] === null || input[key] === '') throw new Error(key + ' is required');
|
|
2670
|
+
}
|
|
2599
2671
|
|
|
2600
|
-
const style = String(input.style);
|
|
2601
|
-
|
|
2602
|
-
|
|
2672
|
+
const style = String(input.style);
|
|
2673
|
+
const pageType = normalizePageTypeInput(input.pageType);
|
|
2674
|
+
getStylePreset(style);
|
|
2675
|
+
validatePageTypeAndStyle(pageType, style);
|
|
2676
|
+
const isMultiLevelDict = style === 'multi_level_dict';
|
|
2603
2677
|
const fields = isMultiLevelDict ? [] : normalizeStructuredFieldArray(input.fields, 'fields');
|
|
2604
2678
|
const levels = isMultiLevelDict ? normalizeLevelsInput(input.levels) : [];
|
|
2605
2679
|
|
|
@@ -2616,7 +2690,8 @@ function ensureArguments(input) {
|
|
|
2616
2690
|
tableName: String(input.tableName),
|
|
2617
2691
|
tableComment: input.tableComment ? String(input.tableComment) : '',
|
|
2618
2692
|
apiPath: normalizeApiPath(input.apiPath),
|
|
2619
|
-
|
|
2693
|
+
pageType,
|
|
2694
|
+
style,
|
|
2620
2695
|
fields,
|
|
2621
2696
|
levels,
|
|
2622
2697
|
children: normalizeChildrenInput(input.children),
|
|
@@ -2659,6 +2734,7 @@ function buildManifest(model, safeArgs, stylePreset, files, note) {
|
|
|
2659
2734
|
return {
|
|
2660
2735
|
mode: 'local-template',
|
|
2661
2736
|
style: safeArgs.style,
|
|
2737
|
+
pageType: model.pageType || '',
|
|
2662
2738
|
styleLabel: stylePreset.label,
|
|
2663
2739
|
runtimeSupported: hasRuntimeSupport(stylePreset),
|
|
2664
2740
|
tableName: model.tableName,
|
|
@@ -2712,10 +2788,11 @@ function buildManifest(model, safeArgs, stylePreset, files, note) {
|
|
|
2712
2788
|
}));
|
|
2713
2789
|
const selectedList = relations.map((relation) => formatRelationCandidate(relation));
|
|
2714
2790
|
|
|
2715
|
-
return {
|
|
2716
|
-
mode: 'local-template',
|
|
2717
|
-
style: safeArgs.style,
|
|
2718
|
-
|
|
2791
|
+
return {
|
|
2792
|
+
mode: 'local-template',
|
|
2793
|
+
style: safeArgs.style,
|
|
2794
|
+
pageType: model.pageType || '',
|
|
2795
|
+
styleLabel: stylePreset.label,
|
|
2719
2796
|
runtimeSupported: hasRuntimeSupport(stylePreset),
|
|
2720
2797
|
tableName: model.tableName,
|
|
2721
2798
|
tableComment: model.tableComment,
|
package/package.json
CHANGED