@stellisoft/stellify-mcp 0.1.23 → 0.1.25

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
@@ -294,12 +294,13 @@ IMPORTANT: This APPENDS to existing method statements. To REPLACE a method's cod
294
294
  },
295
295
  {
296
296
  name: 'save_method',
297
- description: `Update an existing method's properties (name, visibility, returnType, nullable, parameters, data).
297
+ description: `Update an existing method's properties (name, visibility, returnType, nullable, parameters, data, is_async).
298
298
 
299
299
  Use this to modify a method after creation. For updating the method body, use add_method_body instead.
300
300
 
301
301
  Parameters:
302
302
  - data: Array of statement UUIDs that form the method body. Use this to reorder statements or remove unwanted statements from the method.
303
+ - is_async: Set to true for JavaScript/Vue methods that use await.
303
304
 
304
305
  Example - Update return type:
305
306
  {
@@ -308,6 +309,12 @@ Example - Update return type:
308
309
  "nullable": true
309
310
  }
310
311
 
312
+ Example - Mark method as async (for methods using await):
313
+ {
314
+ "uuid": "method-uuid",
315
+ "is_async": true
316
+ }
317
+
311
318
  Example - Remove duplicate/unwanted statements:
312
319
  {
313
320
  "uuid": "method-uuid",
@@ -351,6 +358,10 @@ Example - Remove duplicate/unwanted statements:
351
358
  description: 'Array of statement UUIDs that form the method body. Use to reorder or remove statements.',
352
359
  items: { type: 'string' },
353
360
  },
361
+ is_async: {
362
+ type: 'boolean',
363
+ description: 'Whether the method is async (JavaScript/Vue only). Set to true for methods that use await.',
364
+ },
354
365
  },
355
366
  required: ['uuid'],
356
367
  },
@@ -374,16 +385,20 @@ Example - Remove duplicate/unwanted statements:
374
385
  },
375
386
  {
376
387
  name: 'delete_method',
377
- description: 'Delete a method from a file by UUID. This permanently removes the method and all its code.',
388
+ description: 'Delete a method from a file by UUID. This permanently removes the method and all its code. Requires both the file UUID and method UUID.',
378
389
  inputSchema: {
379
390
  type: 'object',
380
391
  properties: {
392
+ file: {
393
+ type: 'string',
394
+ description: 'UUID of the file containing the method (required)',
395
+ },
381
396
  uuid: {
382
397
  type: 'string',
383
398
  description: 'UUID of the method to delete',
384
399
  },
385
400
  },
386
- required: ['uuid'],
401
+ required: ['file', 'uuid'],
387
402
  },
388
403
  },
389
404
  {
@@ -841,12 +856,20 @@ Examples:
841
856
  inputSchema: {
842
857
  type: 'object',
843
858
  properties: {
859
+ file: {
860
+ type: 'string',
861
+ description: 'UUID of the file containing the statement',
862
+ },
863
+ method: {
864
+ type: 'string',
865
+ description: 'UUID of the method containing the statement (use "file" for file-level statements)',
866
+ },
844
867
  uuid: {
845
868
  type: 'string',
846
869
  description: 'UUID of the statement to delete',
847
870
  },
848
871
  },
849
- required: ['uuid'],
872
+ required: ['file', 'method', 'uuid'],
850
873
  },
851
874
  },
852
875
  {
@@ -895,7 +918,12 @@ Vue SFC example:
895
918
  statements: [importStmtUuid, refStmtUuid] // Statement UUIDs (imports, refs)
896
919
  })
897
920
 
898
- For <script setup> content, the order in statements array determines output order.`,
921
+ For <script setup> content, the order in statements array determines output order.
922
+
923
+ DEPENDENCY RESOLUTION (includes array):
924
+ The 'includes' array accepts BOTH UUIDs and namespace strings.
925
+ Namespace strings (e.g., "Illuminate\\Http\\JsonResponse") are automatically resolved to UUIDs.
926
+ This works the same as create_file's dependency resolution.`,
899
927
  inputSchema: {
900
928
  type: 'object',
901
929
  properties: {
@@ -934,7 +962,7 @@ For <script setup> content, the order in statements array determines output orde
934
962
  includes: {
935
963
  type: 'array',
936
964
  items: { type: 'string' },
937
- description: 'Array of file UUIDs to import',
965
+ description: 'Array of file UUIDs OR namespace strings to import. Namespace strings (e.g., "Illuminate\\\\Http\\\\JsonResponse") are auto-resolved to UUIDs.',
938
966
  },
939
967
  models: {
940
968
  type: 'array',
@@ -967,12 +995,16 @@ WARNING: This is destructive and cannot be undone. Make sure the file is not ref
967
995
  inputSchema: {
968
996
  type: 'object',
969
997
  properties: {
998
+ directory: {
999
+ type: 'string',
1000
+ description: 'UUID of the directory containing the file (get from get_project directories array or get_file response)',
1001
+ },
970
1002
  uuid: {
971
1003
  type: 'string',
972
1004
  description: 'UUID of the file to delete',
973
1005
  },
974
1006
  },
975
- required: ['uuid'],
1007
+ required: ['directory', 'uuid'],
976
1008
  },
977
1009
  },
978
1010
  {
@@ -1078,6 +1110,8 @@ Creates: Model ($fillable, $casts, relationships), Controller (CRUD actions), Se
1078
1110
 
1079
1111
  IMPORTANT: Routes are NOT auto-wired. After creation, use create_route with the returned controller UUID and method UUIDs.
1080
1112
 
1113
+ IMPORTANT: After creation, check the controller methods for return types and parameter types. If methods use classes not already in includes (e.g., JsonResponse), add those class UUIDs to the controller's includes via save_file.
1114
+
1081
1115
  Response includes controller.methods array with {uuid, name} for each action (index, store, update, destroy).`,
1082
1116
  inputSchema: {
1083
1117
  type: 'object',
@@ -1396,18 +1430,30 @@ Key concepts:
1396
1430
 
1397
1431
  1. get_project → find 'js' directory UUID (or create it)
1398
1432
  2. create_file → type='js', extension='vue' for the component
1399
- 3. Create statements for imports:
1400
- - "import { ref } from 'vue';" (REQUIRED for ref())
1401
- - "import { Http, List } from 'stellify-framework';" (for Stellify classes)
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)
1402
1440
  NOTE: The npm package is "stellify-framework" (NOT @stellify/core)
1403
- 4. Create statements for data: "const count = ref(0);"
1404
- 5. create_method + add_method_body functions
1405
- 6. html_to_elements → template (no 'page' param for components)
1406
- 7. update_element wire click handlers to method UUIDs
1407
- 8. save_file with: extension='vue', template=[elementUuid], data=[methodUuids], statements=[importUuids, refUuids]
1408
- 9. **MANDATORY: Create app.js entry file** (see "CRITICAL: JavaScript Entry File" section below)
1409
- 10. Create web route for the page
1410
- 11. **MANDATORY: Add a div with id="app"** to the page using html_to_elements:
1441
+ 5. Create statements for reactive data: "const notes = ref([]);"
1442
+ 6. create_method + add_method_body for functions. Example fetchNotes body:
1443
+ \`\`\`
1444
+ const response = await Http.get('/api/notes');
1445
+ notes.value = response.data || [];
1446
+ \`\`\`
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:
1411
1457
  - html_to_elements with page=routeUuid and elements='<div id="app"></div>'
1412
1458
  - This is where Vue mounts the component. Without it, nothing renders!
1413
1459
 
@@ -1416,8 +1462,33 @@ Key concepts:
1416
1462
  Use for SHOW/DISPLAY/DEMONSTRATE requests - sends instant WebSocket updates to browser.
1417
1463
  Use html_to_elements/update_element for permanent/saved changes.
1418
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.
1470
+
1471
+ Laravel pagination returns: \`{ data: [...items], current_page: 1, per_page: 15, ... }\`
1472
+
1473
+ Since Http.get() returns this JSON directly (no axios wrapper), you access the array with ONE \`.data\`:
1474
+
1475
+ \`\`\`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 || [];
1479
+
1480
+ // WRONG - Double .data (axios habit) - returns undefined, notes stay empty!
1481
+ const response = await Http.get('/api/notes');
1482
+ notes.value = response.data.data || []; // BUG: response.data.data is undefined
1483
+ \`\`\`
1484
+
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
+
1419
1489
  ## Common Pitfalls
1420
1490
 
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.
1421
1492
  - **Stellify imports:** Use "stellify-framework" package (NOT @stellify/core)
1422
1493
  CORRECT: import { Http, List, Form } from 'stellify-framework';
1423
1494
  WRONG: import { Http } from '@stellify/core';
@@ -1426,6 +1497,44 @@ Use html_to_elements/update_element for permanent/saved changes.
1426
1497
  - add_method_body APPENDS, doesn't replace - create new method to replace
1427
1498
  - 'data' array = method UUIDs, 'statements' array = import/variable UUIDs
1428
1499
  - 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
1501
+ - **onMounted:** Use direct method reference: "onMounted(fetchNotes);"
1502
+ The assembler automatically outputs lifecycle hooks after method definitions.
1503
+
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;\`
1429
1538
 
1430
1539
  ## Framework Capabilities (Libraries/Packages)
1431
1540
 
@@ -1468,25 +1577,24 @@ Modules are auto-created if they don't exist. This helps users see all code rela
1468
1577
  **You MUST have a JS entry file to register Vue components for use on pages.**
1469
1578
 
1470
1579
  ### First component in a project:
1471
- 1. Create app.js in the 'js' directory:
1472
- - create_file with type='js', extension='js', name='app'
1473
- 2. Add statements:
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:
1474
1583
  - "import { createApp } from 'vue';"
1475
- - "import NotesApp from './NotesApp.vue';"
1476
1584
  - "createApp(NotesApp).mount('#app');"
1477
- 3. save_file with all statement UUIDs
1585
+ NOTE: The component import (NotesApp) is handled by the includes array, NOT a statement!
1586
+ 3. save_file with statement UUIDs
1478
1587
 
1479
1588
  ### Adding more components (app.js already exists):
1480
1589
  1. **search_files** for "app" to find existing app.js
1481
- 2. **get_file** to retrieve current statements
1482
- 3. Add ONLY the new import statement:
1483
- - "import Counter from './Counter.vue';"
1590
+ 2. **get_file** to retrieve current includes and statements
1591
+ 3. Add the new component UUID to the includes array via save_file
1484
1592
  4. Update the mount code to register the new component:
1485
1593
  - Create new statement: "app.component('Counter', Counter);"
1486
- - Or recreate the initialization to include all components
1487
- 5. save_file with updated statements array
1594
+ 5. save_file with updated includes and statements arrays
1488
1595
 
1489
1596
  **DO NOT create duplicate app.js files or duplicate createApp imports!**
1597
+ **DO NOT use statements for file imports - use the includes array!**
1490
1598
 
1491
1599
  ### Page mount point (div#app):
1492
1600
  Each page that uses Vue components needs a \`<div id="app"></div>\`.
@@ -1496,17 +1604,13 @@ Each page that uses Vue components needs a \`<div id="app"></div>\`.
1496
1604
 
1497
1605
  **Without app.js AND div#app, Vue components will NOT render!**
1498
1606
 
1499
- Example app.js with multiple components:
1500
- \`\`\`javascript
1501
- import { createApp } from 'vue';
1502
- import NotesApp from './NotesApp.vue';
1503
- import Counter from './Counter.vue';
1504
-
1505
- const app = createApp({});
1506
- app.component('NotesApp', NotesApp);
1507
- app.component('Counter', Counter);
1508
- app.mount('#app');
1509
- \`\`\`
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');"]
1510
1614
 
1511
1615
  ## Efficiency Tips
1512
1616
 
@@ -1517,7 +1621,7 @@ app.mount('#app');
1517
1621
  // Create MCP server
1518
1622
  const server = new Server({
1519
1623
  name: 'stellify-mcp',
1520
- version: '0.1.23',
1624
+ version: '0.1.25',
1521
1625
  }, {
1522
1626
  capabilities: {
1523
1627
  tools: {},
@@ -1655,15 +1759,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1655
1759
  };
1656
1760
  }
1657
1761
  case 'delete_method': {
1658
- const { uuid } = args;
1659
- const result = await stellify.deleteMethod(uuid);
1762
+ const { file, uuid } = args;
1763
+ const result = await stellify.deleteMethod(file, uuid);
1660
1764
  return {
1661
1765
  content: [
1662
1766
  {
1663
1767
  type: 'text',
1664
1768
  text: JSON.stringify({
1665
1769
  success: true,
1666
- message: `Deleted method ${uuid}`,
1770
+ message: `Deleted method ${uuid} from file ${file}`,
1667
1771
  data: result,
1668
1772
  }, null, 2),
1669
1773
  },
@@ -1942,8 +2046,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1942
2046
  };
1943
2047
  }
1944
2048
  case 'delete_statement': {
1945
- const { uuid } = args;
1946
- const result = await stellify.deleteStatement(uuid);
2049
+ const { file, method, uuid } = args;
2050
+ const result = await stellify.deleteStatement(file, method, uuid);
1947
2051
  return {
1948
2052
  content: [
1949
2053
  {
@@ -2005,15 +2109,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2005
2109
  };
2006
2110
  }
2007
2111
  case 'delete_file': {
2008
- const { uuid } = args;
2009
- const result = await stellify.deleteFile(uuid);
2112
+ const { directory, uuid } = args;
2113
+ const result = await stellify.deleteFile(directory, uuid);
2010
2114
  return {
2011
2115
  content: [
2012
2116
  {
2013
2117
  type: 'text',
2014
2118
  text: JSON.stringify({
2015
2119
  success: true,
2016
- message: `Deleted file ${uuid}`,
2120
+ message: `Deleted file ${uuid} from directory ${directory}`,
2017
2121
  data: result,
2018
2122
  }, null, 2),
2019
2123
  },
@@ -76,16 +76,16 @@ export declare class StellifyClient {
76
76
  searchFiles(params: SearchFilesParams): Promise<any>;
77
77
  getFile(file: string): Promise<any>;
78
78
  saveFile(file: string, data: any): Promise<any>;
79
- deleteFile(file: string): Promise<any>;
79
+ deleteFile(directory: string, file: string): Promise<any>;
80
80
  getMethod(method: string): Promise<any>;
81
81
  saveMethod(method: string, data: any): Promise<any>;
82
- deleteMethod(method: string): Promise<any>;
82
+ deleteMethod(file: string, method: string): Promise<any>;
83
83
  createStatement(params: {
84
84
  file?: string;
85
85
  method?: string;
86
86
  }): Promise<any>;
87
87
  getStatement(statement: string): Promise<any>;
88
- deleteStatement(statement: string): Promise<any>;
88
+ deleteStatement(file: string, method: string, statement: string): Promise<any>;
89
89
  saveStatement(statement: string, data: any): Promise<any>;
90
90
  createRoute(params: CreateRouteParams): Promise<any>;
91
91
  getRoute(route: string): Promise<any>;
@@ -43,8 +43,8 @@ export class StellifyClient {
43
43
  const response = await this.client.put(`/file/${file}`, data);
44
44
  return response.data;
45
45
  }
46
- async deleteFile(file) {
47
- const response = await this.client.delete(`/file/${file}`);
46
+ async deleteFile(directory, file) {
47
+ const response = await this.client.delete(`/file/${directory}/${file}`);
48
48
  return response.data;
49
49
  }
50
50
  async getMethod(method) {
@@ -55,8 +55,8 @@ export class StellifyClient {
55
55
  const response = await this.client.put(`/method/${method}`, data);
56
56
  return response.data;
57
57
  }
58
- async deleteMethod(method) {
59
- const response = await this.client.delete(`/method/${method}`);
58
+ async deleteMethod(file, method) {
59
+ const response = await this.client.delete(`/method/${file}/${method}`);
60
60
  return response.data;
61
61
  }
62
62
  async createStatement(params) {
@@ -67,8 +67,8 @@ export class StellifyClient {
67
67
  const response = await this.client.get(`/statement/${statement}`);
68
68
  return response.data;
69
69
  }
70
- async deleteStatement(statement) {
71
- const response = await this.client.delete(`/statement/${statement}`);
70
+ async deleteStatement(file, method, statement) {
71
+ const response = await this.client.delete(`/statement/${file}/${method}/${statement}`);
72
72
  return response.data;
73
73
  }
74
74
  async saveStatement(statement, data) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stellisoft/stellify-mcp",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
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.23",
9
+ "version": "0.1.25",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@stellisoft/stellify-mcp",
14
- "version": "0.1.23",
14
+ "version": "0.1.25",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },