@xano/developer-mcp 1.0.5 → 1.0.7

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
@@ -47,6 +47,11 @@ const XANOSCRIPT_DOCS_V2 = {
47
47
  applyTo: ["tasks/*.xs"],
48
48
  description: "Scheduled and cron jobs",
49
49
  },
50
+ triggers: {
51
+ file: "triggers.md",
52
+ applyTo: ["triggers/**/*.xs"],
53
+ description: "Event-driven handlers (table, realtime, workspace, agent, MCP)",
54
+ },
50
55
  database: {
51
56
  file: "database.md",
52
57
  applyTo: ["functions/**/*.xs", "apis/**/*.xs", "tasks/*.xs", "tools/**/*.xs"],
@@ -22,6 +22,7 @@ ${formatRow("function", "Custom reusable functions in `functions/`")}
22
22
  ${formatRow("api_query", "HTTP API endpoints in `apis/`")}
23
23
  ${formatRow("table", "Database table schemas in `tables/`")}
24
24
  ${formatRow("task", "Scheduled background tasks in `tasks/`")}
25
+ ${formatRow("triggers", "Event-driven handlers in `triggers/`")}
25
26
  ${formatRow("tool", "AI-callable tools in `tools/`")}
26
27
  ${formatRow("agent", "AI agents in `agents/`")}
27
28
  ${formatRow("mcp_server", "MCP servers in `mcp_servers/`")}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xano/developer-mcp",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "MCP server for Xano Headless API documentation and XanoScript code validation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -10,26 +10,30 @@ XanoScript is the declarative scripting language for [Xano](https://xano.com), a
10
10
  | `function` | `functions/**/*.xs` | Reusable logic blocks |
11
11
  | `query` | `apis/<group>/*.xs` | HTTP API endpoints |
12
12
  | `task` | `tasks/*.xs` | Scheduled/cron jobs |
13
+ | `*_trigger` | `triggers/**/*.xs` | Event-driven handlers |
13
14
  | `agent` | `agents/**/*.xs` | AI-powered agents |
14
15
  | `tool` | `tools/**/*.xs` | Tools for AI agents |
15
16
  | `mcp_server` | `mcp_servers/**/*.xs` | MCP server definitions |
16
17
  | `addon` | `addons/*.xs` | Subqueries for related data |
17
18
 
19
+ **Important:** Each `.xs` file must contain exactly one definition. You cannot define multiple tables, functions, queries, or other constructs in a single file.
20
+
18
21
  ## Workspace Structure
19
22
 
20
23
  ```
21
24
  project/
22
- ├── tables/ # Database table schemas
23
- ├── functions/ # Reusable functions (supports subfolders)
25
+ ├── tables/ // Database table schemas
26
+ ├── functions/ // Reusable functions (supports subfolders)
24
27
  ├── apis/
25
- │ └── <api-group>/ # API endpoints grouped by domain
26
- ├── tasks/ # Scheduled jobs
27
- ├── agents/ # AI agents
28
- ├── tools/ # AI tools
29
- ├── mcp_servers/ # MCP server definitions
30
- ├── addons/ # Query addons
31
- ├── static/ # Frontend files (HTML, CSS, JS)
32
- └── ephemeral/ # Temporary test environments
28
+ │ └── <api-group>/ // API endpoints grouped by domain
29
+ ├── tasks/ // Scheduled jobs
30
+ ├── triggers/ // Event-driven handlers
31
+ ├── agents/ // AI agents
32
+ ├── tools/ // AI tools
33
+ ├── mcp_servers/ // MCP server definitions
34
+ ├── addons/ // Query addons
35
+ ├── static/ // Frontend files (HTML, CSS, JS)
36
+ └── ephemeral/ // Temporary test environments
33
37
  ```
34
38
 
35
39
  ## Environment Variables
@@ -53,28 +57,36 @@ Custom environment variables are set in the Xano dashboard and accessed as `$env
53
57
  ### Block Structure
54
58
  ```xs
55
59
  <construct> "<name>" {
56
- input { ... } # Parameters (optional)
57
- stack { ... } # Logic
58
- response = $var # Output
60
+ input { ... } // Parameters (optional)
61
+ stack { ... } // Logic
62
+ response = $var // Output
59
63
  }
60
64
  ```
61
65
 
62
66
  ### Variable Access
63
67
  ```xs
64
- $input.field # Input parameters
65
- $var.field # Stack variables
66
- $auth.id # Authenticated user ID
67
- $env.MY_VAR # Environment variable
68
- $db.table.field # Database field reference (in queries)
69
- $this # Current item in loops/maps
68
+ $input.field // Input parameters
69
+ $var.field // Stack variables
70
+ $auth.id // Authenticated user ID
71
+ $env.MY_VAR // Environment variable
72
+ $db.table.field // Database field reference (in queries)
73
+ $this // Current item in loops/maps
70
74
  ```
71
75
 
76
+ ### Comments
77
+ ```xs
78
+ // Single-line comment
79
+ var $total { value = 0 } // Inline comment
80
+ ```
81
+
82
+ **Note:** XanoScript only supports `//` for comments. Other comment styles like `#` are not supported.
83
+
72
84
  ### Filters (Pipe Syntax)
73
85
  ```xs
74
- $value|trim|lower # Chain filters
75
- $input.name|strlen # Get length
76
- $array|first # First element
77
- ($a + $b)|round:2 # Math with precision
86
+ $value|trim|lower // Chain filters
87
+ $input.name|strlen // Get length
88
+ $array|first // First element
89
+ ($a + $b)|round:2 // Math with precision
78
90
  ```
79
91
 
80
92
  ## File Frontmatter
@@ -91,17 +103,21 @@ This helps AI tools apply the correct documentation based on the file being edit
91
103
 
92
104
  ## Documentation Index
93
105
 
94
- 1. [Syntax Reference](syntax.md) - Expressions, operators, filters
95
- 2. [Types & Inputs](types.md) - Data types, validation, input blocks
96
- 3. [Tables](tables.md) - Database schema definitions
97
- 4. [Functions](functions.md) - Reusable function stacks
98
- 5. [APIs](apis.md) - HTTP endpoint definitions
99
- 6. [Tasks](tasks.md) - Scheduled jobs
100
- 7. [Database Operations](database.md) - Query, add, edit, delete
101
- 8. [Agents](agents.md) - AI agent configuration
102
- 9. [Tools](tools.md) - AI tools for agents
103
- 10. [MCP Servers](mcp-servers.md) - Model Context Protocol servers
104
- 11. [Testing](testing.md) - Unit tests and mocking
105
- 12. [Integrations](integrations.md) - Cloud services, Redis, security
106
- 13. [Frontend](frontend.md) - Static frontend development
107
- 14. [Ephemeral](ephemeral.md) - Temporary environments
106
+ Use `xanoscript_docs({ keyword: "<keyword>" })` to retrieve documentation.
107
+
108
+ | Topic | Keyword | Description |
109
+ |-------|---------|-------------|
110
+ | Syntax Reference | `syntax` | Expressions, operators, filters |
111
+ | Types & Inputs | `input` | Data types, validation, input blocks |
112
+ | Tables | `table` | Database schema definitions |
113
+ | Functions | `function` | Reusable function stacks |
114
+ | APIs | `api_query` | HTTP endpoint definitions |
115
+ | Tasks | `task` | Scheduled jobs |
116
+ | Triggers | `trigger` | Event-driven handlers (table, realtime, workspace, agent, MCP) |
117
+ | Database Operations | `db_query` | Query, add, edit, delete |
118
+ | Agents | `agent` | AI agent configuration |
119
+ | Tools | `tool` | AI tools for agents |
120
+ | MCP Servers | `mcp_server` | Model Context Protocol servers |
121
+ | Testing | `testing` | Unit tests and mocking |
122
+ | Frontend | `frontend` | Static frontend development |
123
+ | Ephemeral | `ephemeral` | Temporary environments
@@ -71,10 +71,10 @@ ai.agent.run "Customer Support" {
71
71
 
72
72
  ```xs
73
73
  llm = {
74
- type: "<provider>" # Required
75
- system_prompt: "..." # Agent persona and rules
76
- prompt: "{{ $args.input }}" # User input template
77
- max_steps: 5 # Max LLM calls per run
74
+ type: "<provider>" // Required
75
+ system_prompt: "..." // Agent persona and rules
76
+ prompt: "{{ $args.input }}" // User input template
77
+ max_steps: 5 // Max LLM calls per run
78
78
  }
79
79
  ```
80
80
 
@@ -94,7 +94,7 @@ llm = {
94
94
  prompt: "{{ $args.message }}"
95
95
  max_steps: 3
96
96
  temperature: 0
97
- search_grounding: false # Google Search grounding
97
+ search_grounding: false // Google Search grounding
98
98
  }
99
99
  ```
100
100
 
@@ -108,7 +108,7 @@ llm = {
108
108
  prompt: "{{ $args.message }}"
109
109
  max_steps: 5
110
110
  temperature: 0.2
111
- thinking_tokens: 10000 # Extended thinking
111
+ thinking_tokens: 10000 // Extended thinking
112
112
  include_thoughts: true
113
113
  }
114
114
  ```
@@ -123,7 +123,7 @@ llm = {
123
123
  prompt: "{{ $args.message }}"
124
124
  max_steps: 5
125
125
  temperature: 0.8
126
- reasoning_effort: "medium" # low, medium, high
126
+ reasoning_effort: "medium" // low, medium, high
127
127
  }
128
128
  ```
129
129
 
@@ -134,7 +134,7 @@ llm = {
134
134
  api_key: "{{ $env.GROQ_API_KEY }}"
135
135
  baseURL: "https://api.groq.com/openai/v1"
136
136
  model: "llama-3.3-70b-versatile"
137
- compatibility: "compatible" # Required for non-OpenAI
137
+ compatibility: "compatible" // Required for non-OpenAI
138
138
  ...
139
139
  }
140
140
  ```
@@ -151,7 +151,7 @@ llm = {
151
151
  prompt: "{{ $args.message }}"
152
152
  max_steps: 8
153
153
  temperature: 0.3
154
- send_reasoning: true # Include thinking blocks
154
+ send_reasoning: true // Include thinking blocks
155
155
  }
156
156
  ```
157
157
 
@@ -218,9 +218,9 @@ llm = {
218
218
 
219
219
  ### Available Variables
220
220
  ```xs
221
- {{ $args.any_arg }} # Runtime arguments
222
- {{ $env.MY_VAR }} # Environment variables
223
- {{ "now"|date("Y-m-d") }} # Current date
221
+ {{ $args.any_arg }} // Runtime arguments
222
+ {{ $env.MY_VAR }} // Environment variables
223
+ {{ "now"|date("Y-m-d") }} // Current date
224
224
  ```
225
225
 
226
226
  ---
@@ -324,6 +324,4 @@ agent "Research Assistant" {
324
324
  3. **Limit max_steps** - Prevent infinite loops (3-10 typical)
325
325
  4. **Don't repeat tool descriptions** - They're auto-injected
326
326
  5. **Use environment variables** - Never hardcode API keys
327
- 6. **Keep prompts focused** - One task per agent
328
- 7. **Test with xano-free first** - Free for development
329
- 8. **Use structured outputs** - When you need consistent JSON
327
+ 6. **Test with xano-free first** - Free for development
@@ -9,19 +9,19 @@ HTTP endpoint definitions in XanoScript.
9
9
  ## Quick Reference
10
10
 
11
11
  ```xs
12
- query "<name>" verb=<METHOD> {
13
- api_group = "<GroupName>" # Required: API group for organization
12
+ query "endpoint-path" verb=<METHOD> {
13
+ api_group = "<GroupName>" // Required: API group for organization
14
14
  description = "What this endpoint does"
15
- auth = "<table>" # Optional: require authentication
15
+ auth = "<table>" // Optional: require authentication
16
16
  input { ... }
17
17
  stack { ... }
18
18
  response = $result
19
19
  }
20
20
  ```
21
21
 
22
- ### Query Name (Required)
22
+ ### Query Name (Required, Non-Empty)
23
23
 
24
- The query name is **required** and cannot be empty. It defines the endpoint path after the API group canonical.
24
+ The query name is **required** and **must be a non-empty string**. Empty names (`query "" verb=...`) are invalid. The name defines the endpoint path after the API group canonical.
25
25
 
26
26
  **Full URL path structure:**
27
27
  ```
@@ -38,61 +38,28 @@ The query name can include slashes for nested paths:
38
38
 
39
39
  ### API Groups (Required)
40
40
 
41
- Every API endpoint **must** belong to an API group. API groups organize related endpoints together.
42
-
43
- **Two requirements for API groups:**
44
- 1. Each `query` definition **must** include an `api_group` property specifying which group it belongs to
45
- 2. API groups are organized as folders within `apis/`, each with an `api_group.xs` file
46
-
47
- - API groups appear as top-level folders under `apis/`
48
- - Each group **must** have an `api_group.xs` file that defines the group
49
- - The group contains `.xs` files defining individual endpoints
50
- - The group name becomes part of the endpoint URL path
51
- - You cannot create endpoints directly in the `apis/` root folder
52
-
53
- #### Defining an API Group
54
-
55
- Create an `api_group.xs` file in the group folder:
41
+ Every endpoint must belong to an API group. Each group is a folder with an `api_group.xs` file:
56
42
 
57
43
  ```xs
58
44
  api_group "users" {
59
- canonical = "users" # Required: the URL path segment
60
- description = "User management endpoints"
45
+ canonical = "users" // Required: URL path segment
46
+ description = "User management" // Optional
61
47
  }
62
48
  ```
63
49
 
64
- #### API Group Properties
65
-
66
- | Property | Required | Description |
67
- |----------|----------|-------------|
68
- | `canonical` | **Yes** | The URL path segment for this group. You cannot access a Xano API without it. |
69
- | `description` | No | What this API group contains |
70
-
71
- ```xs
72
- api_group "events" {
73
- canonical = "events-api" # Required: URL will use /events-api
74
- description = "Event management"
75
- }
76
- ```
77
-
78
- > **Important:** The `canonical` property is required. It defines the base path for all endpoints in this group.
79
-
80
50
  ### File Structure
81
51
  ```
82
52
  apis/
83
- ├── users/ # API group folder
84
- │ ├── api_group.xs # Required: defines the group (canonical = "users")
85
- │ ├── list.xs # GET /users/list
86
- ├── create.xs # POST /users/create
87
- └── by-id.xs # GET/PATCH/DELETE /users/{id}
88
- └── products/ # Another API group
89
- ├── api_group.xs # Required: defines the group (canonical = "products")
90
- └── search.xs # GET /products/search
53
+ ├── users/
54
+ │ ├── api_group.xs // Defines group (canonical = "users")
55
+ │ ├── list.xs // GET /users/list
56
+ └── by-id.xs // GET/PATCH/DELETE /users/{id}
57
+ └── products/
58
+ ├── api_group.xs // Defines group (canonical = "products")
59
+ └── search.xs // GET /products/search
91
60
  ```
92
61
 
93
- > **URL Path:** The full endpoint URL is always `/<canonical>/<query name>`. For example, if `api_group.xs` has `canonical = "users"` and a query has `query "profile" verb=GET`, the endpoint URL is `/users/profile`.
94
-
95
- > **Note:** Files placed directly in `apis/` without a group folder are invalid. Each API group folder must contain an `api_group.xs` file.
62
+ Full URL: `/<canonical>/<query name>` (e.g., `/users/profile`)
96
63
 
97
64
  ---
98
65
 
@@ -132,11 +99,11 @@ query "status" verb=GET {
132
99
  ```xs
133
100
  query "profile" verb=GET {
134
101
  api_group = "Users"
135
- auth = "user" # Requires valid JWT
102
+ auth = "user" // Requires valid JWT
136
103
  stack {
137
104
  db.get "user" {
138
105
  field_name = "id"
139
- field_value = $auth.id # User ID from token
106
+ field_value = $auth.id // User ID from token
140
107
  } as $user
141
108
  }
142
109
  response = $user
@@ -354,33 +321,11 @@ stack {
354
321
 
355
322
  ## Error Handling
356
323
 
357
- ### Preconditions
358
- ```xs
359
- stack {
360
- precondition ($input.amount > 0) {
361
- error_type = "inputerror"
362
- error = "Amount must be positive"
363
- }
364
-
365
- precondition ($user != null) {
366
- error_type = "notfound"
367
- error = "User not found"
368
- }
369
-
370
- precondition ($user.id == $auth.id) {
371
- error_type = "accessdenied"
372
- error = "Not authorized"
373
- }
374
- }
375
- ```
324
+ For complete error handling reference, use `xanoscript_docs({ keyword: "syntax" })`.
376
325
 
377
- ### Error Types
378
326
  | Type | HTTP Status |
379
327
  |------|-------------|
380
- | `inputerror` | 400 Bad Request |
381
- | `accessdenied` | 403 Forbidden |
382
- | `notfound` | 404 Not Found |
383
- | `standard` | 500 Internal Server Error |
328
+ | `inputerror` | 400 | `accessdenied` | 403 | `notfound` | 404 | `standard` | 500 |
384
329
 
385
330
  ---
386
331
 
@@ -404,13 +349,8 @@ When using `return = { type: "list", paging: {...} }`:
404
349
 
405
350
  ## Best Practices
406
351
 
407
- 1. **Always set canonical** - Every `api_group` requires a `canonical` property
408
- 2. **Always name queries** - Every `query` requires a non-empty name
409
- 3. **RESTful design** - Use appropriate HTTP methods
410
- 4. **Consistent naming** - Use lowercase, hyphens for multi-word paths
411
- 5. **Authenticate sensitive operations** - Always auth for writes
412
- 6. **Validate inputs** - Use filters and preconditions
413
- 7. **Return appropriate errors** - Use correct error types
414
- 8. **Paginate lists** - Never return unbounded lists
415
- 9. **Document with description** - Explain what endpoint does
416
- 10. **Group related endpoints** - Organize by resource in api groups
352
+ 1. **RESTful design** - Use appropriate HTTP methods for operations
353
+ 2. **Consistent naming** - Use lowercase, hyphens for multi-word paths
354
+ 3. **Authenticate writes** - Always require auth for POST/PATCH/DELETE
355
+ 4. **Paginate lists** - Never return unbounded result sets
356
+ 5. **Group by resource** - Organize endpoints in logical api groups
@@ -35,7 +35,7 @@ db.query "product" {
35
35
 
36
36
  ### Where Operators
37
37
  ```xs
38
- # Comparison
38
+ // Comparison
39
39
  $db.product.price == 100
40
40
  $db.product.price != 0
41
41
  $db.product.price > 50
@@ -43,32 +43,32 @@ $db.product.price >= 50
43
43
  $db.product.price < 100
44
44
  $db.product.price <= 100
45
45
 
46
- # Null-safe (ignore if value is null)
46
+ // Null-safe (ignore if value is null)
47
47
  $db.product.category ==? $input.category
48
48
  $db.product.price >=? $input.min_price
49
49
 
50
- # String matching
51
- $db.product.name includes "phone" # Contains substring
52
- $db.product.tags contains "featured" # Array contains value
50
+ // String matching
51
+ $db.product.name includes "phone" // Contains substring
52
+ $db.product.tags contains "featured" // Array contains value
53
53
  $db.product.tags not contains "hidden"
54
54
 
55
- # Array overlap
55
+ // Array overlap
56
56
  $db.product.tags overlaps ["a", "b"]
57
57
  $db.product.tags not overlaps ["x", "y"]
58
58
 
59
- # Combining conditions
59
+ // Combining conditions
60
60
  $db.product.is_active == true && $db.product.price > 0
61
61
  $db.product.category == "electronics" || $db.product.featured == true
62
62
  ```
63
63
 
64
64
  ### Return Types
65
65
  ```xs
66
- # List (default)
66
+ // List (default)
67
67
  db.query "product" {
68
68
  return = { type: "list" }
69
69
  } as $products
70
70
 
71
- # With pagination
71
+ // With pagination
72
72
  db.query "product" {
73
73
  return = {
74
74
  type: "list",
@@ -76,19 +76,19 @@ db.query "product" {
76
76
  }
77
77
  } as $products
78
78
 
79
- # Single record
79
+ // Single record
80
80
  db.query "product" {
81
81
  where = $db.product.sku == $input.sku
82
82
  return = { type: "single" }
83
83
  } as $product
84
84
 
85
- # Count
85
+ // Count
86
86
  db.query "product" {
87
87
  where = $db.product.is_active == true
88
88
  return = { type: "count" }
89
89
  } as $count
90
90
 
91
- # Exists
91
+ // Exists
92
92
  db.query "product" {
93
93
  where = $db.product.email == $input.email
94
94
  return = { type: "exists" }
@@ -98,9 +98,9 @@ db.query "product" {
98
98
  ### Sorting
99
99
  ```xs
100
100
  db.query "product" {
101
- sort = { created_at: "desc" } # Descending
102
- sort = { name: "asc" } # Ascending
103
- sort = { id: "rand" } # Random
101
+ sort = { created_at: "desc" } // Descending
102
+ sort = { name: "asc" } // Ascending
103
+ sort = { id: "rand" } // Random
104
104
  } as $products
105
105
  ```
106
106
 
@@ -110,7 +110,7 @@ db.query "comment" {
110
110
  join = {
111
111
  post: {
112
112
  table: "post",
113
- type: "inner", # inner, left, right
113
+ type: "inner", // inner, left, right
114
114
  where: $db.comment.post_id == $db.post.id
115
115
  }
116
116
  }
@@ -279,7 +279,7 @@ Delete all records from a table.
279
279
 
280
280
  ```xs
281
281
  db.truncate "temp_data" {
282
- reset = true # Reset auto-increment
282
+ reset = true // Reset auto-increment
283
283
  }
284
284
  ```
285
285
 
@@ -293,7 +293,7 @@ Execute raw SQL (use sparingly).
293
293
  db.direct_query {
294
294
  sql = "SELECT * FROM users WHERE email = ? AND status = ?"
295
295
  arg = [$input.email, "active"]
296
- response_type = "list" # list or single
296
+ response_type = "list" // list or single
297
297
  } as $results
298
298
  ```
299
299
 
@@ -339,9 +339,9 @@ Filters for use in `where` clauses:
339
339
 
340
340
  ### String/Text
341
341
  ```xs
342
- $db.name|to_lower # Case conversion
343
- $db.name|concat:" " # Concatenation
344
- $db.text|substr:0:100 # Substring
342
+ $db.name|to_lower // Case conversion
343
+ $db.name|concat:" " // Concatenation
344
+ $db.text|substr:0:100 // Substring
345
345
  ```
346
346
 
347
347
  ### Numeric
@@ -363,8 +363,8 @@ $db.created_at|timestamp_subtract_hours:24
363
363
 
364
364
  ### Geographic
365
365
  ```xs
366
- $db.location|distance:$input.point # Distance in meters
367
- $db.location|within:$input.point:1000 # Within radius
366
+ $db.location|distance:$input.point // Distance in meters
367
+ $db.location|within:$input.point:1000 // Within radius
368
368
  ```
369
369
 
370
370
  ### Vector (AI/ML)
@@ -385,7 +385,7 @@ $db.content|search_rank:$input.query
385
385
  Connect to external databases:
386
386
 
387
387
  ```xs
388
- # PostgreSQL
388
+ // PostgreSQL
389
389
  db.external.postgres.direct_query {
390
390
  connection_string = $env.EXTERNAL_PG_URL
391
391
  sql = "SELECT * FROM users WHERE id = ?"
@@ -393,13 +393,13 @@ db.external.postgres.direct_query {
393
393
  response_type = "single"
394
394
  } as $user
395
395
 
396
- # MySQL
396
+ // MySQL
397
397
  db.external.mysql.direct_query { ... }
398
398
 
399
- # MS SQL
399
+ // MS SQL
400
400
  db.external.mssql.direct_query { ... }
401
401
 
402
- # Oracle
402
+ // Oracle
403
403
  db.external.oracle.direct_query { ... }
404
404
  ```
405
405
 
@@ -411,7 +411,4 @@ db.external.oracle.direct_query { ... }
411
411
  2. **Use db.get for single lookups** - Simpler than db.query with single return
412
412
  3. **Use db.patch for dynamic updates** - Accepts variable data
413
413
  4. **Use transactions for atomicity** - Ensure all-or-nothing operations
414
- 5. **Add indexes** - Index fields used in where clauses
415
- 6. **Use null-safe operators** - `==?` for optional filters
416
- 7. **Paginate results** - Never return unbounded lists
417
- 8. **Validate before delete** - Check ownership/permissions
414
+ 5. **Use null-safe operators** - `==?` for optional filters
@@ -116,8 +116,8 @@ One-time operation with setup and cleanup hooks.
116
116
  ```xs
117
117
  function "$main" {
118
118
  input {
119
- json args # Runtime arguments
120
- json pre # Result from $pre
119
+ json args // Runtime arguments
120
+ json pre // Result from $pre
121
121
  }
122
122
  stack {
123
123
  db.query authors {
@@ -325,9 +325,6 @@ publish_ephemeral_service name="migration" directory="ephemeral/my-job" mode="jo
325
325
 
326
326
  1. **Keep isolated** - Self-contained with own tables/functions
327
327
  2. **Seed test data** - Use `items` in table definitions
328
- 3. **Use environment variables** - Store config in workspace `env`
329
- 4. **Handle cleanup** - Use `$post` for notifications/cleanup
330
- 5. **Validate in $pre** - Check preconditions before main logic
331
- 6. **Use preconditions** - Verify expected outcomes in jobs
332
- 7. **Name clearly** - Indicate purpose: `auth-test`, `data-migration`
333
- 8. **Set end dates** - Use `ends_on` for temporary schedules
328
+ 3. **Handle cleanup** - Use `$post` for notifications/cleanup
329
+ 4. **Validate in $pre** - Check preconditions before main logic
330
+ 5. **Name clearly** - Indicate purpose: `auth-test`, `data-migration`
@@ -11,10 +11,10 @@ Static frontend development and Lovable/Supabase migration.
11
11
  ### Directory Structure
12
12
  ```
13
13
  static/
14
- ├── index.html # Main entry point
14
+ ├── index.html // Main entry point
15
15
  ├── css/
16
16
  ├── js/
17
- │ └── api.js # Centralized API calls
17
+ │ └── api.js // Centralized API calls
18
18
  └── assets/
19
19
  ```
20
20
 
@@ -284,8 +284,5 @@ The tool will:
284
284
 
285
285
  1. **Get API specs first** - Use `get_xano_api_specifications` before coding
286
286
  2. **Centralize API calls** - Single `api.js` file
287
- 3. **Handle errors** - Show user-friendly messages
288
- 4. **Store tokens securely** - localStorage for web, secure storage for mobile
289
- 5. **Validate before submit** - Client-side validation before API calls
290
- 6. **Use loading states** - Show progress during API calls
291
- 7. **Test incrementally** - Migrate one feature at a time
287
+ 3. **Store tokens securely** - localStorage for web, secure storage for mobile
288
+ 4. **Test incrementally** - Migrate one feature at a time