@stellisoft/stellify-mcp 0.1.25 → 0.1.27

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/dist/index.js CHANGED
@@ -26,7 +26,7 @@ const STELLIFY_FRAMEWORK_API = {
26
26
  List: ['create', 'from', 'range', 'add', 'remove', 'removeWhere', 'set', 'get', 'first', 'last', 'sort', 'sortBy', 'reverse', 'filter', 'find', 'findIndex', 'map', 'reduce', 'forEach', 'includes', 'indexOf', 'every', 'some', 'slice', 'take', 'skip', 'chunk', 'unique', 'uniqueBy', 'groupBy', 'flatten', 'concat', 'isEmpty', 'isNotEmpty', 'count', 'clear', 'toArray', 'toJSON', 'clone', 'sum', 'avg', 'min', 'max'],
27
27
  Tree: ['create', 'setRoot', 'addChild', 'removeNode', 'getNode', 'getRoot', 'getChildren', 'getParent', 'getSiblings', 'getAncestors', 'getDescendants', 'getDepth', 'getPath', 'traverse', 'find', 'findAll', 'move', 'toArray', 'size'],
28
28
  // Network
29
- Http: ['create', 'get', 'post', 'put', 'patch', 'delete', 'withHeaders', 'withToken', 'withTimeout'],
29
+ Http: ['create', 'get', 'post', 'put', 'patch', 'delete', 'items', 'withHeaders', 'withToken', 'withTimeout'],
30
30
  Socket: ['create', 'connect', 'disconnect', 'send', 'sendEvent', 'on', 'off', 'once', 'isConnected', 'getState'],
31
31
  Auth: ['create', 'login', 'logout', 'fetchUser', 'getUser', 'getToken', 'isAuthenticated', 'setToken', 'setUser', 'refresh', 'onAuthChange', 'offAuthChange', 'getAuthHeader'],
32
32
  Stream: ['create', 'headers', 'withToken', 'onChunk', 'onComplete', 'onError', 'get', 'post', 'abort', 'getBuffer', 'getChunks', 'isStreaming', 'clear'],
@@ -77,12 +77,8 @@ The npm package is "stellify-framework" (NOT @stellify/core).
77
77
  Import like: import { Http, List, Form } from 'stellify-framework';
78
78
 
79
79
  IMPORTANT - List class and Vue reactivity:
80
- The List class methods return List instances, NOT plain arrays.
81
- Vue's v-for directive cannot iterate over List instances directly.
82
-
83
- When assigning to a Vue ref that will be used with v-for, call .toArray():
84
- - CORRECT: notes.value = List.from(response.data).toArray();
85
- - INCORRECT (v-for won't work): notes.value = List.from(response.data);
80
+ List is iterable and works directly with Vue's v-for directive.
81
+ Use List.from() to wrap arrays for chainable operations (filter, map, sort, etc.).
86
82
 
87
83
  Example response:
88
84
  {
@@ -127,20 +123,27 @@ This creates an EMPTY file shell - no methods, statements, or template yet. The
127
123
 
128
124
  COMPLETE WORKFLOW:
129
125
  1. create_file → creates empty shell, returns file UUID
130
- 2. create_statement + add_statement_code → add variables/imports (returns statement UUIDs)
131
- 3. create_method + add_method_body → add functions (returns method UUIDs)
126
+ 2. create_statement_with_code → add variables/imports in ONE call (returns statement UUIDs)
127
+ 3. create_method with body param → add functions in ONE call (returns method UUIDs)
132
128
  4. html_to_elements → create template elements (returns element UUIDs)
133
129
  5. save_file → FINALIZE by wiring template/data arrays with all collected UUIDs
134
130
 
131
+ LEGACY (still supported but prefer combined tools above):
132
+ - create_statement + add_statement_code (2 calls instead of 1)
133
+ - create_method + add_method_body + save_method for is_async (3 calls instead of 1)
134
+
135
135
  For PHP: Use type='class', 'model', 'controller', or 'middleware'.
136
136
  For Vue: Use type='js' and extension='vue'. Place in the 'js' directory.
137
+ - Auto-creates app.js (check response.appJs)
138
+ - Auto-creates template route for visual editor (check response.templateRoute.uuid)
137
139
 
138
140
  DEPENDENCY RESOLUTION (automatic):
139
- Pass 'includes' as an array of namespace strings (e.g., ["App\\Models\\User", "Illuminate\\Support\\Facades\\Hash"]).
140
- The system resolves these to UUIDs automatically, creating missing dependencies on-demand:
141
- - App\\* classes → creates stub file in your project (tenant DB)
141
+ Pass 'includes' as an array of namespace strings for FRAMEWORK classes (e.g., ["Illuminate\\Http\\Request", "Illuminate\\Support\\Facades\\Hash"]).
142
+ The system resolves these to UUIDs automatically:
142
143
  - Illuminate\\*/Laravel\\* → fetches from Laravel API, creates in Application DB
143
- - Vendor packages → fetches from vendor, creates in Application DB`,
144
+ - Vendor packages → fetches from vendor, creates in Application DB
145
+
146
+ NOTE: For controllers that use PROJECT models (Feedback, Vote, etc.), add those to the 'models' array in save_file instead. Do NOT put project models in includes - this causes duplicate use statement errors.`,
144
147
  inputSchema: {
145
148
  type: 'object',
146
149
  properties: {
@@ -180,11 +183,13 @@ The system resolves these to UUIDs automatically, creating missing dependencies
180
183
  },
181
184
  {
182
185
  name: 'create_method',
183
- description: `Create a method signature in a file. This only creates the method declaration, not the body. Use add_method_body to add implementation.
186
+ description: `Create a method in a file. Can optionally include the body and async flag in a single call.
187
+
188
+ **NEW: Combined creation** - Pass 'body' to create the complete method in ONE call. Async is auto-detected when body contains \`await\`.
184
189
 
185
190
  Parameters are automatically created as clauses. The response includes the clause UUIDs for each parameter.
186
191
 
187
- Example request:
192
+ Example request (simple - signature only):
188
193
  {
189
194
  "file": "file-uuid",
190
195
  "name": "verify",
@@ -196,14 +201,21 @@ Example request:
196
201
  ]
197
202
  }
198
203
 
204
+ Example request (combined - with body, async auto-detected):
205
+ {
206
+ "file": "file-uuid",
207
+ "name": "fetchData",
208
+ "body": "const response = await Http.get('/api/data');\\nreturn response.data;"
209
+ }
210
+
199
211
  Example response includes:
200
212
  {
201
213
  "uuid": "method-uuid",
202
- "name": "verify",
203
- "returnType": "object",
204
- "nullable": true,
214
+ "name": "fetchData",
215
+ "is_async": true,
205
216
  "parameters": ["clause-uuid-for-credentials"],
206
- ...
217
+ "statements": {...}, // Only if body was provided
218
+ "clauses": {...} // Only if body was provided
207
219
  }`,
208
220
  inputSchema: {
209
221
  type: 'object',
@@ -225,6 +237,10 @@ Example response includes:
225
237
  type: 'boolean',
226
238
  description: 'Whether the method is static (PHP only)',
227
239
  },
240
+ is_async: {
241
+ type: 'boolean',
242
+ description: 'Whether the method is async (JavaScript/Vue only). Set to true for methods that use await.',
243
+ },
228
244
  returnType: {
229
245
  type: 'string',
230
246
  description: 'Return type (e.g., "int", "string", "void", "object")',
@@ -259,6 +275,10 @@ Example response includes:
259
275
  required: ['name'],
260
276
  },
261
277
  },
278
+ body: {
279
+ type: 'string',
280
+ description: 'Method body code (optional). If provided, automatically parses and adds the code. Example: "const response = await Http.get(\\"/api/data\\");\\nreturn response.data;"',
281
+ },
262
282
  },
263
283
  required: ['file', 'name'],
264
284
  },
@@ -434,7 +454,9 @@ Example - Remove duplicate/unwanted statements:
434
454
  },
435
455
  {
436
456
  name: 'create_route',
437
- description: `Create a route/page. For API routes, pass controller and controller_method UUIDs to wire execution.
457
+ description: `Create a route/page. For API routes, you MUST pass BOTH controller AND controller_method UUIDs to wire execution.
458
+
459
+ IMPORTANT: Both 'controller' (file UUID) and 'controller_method' (method UUID) are required together for API routes to execute code. Without both, the route won't run any code.
438
460
 
439
461
  Route params like {id} auto-inject into controller method parameters when names match.`,
440
462
  inputSchema: {
@@ -466,11 +488,11 @@ Route params like {id} auto-inject into controller method parameters when names
466
488
  },
467
489
  controller: {
468
490
  type: 'string',
469
- description: 'UUID of the controller file to handle this route. Required for API routes to execute code.',
491
+ description: 'UUID of the controller file. MUST be provided together with controller_method for API routes to execute code.',
470
492
  },
471
493
  controller_method: {
472
494
  type: 'string',
473
- description: 'UUID of the method within the controller to execute. Required for API routes to execute code.',
495
+ description: 'UUID of the method to execute. MUST be provided together with controller for API routes to execute code.',
474
496
  },
475
497
  data: {
476
498
  type: 'object',
@@ -505,6 +527,7 @@ Use this to look up a route you created or to find existing routes in the projec
505
527
  description: `Update an existing route/page. Use this to wire a route to a controller method.
506
528
 
507
529
  IMPORTANT: This is how you connect API routes to controller methods!
530
+ IMPORTANT: You MUST provide BOTH controller AND controller_method together - they are a pair.
508
531
 
509
532
  Example - Wire an API route to a controller method:
510
533
  {
@@ -514,8 +537,8 @@ Example - Wire an API route to a controller method:
514
537
  }
515
538
 
516
539
  Available fields:
517
- - controller: UUID of the controller file
518
- - controller_method: UUID of the method to execute
540
+ - controller: UUID of the controller file (MUST be paired with controller_method)
541
+ - controller_method: UUID of the method to execute (MUST be paired with controller)
519
542
  - path: URL path
520
543
  - name: Route name
521
544
  - type: "web" or "api"
@@ -531,11 +554,11 @@ Available fields:
531
554
  },
532
555
  controller: {
533
556
  type: 'string',
534
- description: 'UUID of the controller file to handle this route',
557
+ description: 'UUID of the controller file. MUST be provided together with controller_method.',
535
558
  },
536
559
  controller_method: {
537
560
  type: 'string',
538
- description: 'UUID of the method within the controller to execute',
561
+ description: 'UUID of the method to execute. MUST be provided together with controller.',
539
562
  },
540
563
  path: {
541
564
  type: 'string',
@@ -657,11 +680,22 @@ Generates: <div class="card p-4" v-for="note in notes" :key="note.id">`,
657
680
  name: 'update_element',
658
681
  description: `Update a UI element's attributes.
659
682
 
660
- Pass data object with: tag, classes (array), text, variable (for v-model), and event handlers (click, submit, etc. = method UUID).
683
+ Pass data object with: tag, classes (array), text, variable (for v-model), and event handlers.
661
684
 
662
685
  Key fields: inputType (not 'type') for button/input HTML type. clickArgs for handler arguments in v-for loops.
663
686
 
664
- Event handlers: click, submit, change, input, focus, blur, keydown, keyup, mouseenter, mouseleave.`,
687
+ EVENT HANDLERS - Use method UUIDs:
688
+ { "click": "method-uuid" } → @click="methodName"
689
+ { "click": "method-uuid", "clickArgs": "item" } → @click="methodName(item)"
690
+
691
+ Create methods for all handlers, including simple state changes like opening modals or toggling flags.
692
+
693
+ Event types: click, submit, change, input, focus, blur, keydown, keyup, mouseenter, mouseleave.
694
+
695
+ EFFICIENCY - Prefer updates over delete/recreate:
696
+ - Move between routes: change \`routeParent\` attribute
697
+ - Reparent elements: change \`parent\` attribute
698
+ - Reorder children: update parent's \`data\` array with new UUID order`,
665
699
  inputSchema: {
666
700
  type: 'object',
667
701
  properties: {
@@ -754,6 +788,8 @@ Note: To reorder elements, use update_element to modify the parent element's 'da
754
788
 
755
789
  Auto-detects Vue bindings ({{ var }}). For Vue components, omit 'page'. For pages, provide route UUID.
756
790
 
791
+ **@click auto-wiring:** Pass 'file' UUID to auto-resolve @click="methodName" handlers. Methods must exist in the file first.
792
+
757
793
  Prefer SVG icons over emoji (encoding issues).`,
758
794
  inputSchema: {
759
795
  type: 'object',
@@ -770,6 +806,10 @@ Prefer SVG icons over emoji (encoding issues).`,
770
806
  type: 'string',
771
807
  description: 'Parent element UUID to attach to (alternative to page)',
772
808
  },
809
+ file: {
810
+ type: 'string',
811
+ description: 'Vue component file UUID. Pass this to auto-wire @click handlers to method UUIDs.',
812
+ },
773
813
  test: {
774
814
  type: 'boolean',
775
815
  description: 'If true, returns structure without creating elements',
@@ -799,6 +839,8 @@ Prefer SVG icons over emoji (encoding issues).`,
799
839
  name: 'create_statement',
800
840
  description: `Create an empty statement in a file. This is step 1 of 2 - you MUST call add_statement_code next to add the actual code.
801
841
 
842
+ **ALTERNATIVE:** Use create_statement_with_code for a single-call approach that combines both steps.
843
+
802
844
  IMPORTANT: This is a TWO-STEP process:
803
845
  1. create_statement → returns statement UUID
804
846
  2. add_statement_code → adds the actual code to that statement
@@ -822,10 +864,42 @@ For Vue components, include the returned statement UUID in save_file's 'statemen
822
864
  },
823
865
  },
824
866
  },
867
+ {
868
+ name: 'create_statement_with_code',
869
+ description: `Create a statement with code in a SINGLE call. This combines create_statement and add_statement_code.
870
+
871
+ **PREFERRED:** Use this instead of the two-step create_statement → add_statement_code process.
872
+
873
+ Examples:
874
+ - PHP: "use Illuminate\\Http\\Request;" or "private $items = [];"
875
+ - JS/Vue: "const count = ref(0);" or "import { ref } from 'vue';"
876
+
877
+ For Vue components, include the returned statement UUID in save_file's 'statements' array (NOT 'data' - that's for methods).`,
878
+ inputSchema: {
879
+ type: 'object',
880
+ properties: {
881
+ file: {
882
+ type: 'string',
883
+ description: 'UUID of the file to add the statement to',
884
+ },
885
+ code: {
886
+ type: 'string',
887
+ description: 'The code for the statement (e.g., "const count = ref(0);")',
888
+ },
889
+ method: {
890
+ type: 'string',
891
+ description: 'UUID of the method to add the statement to (optional, for method body statements)',
892
+ },
893
+ },
894
+ required: ['file', 'code'],
895
+ },
896
+ },
825
897
  {
826
898
  name: 'add_statement_code',
827
899
  description: `Add code to an existing statement. This is step 2 of 2 - call this AFTER create_statement.
828
900
 
901
+ **ALTERNATIVE:** Use create_statement_with_code for a single-call approach.
902
+
829
903
  The statement must already exist (created via create_statement). This parses and stores the code.
830
904
 
831
905
  Examples:
@@ -962,12 +1036,12 @@ This works the same as create_file's dependency resolution.`,
962
1036
  includes: {
963
1037
  type: 'array',
964
1038
  items: { type: 'string' },
965
- description: 'Array of file UUIDs OR namespace strings to import. Namespace strings (e.g., "Illuminate\\\\Http\\\\JsonResponse") are auto-resolved to UUIDs.',
1039
+ description: 'Array of file UUIDs OR namespace strings for FRAMEWORK classes only (Request, JsonResponse, etc.). Do NOT put project models here - use the models array instead.',
966
1040
  },
967
1041
  models: {
968
1042
  type: 'array',
969
1043
  items: { type: 'string' },
970
- description: 'Array of model file UUIDs that this controller uses (required for model class loading)',
1044
+ description: 'Array of model file UUIDs for PROJECT models (Feedback, Vote, etc.). These get sandbox namespace automatically. Do NOT also add these to includes or you will get duplicate use statement errors.',
971
1045
  },
972
1046
  },
973
1047
  required: ['uuid', 'name', 'type'],
@@ -1404,7 +1478,7 @@ The response includes actionable suggestions like:
1404
1478
  },
1405
1479
  ];
1406
1480
  // Server instructions for tool discovery (used by MCP Tool Search)
1407
- const SERVER_INSTRUCTIONS = `Stellify is a coding platform where you code alongside AI on a codebase maintained and curated by AI. Build Laravel/PHP and Vue.js applications.
1481
+ const SERVER_INSTRUCTIONS = `Stellify is a coding platform where you code alongside AI on a codebase maintained and curated by AI. Build Laravel, stellify-framework, and Vue.js applications.
1408
1482
 
1409
1483
  Use Stellify tools when:
1410
1484
  - Building PHP controllers, models, middleware, or classes
@@ -1430,198 +1504,89 @@ Key concepts:
1430
1504
 
1431
1505
  1. get_project → find 'js' directory UUID (or create it)
1432
1506
  2. create_file → type='js', extension='vue' for the component
1433
- 3. **Create a template route** for editor access:
1434
- - create_route with name like "notes-template" or "component-name-template"
1435
- - This route is NOT where the component displays - it's only for accessing the template in the Stellify visual editor
1436
- - The actual display route (e.g., "/notes") will have just \`<div id="app"></div>\` where Vue mounts
1437
- 4. Create statements for imports:
1438
- - "import { ref, onMounted } from 'vue';" (REQUIRED for ref() and lifecycle hooks)
1439
- - "import { Http } from 'stellify-framework';" (for API calls)
1440
- NOTE: The npm package is "stellify-framework" (NOT @stellify/core)
1441
- 5. Create statements for reactive data: "const notes = ref([]);"
1442
- 6. create_method + add_method_body for functions. Example fetchNotes body:
1507
+ - **Auto-creates app.js** and **template route** (check response for appJs and templateRoute fields)
1508
+ 3. Create statements for imports using **create_statement_with_code** (ONE call each):
1509
+ - create_statement_with_code(file, "import { ref, onMounted } from 'vue';")
1510
+ - create_statement_with_code(file, "import { Http } from 'stellify-framework';")
1511
+ 4. Create statements for reactive data: create_statement_with_code(file, "const notes = ref([]);")
1512
+ 5. **create_method with body** (async auto-detected from \`await\`). Example:
1443
1513
  \`\`\`
1444
- const response = await Http.get('/api/notes');
1445
- notes.value = response.data || [];
1514
+ create_method({
1515
+ file: fileUuid,
1516
+ name: "fetchNotes",
1517
+ body: "const response = await Http.get('/api/notes');\\nnotes.value = response.data || [];"
1518
+ })
1446
1519
  \`\`\`
1447
- **THEN call save_method with is_async: true** (required for methods using await)
1448
- 7. Create statement for onMounted: "onMounted(fetchNotes);" (direct method reference, no arrow wrapper)
1449
- 8. html_to_elements → template **with page=templateRouteUuid** (attach to the template route for editor access)
1450
- 9. update_element wire click handlers to method UUIDs
1451
- 10. save_file with: extension='vue', template=[elementUuid], data=[methodUuids], statements=[importUuids, refUuids, onMountedUuid]
1452
- - **data = method UUIDs only** (functions)
1453
- - **statements = statement UUIDs** (imports, refs, onMounted)
1454
- 11. **MANDATORY: Create app.js entry file** (see "CRITICAL: JavaScript Entry File" section below)
1455
- 12. Create web route for the display page (e.g., "/notes")
1456
- 13. **MANDATORY: Add a div with id="app"** to the display page using html_to_elements:
1457
- - html_to_elements with page=routeUuid and elements='<div id="app"></div>'
1458
- - This is where Vue mounts the component. Without it, nothing renders!
1459
-
1460
- ## Real-Time UI (broadcast_element_command)
1461
-
1462
- Use for SHOW/DISPLAY/DEMONSTRATE requests - sends instant WebSocket updates to browser.
1463
- Use html_to_elements/update_element for permanent/saved changes.
1464
-
1465
- ## CRITICAL: Http Response Handling (Most Common Error)
1466
-
1467
- **This is the #1 cause of "notes not displaying" bugs.**
1468
-
1469
- When fetching data from API endpoints, stellify-framework's Http class returns the JSON body DIRECTLY - it does NOT wrap responses like axios does.
1520
+ 6. Create statement for onMounted: create_statement_with_code(file, "onMounted(fetchData);")
1521
+ 7. **Create UI interaction methods** for any button that changes state:
1522
+ \`\`\`
1523
+ create_method({ file: fileUuid, name: "openModal", body: "showModal.value = true;" })
1524
+ create_method({ file: fileUuid, name: "closeModal", body: "showModal.value = false;" })
1525
+ \`\`\`
1526
+ 8. html_to_elements with @click handlers auto-wired:
1527
+ \`\`\`
1528
+ html_to_elements({
1529
+ elements: '<button @click="openModal">Open</button>',
1530
+ page: templateRoute.uuid,
1531
+ file: fileUuid // Auto-wires @click="openModal" to the method UUID
1532
+ })
1533
+ \`\`\`
1534
+ 9. save_file with: extension='vue', template=[elementUuid], data=[methodUuids], statements=[importUuids, refUuids, onMountedUuid]
1535
+ 11. Create web route for the display page (e.g., "/notes")
1536
+ 12. Add \`<div id="app"></div>\` to the display page: html_to_elements(page=routeUuid, elements='<div id="app"></div>')
1470
1537
 
1471
- Laravel pagination returns: \`{ data: [...items], current_page: 1, per_page: 15, ... }\`
1538
+ ## Fetching Paginated Data
1472
1539
 
1473
- Since Http.get() returns this JSON directly (no axios wrapper), you access the array with ONE \`.data\`:
1540
+ Use \`Http.items()\` for paginated endpoints - it auto-extracts the array from Laravel responses:
1474
1541
 
1475
1542
  \`\`\`javascript
1476
- // CORRECT - Http returns JSON directly, .data gets the paginated array
1477
- const response = await Http.get('/api/notes');
1478
- notes.value = response.data || [];
1543
+ // Recommended - auto-extracts .data from paginated response
1544
+ notes.value = await Http.items('/api/notes');
1479
1545
 
1480
- // WRONG - Double .data (axios habit) - returns undefined, notes stay empty!
1546
+ // Alternative - manual extraction
1481
1547
  const response = await Http.get('/api/notes');
1482
- notes.value = response.data.data || []; // BUG: response.data.data is undefined
1548
+ notes.value = response.data || [];
1483
1549
  \`\`\`
1484
1550
 
1485
- **Why this mistake happens:** With axios, you write \`response.data.data\` because axios wraps the HTTP response (\`response.data\` unwraps axios, then \`.data\` gets the pagination array). Stellify's Http skips the wrapper, so you only need ONE \`.data\`.
1486
-
1487
- **Symptom:** Notes exist in database, API returns them, but UI shows empty list or "No notes yet" message.
1488
-
1489
1551
  ## Common Pitfalls
1490
1552
 
1491
- - **Vue template editor access:** Templates MUST be attached to a route for users to edit them in the visual editor. Create a separate "template route" (e.g., "notes-template") and pass its UUID to html_to_elements. This is different from the display route where the component renders.
1553
+ - **@click auto-wiring:** Pass the file UUID to html_to_elements to auto-wire @click handlers. Methods must be created BEFORE calling html_to_elements.
1492
1554
  - **Stellify imports:** Use "stellify-framework" package (NOT @stellify/core)
1493
1555
  CORRECT: import { Http, List, Form } from 'stellify-framework';
1494
1556
  WRONG: import { Http } from '@stellify/core';
1495
1557
  - v-model requires ref(), NOT Form class: const formData = ref({title: ''})
1496
- - List.from() returns List, not array - use .toArray() for v-for
1558
+ - List is iterable and works directly with v-for (no .toArray() needed)
1497
1559
  - add_method_body APPENDS, doesn't replace - create new method to replace
1498
1560
  - 'data' array = method UUIDs, 'statements' array = import/variable UUIDs
1499
1561
  - For buttons in forms, set inputType: "button" to prevent auto-submit
1500
- - **Async methods:** Methods using await MUST be marked async via save_method with is_async: true
1562
+ - **Async methods:** Auto-detected when body contains \`await\`. Response includes \`is_async: true\` if detected.
1501
1563
  - **onMounted:** Use direct method reference: "onMounted(fetchNotes);"
1502
1564
  The assembler automatically outputs lifecycle hooks after method definitions.
1503
1565
 
1504
- ## CRITICAL: Nullable Refs and v-if Guards
1505
-
1506
- **This causes "Cannot read properties of null" errors.**
1507
-
1508
- When using a nullable ref (e.g., \`const editingNote = ref(null)\`) with v-model or property access in templates, you MUST guard with an explicit v-if that checks the ref is not null.
1509
-
1510
- **WRONG - v-else does NOT protect against null evaluation:**
1511
- \`\`\`html
1512
- <template v-if="!editingItem">
1513
- <!-- view mode -->
1514
- </template>
1515
- <template v-else>
1516
- <input v-model="editingItem.title" /> <!-- ERROR: editingItem could be null -->
1517
- </template>
1518
- \`\`\`
1519
-
1520
- **CORRECT - explicit v-if with null check:**
1521
- \`\`\`html
1522
- <template v-if="!editingItem || editingItem.id !== item.id">
1523
- <!-- view mode -->
1524
- </template>
1525
- <template v-if="editingItem && editingItem.id === item.id">
1526
- <input v-model="editingItem.title" /> <!-- Safe: editingItem is guaranteed non-null -->
1527
- </template>
1528
- \`\`\`
1529
-
1530
- **Why v-else fails:** Vue evaluates v-model bindings during compilation/render setup, before the v-else condition is fully applied. The explicit v-if with \`editingItem &&\` ensures the binding is only evaluated when the ref exists.
1531
-
1532
- **Inline editing pattern (CRUD apps):**
1533
- 1. Create ref: \`const editingItem = ref(null);\`
1534
- 2. View template: \`v-if="!editingItem || editingItem.id !== item.id"\`
1535
- 3. Edit template: \`v-if="editingItem && editingItem.id === item.id"\` (NOT v-else!)
1536
- 4. Start editing: \`editingItem.value = { ...item };\`
1537
- 5. Cancel/save: \`editingItem.value = null;\`
1538
-
1539
- ## Framework Capabilities (Libraries/Packages)
1540
-
1541
- **CRITICAL: You write BUSINESS LOGIC only. Capabilities are installed packages/libraries (Sanctum, Stripe, AWS SDK, etc.) that Stellify provides. You CANNOT create these by writing code.**
1542
-
1543
- Examples of capabilities (packages you cannot write):
1544
- - Authentication: laravel/sanctum, laravel/socialite
1545
- - Payments: stripe/stripe-php
1546
- - Storage: aws/aws-sdk-php, league/flysystem-aws-s3-v3
1547
- - Email: mailgun/mailgun-php, aws/aws-sdk-php (SES)
1548
- - Search: meilisearch/meilisearch-php, algolia/algoliasearch-client-php
1549
- - WebSocket: laravel/reverb
1550
-
1551
- **WORKFLOW:**
1552
-
1553
- 1. When a user requests functionality that might need a package/library, check the capabilities list from get_project FIRST.
1554
-
1555
- 2. If status is "available" → package is installed, proceed with business logic.
1556
-
1557
- 3. If status is "needs_config" → package installed but needs API keys. INFORM THE USER to configure it in Project Settings.
1558
-
1559
- 4. If status is "not_available" or doesn't exist:
1560
- - STOP IMMEDIATELY
1561
- - Call request_capability() to log it
1562
- - INFORM THE USER: "This feature requires the [X] package which isn't installed in Stellify yet. I've logged a request. This cannot be built until the package is added."
1563
-
1564
- **NEVER write code that belongs in a package.** If you find yourself writing OAuth flows, payment processing, S3 clients, email transport, search indexing, or similar infrastructure - STOP. That's a capability request, not business logic.
1565
-
1566
- ## Project Modules (Code Organization)
1566
+ ## Nullable Refs: Use explicit v-if, NOT v-else
1567
1567
 
1568
- When building features, group related files by passing a "module" parameter.
1568
+ When using nullable refs (e.g., \`const editingItem = ref(null)\`) with v-model, use explicit v-if guards:
1569
+ - WRONG: \`<template v-else><input v-model="editingItem.title"/></template>\`
1570
+ - CORRECT: \`<template v-if="editingItem && editingItem.id === item.id"><input v-model="editingItem.title"/></template>\`
1569
1571
 
1570
- - create_file(..., module: "blog-posts") - auto-groups file
1571
- - create_route(..., module: "blog-posts") - auto-groups route
1572
+ Vue evaluates v-model bindings before v-else is applied, causing "Cannot read properties of null" errors.
1572
1573
 
1573
- Modules are auto-created if they don't exist. This helps users see all code related to a feature grouped together.
1574
+ ## Stack and Business Logic
1574
1575
 
1575
- ## CRITICAL: JavaScript Entry File (app.js)
1576
+ **Default stack:** Laravel (PHP), stellify-framework (JS), and Vue.js. Available capabilities (optional packages/libraries) are returned by \`get_project\`. Use \`get_stellify_framework_api\` for the stellify-framework API reference.
1576
1577
 
1577
- **You MUST have a JS entry file to register Vue components for use on pages.**
1578
+ **All business logic** (controllers, models, middleware, etc.) goes in the tenant DB via MCP tools. If a required capability is not available, use \`request_capability\` to log it.
1578
1579
 
1579
- ### First component in a project:
1580
- 1. Create app.js in the 'js' directory with the component in includes array:
1581
- - create_file with type='js', extension='js', name='app', includes=[component-file-uuid]
1582
- 2. Add statements for NAMED imports only:
1583
- - "import { createApp } from 'vue';"
1584
- - "createApp(NotesApp).mount('#app');"
1585
- NOTE: The component import (NotesApp) is handled by the includes array, NOT a statement!
1586
- 3. save_file with statement UUIDs
1580
+ ## JavaScript Entry File (app.js) - Auto-Generated
1587
1581
 
1588
- ### Adding more components (app.js already exists):
1589
- 1. **search_files** for "app" to find existing app.js
1590
- 2. **get_file** to retrieve current includes and statements
1591
- 3. Add the new component UUID to the includes array via save_file
1592
- 4. Update the mount code to register the new component:
1593
- - Create new statement: "app.component('Counter', Counter);"
1594
- 5. save_file with updated includes and statements arrays
1582
+ When you create a Vue component, **app.js is automatically created/updated** with component registration. The create_file response will confirm this with an \`appJs\` field.
1595
1583
 
1596
- **DO NOT create duplicate app.js files or duplicate createApp imports!**
1597
- **DO NOT use statements for file imports - use the includes array!**
1598
-
1599
- ### Page mount point (div#app):
1600
- Each page that uses Vue components needs a \`<div id="app"></div>\`.
1601
-
1602
- - **First time on a page:** html_to_elements(page=routeUuid, elements='<div id="app"></div>')
1603
- - **Page already has it:** Do NOT add another one. Check with get_route first if unsure.
1604
-
1605
- **Without app.js AND div#app, Vue components will NOT render!**
1606
-
1607
- ### Import types summary:
1608
- - **File imports (components, classes):** Use \`includes\` array with file UUIDs
1609
- - **Named imports (vue, stellify-framework):** Use statements with add_statement_code
1610
-
1611
- Example app.js structure:
1612
- - includes: [notesAppFileUuid, counterFileUuid]
1613
- - statements: ["import { createApp } from 'vue';", "const app = createApp({});", "app.component('NotesApp', NotesApp);", "app.component('Counter', Counter);", "app.mount('#app');"]
1614
-
1615
- ## Efficiency Tips
1616
-
1617
- - **Move elements between routes:** Use \`update_element\` to change \`routeParent\` - don't delete/recreate
1618
- - **Reparent elements:** Update \`parent\` attribute instead of recreating
1619
- - **Reorder children:** Update parent's \`data\` array with new UUID order
1620
- - Always prefer updating over deleting and recreating`;
1584
+ **Page mount point:** Each page using Vue needs \`<div id="app"></div>\`:
1585
+ - html_to_elements(page=routeUuid, elements='<div id="app"></div>')`;
1621
1586
  // Create MCP server
1622
1587
  const server = new Server({
1623
1588
  name: 'stellify-mcp',
1624
- version: '0.1.25',
1589
+ version: '0.1.27',
1625
1590
  }, {
1626
1591
  capabilities: {
1627
1592
  tools: {},
@@ -1684,14 +1649,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1684
1649
  case 'create_file': {
1685
1650
  const result = await stellify.createFile(args);
1686
1651
  const fileData = result.data || result;
1652
+ const appJs = fileData.appJs || result.appJs;
1653
+ let message = `Created file "${args.name}" (UUID: ${fileData.uuid})`;
1654
+ if (appJs?.created) {
1655
+ message += `. Auto-created app.js (UUID: ${appJs.uuid}) with component registration.`;
1656
+ }
1657
+ else if (appJs?.updated) {
1658
+ message += `. Auto-updated app.js to include this component.`;
1659
+ }
1687
1660
  return {
1688
1661
  content: [
1689
1662
  {
1690
1663
  type: 'text',
1691
1664
  text: JSON.stringify({
1692
1665
  success: true,
1693
- message: `Created file "${args.name}" (UUID: ${fileData.uuid})`,
1666
+ message,
1694
1667
  file: fileData,
1668
+ appJs: appJs || null,
1695
1669
  }, null, 2),
1696
1670
  },
1697
1671
  ],
@@ -1700,15 +1674,26 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1700
1674
  case 'create_method': {
1701
1675
  const result = await stellify.createMethod(args);
1702
1676
  const methodData = result.data || result;
1677
+ const hasBody = !!args.body;
1678
+ const response = {
1679
+ success: true,
1680
+ message: hasBody
1681
+ ? `Created method "${args.name}" with body (UUID: ${methodData.uuid})`
1682
+ : `Created method "${args.name}" (UUID: ${methodData.uuid})`,
1683
+ method: methodData,
1684
+ };
1685
+ // Include statements and clauses if body was provided
1686
+ if (result.statements) {
1687
+ response.statements = result.statements;
1688
+ }
1689
+ if (result.clauses) {
1690
+ response.clauses = result.clauses;
1691
+ }
1703
1692
  return {
1704
1693
  content: [
1705
1694
  {
1706
1695
  type: 'text',
1707
- text: JSON.stringify({
1708
- success: true,
1709
- message: `Created method "${args.name}" (UUID: ${methodData.uuid})`,
1710
- method: methodData,
1711
- }, null, 2),
1696
+ text: JSON.stringify(response, null, 2),
1712
1697
  },
1713
1698
  ],
1714
1699
  };
@@ -2030,6 +2015,24 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2030
2015
  ],
2031
2016
  };
2032
2017
  }
2018
+ case 'create_statement_with_code': {
2019
+ const result = await stellify.createStatementWithCode(args);
2020
+ const statementData = result.data || result;
2021
+ return {
2022
+ content: [
2023
+ {
2024
+ type: 'text',
2025
+ text: JSON.stringify({
2026
+ success: true,
2027
+ message: `Created statement with code (UUID: ${statementData.uuid})`,
2028
+ statement: statementData.statement || statementData,
2029
+ statements: statementData.statements,
2030
+ clauses: statementData.clauses,
2031
+ }, null, 2),
2032
+ },
2033
+ ],
2034
+ };
2035
+ }
2033
2036
  case 'add_statement_code': {
2034
2037
  const result = await stellify.addStatementCode(args);
2035
2038
  return {
@@ -17,11 +17,16 @@ export interface CreateMethodParams {
17
17
  name: string;
18
18
  visibility?: 'public' | 'protected' | 'private';
19
19
  is_static?: boolean;
20
+ is_async?: boolean;
20
21
  returnType?: string;
22
+ nullable?: boolean;
21
23
  parameters?: Array<{
22
24
  name: string;
23
- type: string;
25
+ type?: string;
26
+ datatype?: string;
27
+ value?: string;
24
28
  }>;
29
+ body?: string;
25
30
  }
26
31
  export interface AddMethodBodyParams {
27
32
  file: string;
@@ -84,6 +89,11 @@ export declare class StellifyClient {
84
89
  file?: string;
85
90
  method?: string;
86
91
  }): Promise<any>;
92
+ createStatementWithCode(params: {
93
+ file: string;
94
+ code: string;
95
+ method?: string;
96
+ }): Promise<any>;
87
97
  getStatement(statement: string): Promise<any>;
88
98
  deleteStatement(file: string, method: string, statement: string): Promise<any>;
89
99
  saveStatement(statement: string, data: any): Promise<any>;
@@ -110,6 +120,7 @@ export declare class StellifyClient {
110
120
  elements: string;
111
121
  page?: string;
112
122
  selection?: string;
123
+ file?: string;
113
124
  test?: boolean;
114
125
  }): Promise<any>;
115
126
  getDirectory(uuid: string): Promise<any>;
@@ -63,6 +63,11 @@ export class StellifyClient {
63
63
  const response = await this.client.post('/statement', params);
64
64
  return response.data;
65
65
  }
66
+ // NEW: Combined create statement with code in a single call
67
+ async createStatementWithCode(params) {
68
+ const response = await this.client.post('/statement/with-code', params);
69
+ return response.data;
70
+ }
66
71
  async getStatement(statement) {
67
72
  const response = await this.client.get(`/statement/${statement}`);
68
73
  return response.data;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stellisoft/stellify-mcp",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
4
4
  "mcpName": "io.github.MattStellisoft/stellify-mcp",
5
5
  "description": "MCP server for Stellify - AI-native code generation platform",
6
6
  "main": "dist/index.js",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/Stellify-Software-Ltd/stellify-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.1.25",
9
+ "version": "0.1.27",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@stellisoft/stellify-mcp",
14
- "version": "0.1.25",
14
+ "version": "0.1.27",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },