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.
Files changed (2) hide show
  1. package/mcp_server.js +83 -38
  2. 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.33';
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: 'Target main table name from the structured feature metadata.' },
153
- tableComment: { type: 'string', description: 'Main table comment or feature label for menu/title generation.' },
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: 'Style id from assets/style-catalog.json.' },
161
- fields: {
162
- type: 'array',
163
- description: 'Structured main-table field metadata provided by the caller.',
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
- sourceKind: { type: 'string', enum: ['entity', 'display', 'virtual', 'common'], description: 'Field source kind.' },
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
- sourceKind: { type: 'string', enum: ['entity', 'display', 'virtual', 'common'], description: 'Field source kind.' },
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: 'Module table name.' },
247
- tableComment: { type: 'string', description: 'Module display label.' },
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
- sourceKind: { type: 'string', enum: ['entity', 'display', 'virtual', 'common'], description: 'Field source kind.' },
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') return 'dictionary';
1225
- if (normalized === 'business') return 'feature';
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
- for (const key of TOOL_SCHEMA.required) {
2624
- if (input[key] === undefined || input[key] === null || input[key] === '') throw new Error(key + ' is required');
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "worsoft-frontend-codegen-local-mcp",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "description": "Worsoft frontend local-template code generation MCP server.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "worsoft <sw@worsoft.vip>",