worsoft-frontend-codegen-local-mcp 0.1.33 → 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 +83 -38
- 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,19 +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.' },
|
|
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
155
|
pageType: {
|
|
156
156
|
type: 'string',
|
|
157
157
|
enum: ['feature', 'dictionary', 'non_standard', 'business', 'dict'],
|
|
158
|
-
description: 'Structured page type from parseResult. Dictionary pages are restricted to dialog-based templates.',
|
|
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
159
|
},
|
|
160
|
-
style: { type: 'string', enum: Object.keys(STYLE_CATALOG), description: '
|
|
161
|
-
fields: {
|
|
162
|
-
type: 'array',
|
|
163
|
-
description: 'Structured main-table field metadata
|
|
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.',
|
|
164
164
|
items: {
|
|
165
165
|
type: 'object',
|
|
166
166
|
properties: {
|
|
@@ -177,8 +177,10 @@ const TOOL_SCHEMA = {
|
|
|
177
177
|
queryType: { type: ['string', 'number'], description: 'Structured query type for list filtering, for example 20 for date ranges or 30 for dictionary filters.' },
|
|
178
178
|
dictType: { type: 'string', description: 'Dictionary type code from structured metadata.' },
|
|
179
179
|
defaultValue: { type: ['string', 'number', 'boolean'], description: 'Optional default value.' },
|
|
180
|
-
description: { type: 'string', description: 'Field description or notes.' },
|
|
181
|
-
|
|
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.' },
|
|
182
184
|
primary: { type: ['boolean', 'string'], description: 'Whether the field is the primary key.' },
|
|
183
185
|
},
|
|
184
186
|
required: ['fieldName', 'label', 'type'],
|
|
@@ -187,7 +189,7 @@ const TOOL_SCHEMA = {
|
|
|
187
189
|
},
|
|
188
190
|
children: {
|
|
189
191
|
type: 'array',
|
|
190
|
-
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.',
|
|
191
193
|
items: {
|
|
192
194
|
type: 'object',
|
|
193
195
|
properties: {
|
|
@@ -216,8 +218,10 @@ const TOOL_SCHEMA = {
|
|
|
216
218
|
queryType: { type: ['string', 'number'], description: 'Structured query type for list filtering, for example 20 for date ranges or 30 for dictionary filters.' },
|
|
217
219
|
dictType: { type: 'string', description: 'Dictionary type code from structured metadata.' },
|
|
218
220
|
defaultValue: { type: ['string', 'number', 'boolean'], description: 'Optional default value.' },
|
|
219
|
-
description: { type: 'string', description: 'Field description or notes.' },
|
|
220
|
-
|
|
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.' },
|
|
221
225
|
primary: { type: ['boolean', 'string'], description: 'Whether the field is the primary key.' },
|
|
222
226
|
},
|
|
223
227
|
required: ['fieldName', 'label', 'type'],
|
|
@@ -243,8 +247,8 @@ const TOOL_SCHEMA = {
|
|
|
243
247
|
items: {
|
|
244
248
|
type: 'object',
|
|
245
249
|
properties: {
|
|
246
|
-
tableName: { type: 'string', description: '
|
|
247
|
-
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.' },
|
|
248
252
|
apiPath: { type: 'string', description: 'Backend API base path for this module.' },
|
|
249
253
|
primaryKey: { type: 'string', description: 'Primary key field name. Defaults to id when omitted.' },
|
|
250
254
|
queryParentField: { type: 'string', description: 'Direct parent foreign key field used for page queries.' },
|
|
@@ -271,8 +275,10 @@ const TOOL_SCHEMA = {
|
|
|
271
275
|
queryType: { type: ['string', 'number'], description: 'Structured query type for list filtering, for example 20 for date ranges or 30 for dictionary filters.' },
|
|
272
276
|
dictType: { type: 'string', description: 'Dictionary type code from structured metadata.' },
|
|
273
277
|
defaultValue: { type: ['string', 'number', 'boolean'], description: 'Optional default value.' },
|
|
274
|
-
description: { type: 'string', description: 'Field description or notes.' },
|
|
275
|
-
|
|
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.' },
|
|
276
282
|
primary: { type: ['boolean', 'string'], description: 'Whether the field is the primary key.' }
|
|
277
283
|
},
|
|
278
284
|
required: ['fieldName', 'label', 'type'],
|
|
@@ -850,15 +856,34 @@ function findDictType(comment) {
|
|
|
850
856
|
return extractDictType(comment);
|
|
851
857
|
}
|
|
852
858
|
|
|
853
|
-
function mapFieldType(field) {
|
|
854
|
-
if (field.dictType) return 'select';
|
|
855
|
-
if (field.sqlType === 'DATETIME' || field.sqlType === 'TIMESTAMP') return 'datetime';
|
|
856
|
-
if (field.sqlType === 'DATE') return 'date';
|
|
857
|
-
if (['INT', 'BIGINT', 'DECIMAL', 'NUMERIC'].includes(field.sqlType)) return 'number';
|
|
858
|
-
if (field.sqlType === 'TEXT') return 'textarea';
|
|
859
|
-
if (field.sqlType === 'VARCHAR' && field.length && Number(field.length) > 64) return 'textarea';
|
|
860
|
-
return 'text';
|
|
861
|
-
}
|
|
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
|
+
}
|
|
862
887
|
|
|
863
888
|
function escapeForRegex(value) {
|
|
864
889
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
@@ -1013,6 +1038,7 @@ function normalizeStructuredField(inputField, index, contextLabel) {
|
|
|
1013
1038
|
}
|
|
1014
1039
|
|
|
1015
1040
|
const { length, scale } = normalizeStructuredLengthAndScale(inputField.length, inputField.scale);
|
|
1041
|
+
const explicitFormType = normalizeStructuredFormType(inputField.formType || inputField.componentType);
|
|
1016
1042
|
const explicitQueryType =
|
|
1017
1043
|
inputField.queryType === undefined || inputField.queryType === null || inputField.queryType === ''
|
|
1018
1044
|
? undefined
|
|
@@ -1028,6 +1054,7 @@ function normalizeStructuredField(inputField, index, contextLabel) {
|
|
|
1028
1054
|
comment: label,
|
|
1029
1055
|
label,
|
|
1030
1056
|
description: String(inputField.description || '').trim(),
|
|
1057
|
+
formType: explicitFormType,
|
|
1031
1058
|
dictType: normalizeDictType(inputField.dictType),
|
|
1032
1059
|
notNull: parseBooleanLike(inputField.required, false),
|
|
1033
1060
|
defaultValue: normalizeDefaultValue(inputField.defaultValue),
|
|
@@ -1221,12 +1248,29 @@ function getStylePreset(styleId) {
|
|
|
1221
1248
|
function normalizePageTypeInput(pageType) {
|
|
1222
1249
|
if (pageType === undefined || pageType === null || pageType === '') return '';
|
|
1223
1250
|
const normalized = String(pageType).trim();
|
|
1224
|
-
if (normalized === 'dict'
|
|
1225
|
-
|
|
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
|
+
}
|
|
1226
1254
|
if (['feature', 'dictionary', 'non_standard'].includes(normalized)) return normalized;
|
|
1227
1255
|
throw new Error('Unsupported pageType: ' + normalized);
|
|
1228
1256
|
}
|
|
1229
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
|
+
|
|
1230
1274
|
function validatePageTypeAndStyle(pageType, style) {
|
|
1231
1275
|
if (!pageType) return;
|
|
1232
1276
|
if (pageType === 'non_standard') {
|
|
@@ -1242,9 +1286,9 @@ function hasRuntimeSupport(stylePreset) {
|
|
|
1242
1286
|
return Boolean(stylePreset.runtime && stylePreset.runtime.supported && stylePreset.runtime.templateDir);
|
|
1243
1287
|
}
|
|
1244
1288
|
|
|
1245
|
-
function normalizeFields(parsed) {
|
|
1246
|
-
return parsed.fields.map((field) => ({ ...field, formType: mapFieldType(field), isAudit: isAuditField(field.fieldName) }));
|
|
1247
|
-
}
|
|
1289
|
+
function normalizeFields(parsed) {
|
|
1290
|
+
return parsed.fields.map((field) => ({ ...field, formType: field.formType || mapFieldType(field), isAudit: isAuditField(field.fieldName) }));
|
|
1291
|
+
}
|
|
1248
1292
|
|
|
1249
1293
|
function ensureFieldExists(fields, fieldName, tableName, role) {
|
|
1250
1294
|
const field = fields.find((item) => item.fieldName === fieldName);
|
|
@@ -2618,11 +2662,12 @@ function renderFiles(model, stylePreset, sharedSupport, localeZhSupport) {
|
|
|
2618
2662
|
return files;
|
|
2619
2663
|
}
|
|
2620
2664
|
|
|
2621
|
-
function ensureArguments(input) {
|
|
2622
|
-
if (!input || typeof input !== 'object') throw new Error('Arguments must be an object');
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
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
|
+
}
|
|
2626
2671
|
|
|
2627
2672
|
const style = String(input.style);
|
|
2628
2673
|
const pageType = normalizePageTypeInput(input.pageType);
|
package/package.json
CHANGED