@@ -104,8 +104,18 @@ const generateWidgetCodeSchema = z.object({
104104 widgetPath : z . string ( ) . min ( 1 ) . describe ( "Absolute path to the scaffolded widget directory" ) ,
105105 description : z . string ( ) . min ( 1 ) . describe ( "Description of what the widget should do" ) ,
106106 properties : z
107- . array ( propertyDefinitionSchema )
108- . optional ( )
107+ . preprocess ( v => {
108+ // MCP clients (e.g. Maia) sometimes send JSON arrays as a stringified string.
109+ // Parse it transparently so validation still runs on the actual array contents.
110+ if ( typeof v === "string" ) {
111+ try {
112+ return JSON . parse ( v ) ;
113+ } catch {
114+ return v ; // let Zod report the type error
115+ }
116+ }
117+ return v ;
118+ } , z . array ( propertyDefinitionSchema ) . optional ( ) )
109119 . describe ( "Array of property definitions. If not provided, returns suggestions." ) ,
110120 widgetPattern : z
111121 . enum ( [ "display" , "button" , "input" , "container" , "dataList" ] )
@@ -225,12 +235,15 @@ async function cleanupScaffoldFiles(widgetPath: string, widgetName: string): Pro
225235 const packageXmlPath = join ( srcDir , "package.xml" ) ;
226236 const widgetNameLower = widgetName . toLowerCase ( ) ;
227237 let version = "1.0.0" ;
238+ let packagePath = "mendix" ;
228239 try {
229240 const pkgJson = JSON . parse ( await readFile ( join ( widgetPath , "package.json" ) , "utf-8" ) ) ;
230241 if ( pkgJson . version ) version = pkgJson . version ;
242+ if ( pkgJson . packagePath ) packagePath = pkgJson . packagePath ;
231243 } catch {
232244 /* use default */
233245 }
246+ const filePath = `${ packagePath . replace ( / \. / g, "/" ) } /${ widgetNameLower } ` ;
234247 const packageXml = [
235248 '<?xml version="1.0" encoding="utf-8" ?>' ,
236249 '<package xmlns="http://www.mendix.com/package/1.0/">' ,
@@ -239,7 +252,7 @@ async function cleanupScaffoldFiles(widgetPath: string, widgetName: string): Pro
239252 ` <widgetFile path="${ widgetName } .xml"/>` ,
240253 " </widgetFiles>" ,
241254 " <files>" ,
242- ` <file path="mendix/ ${ widgetNameLower } "/>` ,
255+ ` <file path="${ filePath } "/>` ,
243256 " </files>" ,
244257 " </clientModule>" ,
245258 "</package>"
@@ -418,6 +431,55 @@ function getPatternDescription(pattern: WidgetPattern): string {
418431 }
419432}
420433
434+ /**
435+ * Detects a mismatch between the selected widget pattern and the provided properties.
436+ *
437+ * Returns a warning string when the pattern's key property types are absent,
438+ * or null when the properties satisfy the pattern's requirements.
439+ */
440+ export function detectTemplateMismatch ( pattern : WidgetPattern , properties : PropertyDefinition [ ] ) : string | null {
441+ const types = properties . map ( p => p . type ) ;
442+
443+ switch ( pattern ) {
444+ case "button" : {
445+ const warnings : string [ ] = [ ] ;
446+ if ( ! types . includes ( "action" ) ) {
447+ warnings . push ( "button will be permanently disabled (no action property)" ) ;
448+ }
449+ if ( ! types . includes ( "textTemplate" ) && ! types . includes ( "string" ) ) {
450+ warnings . push ( "button will render empty text (no textTemplate or string property for caption)" ) ;
451+ }
452+ return warnings . length > 0 ? warnings . join ( "; " ) : null ;
453+ }
454+ case "input" :
455+ if ( ! types . includes ( "attribute" ) ) {
456+ return "no attribute property for data binding — input pattern needs an attribute to read/write values" ;
457+ }
458+ return null ;
459+ case "display" :
460+ if ( ! types . includes ( "textTemplate" ) && ! types . includes ( "expression" ) && ! types . includes ( "string" ) ) {
461+ return "read-only display component with no dynamic text source — only primitive types found; customize the generated code or add a textTemplate/expression property" ;
462+ }
463+ return null ;
464+ case "container" :
465+ if ( ! types . includes ( "widgets" ) ) {
466+ return "no child content slot — container pattern needs a widgets property for nested widget content" ;
467+ }
468+ return null ;
469+ case "dataList" : {
470+ const missing : string [ ] = [ ] ;
471+ if ( ! types . includes ( "datasource" ) ) missing . push ( "datasource" ) ;
472+ if ( ! types . includes ( "widgets" ) ) missing . push ( "widgets" ) ;
473+ if ( missing . length > 0 ) {
474+ return `dataList pattern is missing required properties: ${ missing . join ( ", " ) } ` ;
475+ }
476+ return null ;
477+ }
478+ default :
479+ return null ;
480+ }
481+ }
482+
421483// =============================================================================
422484// Tool Handler
423485// =============================================================================
@@ -491,6 +553,7 @@ async function handleGenerateWidgetCode(args: GenerateWidgetCodeInput): Promise<
491553 const filesToWrite = [
492554 { path : `src/${ widgetName } .xml` , content : xmlResult . xml } ,
493555 { path : `src/${ widgetName } .tsx` , content : tsxResult . mainComponent } ,
556+ { path : `src/${ widgetName } .editorPreview.tsx` , content : tsxResult . editorPreview ! } ,
494557 { path : `src/ui/${ widgetName } .scss` , content : `.widget-${ widgetName . toLowerCase ( ) } {\n}\n` } ,
495558 { path : `src/.widget-definition.json` , content : JSON . stringify ( widgetDefinition , null , 2 ) }
496559 ] ;
@@ -518,24 +581,40 @@ async function handleGenerateWidgetCode(args: GenerateWidgetCodeInput): Promise<
518581
519582 // Build success response
520583 const propSummary = properties . map ( p => p . key ) . join ( ", " ) ;
584+ const mismatch = detectTemplateMismatch ( pattern , properties as PropertyDefinition [ ] ) ;
585+
586+ const lines = [
587+ `✅ Widget code generated successfully!` ,
588+ "" ,
589+ `📁 Files written:` ,
590+ ` • src/${ widgetName } .xml - Widget definition with ${ properties . length } properties (${ propSummary } )` ,
591+ ` • src/${ widgetName } .tsx - Component using ${ pattern } pattern` ,
592+ ` • src/ui/${ widgetName } .scss - Empty SCSS placeholder` ,
593+ ` • src/.widget-definition.json - Widget definition snapshot (used by update-widget-properties)`
594+ ] ;
521595
522- return createToolResponse (
523- [
524- `✅ Widget code generated successfully!` ,
596+ if ( mismatch ) {
597+ lines . push ( "" , `⚠️ Template notice: ${ mismatch } ` ) ;
598+ lines . push (
525599 "" ,
526- `📁 Files written:` ,
527- ` • src/${ widgetName } .xml - Widget definition with ${ properties . length } properties (${ propSummary } )` ,
528- ` • src/${ widgetName } .tsx - Component using ${ pattern } pattern` ,
529- ` • src/ui/${ widgetName } .scss - Empty SCSS placeholder` ,
530- ` • src/.widget-definition.json - Widget definition snapshot (used by update-widget-properties)` ,
600+ `🔨 Next steps:` ,
601+ ` 1. Review and customize the generated code (use write-widget-file to update src/${ widgetName } .tsx)` ,
602+ ` 2. Run build-widget to compile and validate` ,
603+ ` 3. Update src/${ widgetName } .editorPreview.tsx for Studio Pro design mode preview` ,
604+ ` 4. Test in Mendix Studio Pro`
605+ ) ;
606+ } else {
607+ lines . push (
531608 "" ,
532609 `🔨 Next steps:` ,
533610 ` 1. Run build-widget to compile and validate` ,
534611 ` 2. Review and customize generated code` ,
535612 ` 3. Update src/${ widgetName } .editorPreview.tsx for Studio Pro design mode preview` ,
536613 ` 4. Test in Mendix Studio Pro`
537- ] . join ( "\n" )
538- ) ;
614+ ) ;
615+ }
616+
617+ return createToolResponse ( lines . join ( "\n" ) ) ;
539618 } catch ( error ) {
540619 const message = error instanceof Error ? error . message : String ( error ) ;
541620 console . error ( `[code-generation] Error: ${ message } ` ) ;
0 commit comments