@xano/developer-mcp 1.0.46 → 1.0.48

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.
@@ -33,21 +33,46 @@ conditional {
33
33
 
34
34
  > **Note:** Use `elseif` (one word), not `else if`.
35
35
 
36
+ ## Switch
37
+
38
+ ```xs
39
+ switch ($input.status) {
40
+ case ("active") {
41
+ var $message { value = "User is active" }
42
+ } break
43
+ case ("pending") {
44
+ var $message { value = "User is pending" }
45
+ } break
46
+ default {
47
+ var $message { value = "Unknown status" }
48
+ }
49
+ }
50
+ ```
51
+
52
+ > **Note:** `break` goes **after** the closing `}` of each `case` block. The `default` case does not need `break`.
53
+
36
54
  ## Loops
37
55
 
38
56
  ```xs
39
57
  // For each loop
40
- each ($input.items as $item) {
41
- debug.log { value = $item.name }
58
+ foreach ($input.items) {
59
+ each as $item {
60
+ debug.log { value = $item.name }
61
+ }
42
62
  }
43
63
 
44
- // While loop (must be inside stack block)
45
- stack {
46
- var $counter { value = 0 }
47
- while ($counter < 10) {
48
- each {
49
- var.update $counter { value = $counter + 1 }
50
- }
64
+ // For loop (iterate N times)
65
+ for (10) {
66
+ each as $index {
67
+ debug.log { value = $index }
68
+ }
69
+ }
70
+
71
+ // While loop
72
+ var $counter { value = 0 }
73
+ while ($counter < 10) {
74
+ each {
75
+ var.update $counter { value = $counter + 1 }
51
76
  }
52
77
  }
53
78
 
@@ -149,25 +149,29 @@ var $content_type { value = $api_result.response.headers|first }
149
149
  var $retries { value = 0 }
150
150
  var $success { value = false }
151
151
 
152
- while ($retries < 3 && $success == false) {
153
- try_catch {
154
- try {
155
- api.request {
156
- url = "https://api.example.com/data"
157
- method = "GET"
158
- timeout = 10
159
- } as $api_result
160
-
161
- conditional {
162
- if ($api_result.response.status == 200) {
163
- var.update $success { value = true }
152
+ stack {
153
+ while ($retries < 3 && $success == false) {
154
+ each {
155
+ try_catch {
156
+ try {
157
+ api.request {
158
+ url = "https://api.example.com/data"
159
+ method = "GET"
160
+ timeout = 10
161
+ } as $api_result
162
+
163
+ conditional {
164
+ if ($api_result.response.status == 200) {
165
+ var.update $success { value = true }
166
+ }
167
+ }
168
+ }
169
+ catch {
170
+ var.update $retries { value = $retries + 1 }
171
+ util.sleep { value = 2 }
164
172
  }
165
173
  }
166
174
  }
167
- catch {
168
- var.update $retries { value = $retries + 1 }
169
- util.sleep { value = 2 }
170
- }
171
175
  }
172
176
  }
173
177
  ```
@@ -286,11 +286,14 @@ For basic rate limiting setup, see `xanoscript_docs({ topic: "security" })`. Bel
286
286
 
287
287
  ```xs
288
288
  // Different limits by user tier
289
- var $limit {
290
- value = conditional {
291
- if ($auth.tier == "premium") { 1000 }
292
- elseif ($auth.tier == "pro") { 500 }
293
- else { 100 }
289
+ var $limit { value = 100 }
290
+
291
+ conditional {
292
+ if ($auth.tier == "premium") {
293
+ var.update $limit { value = 1000 }
294
+ }
295
+ elseif ($auth.tier == "pro") {
296
+ var.update $limit { value = 500 }
294
297
  }
295
298
  }
296
299
 
@@ -611,6 +611,28 @@ stack {
611
611
  }
612
612
  ```
613
613
 
614
+ ### 13. Multiple constructs in one file
615
+ Each `.xs` file must contain exactly **one** construct. Placing two constructs in the same file causes a parse error.
616
+
617
+ ```xs
618
+ // ❌ Wrong - two constructs in one file
619
+ function "helper_a" {
620
+ stack { ... }
621
+ response = $result
622
+ }
623
+
624
+ function "helper_b" {
625
+ stack { ... }
626
+ response = $result
627
+ }
628
+ ```
629
+
630
+ ```
631
+ // ✅ Correct - one construct per file
632
+ // function/helper_a.xs → contains only "helper_a"
633
+ // function/helper_b.xs → contains only "helper_b"
634
+ ```
635
+
614
636
  ---
615
637
 
616
638
  ## Related Topics
@@ -112,22 +112,42 @@ Working with...
112
112
 
113
113
  > **Note:** There is no `default` filter. Use conditional blocks or `first_notnull`/`first_notempty` instead.
114
114
 
115
- ### String Concatenation with Filters
115
+ ### Parentheses and Filter Precedence
116
116
 
117
- When concatenating strings that use filters, wrap each filtered expression in parentheses:
117
+ > **Rule:** Filters bind greedily to the left. Without parentheses, the parser extends the filter expression into the operator that follows — producing invalid or unexpected results. **When in doubt, wrap `$var|filter` in parentheses.**
118
+
119
+ The filter `|` grabs everything to its right until it hits a boundary. This means `$arr|count > 3` is parsed as `$arr | (length > 3)` — treating `count > 3` as the filter argument, which is invalid.
118
120
 
119
121
  ```xs
120
- // Correct - parentheses around filtered expressions
122
+ // Wrong parser reads filter argument as "count == 0" (invalid)
123
+ if ($arr|count == 0) { ... }
124
+
125
+ // ❌ Wrong — parse error in concatenation
121
126
  var $message {
122
- value = ($status|to_text) ~ ": " ~ ($data|json_encode)
127
+ value = $status|to_text ~ ": " ~ $data|json_encode
123
128
  }
129
+ ```
130
+
131
+ ```xs
132
+ // ✅ Correct — evaluate filter first, then apply operator
133
+
134
+ // ✅ Correct
135
+ if (($arr|count) == 0) { ... }
124
136
 
125
- // Incorrect - missing parentheses causes parse error
137
+ // Correct wrap each filtered expression for concatenation
126
138
  var $message {
127
- value = $status|to_text ~ ": " ~ $data|json_encode
139
+ value = ($status|to_text) ~ ": " ~ ($data|json_encode)
128
140
  }
141
+
142
+ // ✅ Correct — filter result used in arithmetic
143
+ var $total { value = ($prices|sum) + $tax }
144
+
145
+ // ✅ Correct — filter result compared
146
+ var $is_long { value = ($text|strlen) > 100 }
129
147
  ```
130
148
 
149
+ **Summary:** Any time you apply an operator (`>`, `<`, `==`, `!=`, `~`, `+`, `-`, etc.) to a filtered value, wrap the `$var|filter` portion in its own parentheses.
150
+
131
151
  ---
132
152
 
133
153
  ## Conditional Blocks
@@ -150,16 +170,19 @@ conditional {
150
170
 
151
171
  > **Important:** Use `elseif` (one word), not `else if` or `else { if (...) }`. Nested `if` inside `else` blocks is not supported.
152
172
 
153
- ### Conditional as Expression
173
+ ### Conditional with Variable Updates
154
174
 
155
- Use conditional blocks inline to return values:
175
+ Use conditional blocks to update a variable based on conditions:
156
176
 
157
177
  ```xs
158
- var $tier_limit {
159
- value = conditional {
160
- if ($auth.tier == "premium") { 1000 }
161
- elseif ($auth.tier == "pro") { 500 }
162
- else { 100 }
178
+ var $tier_limit { value = 100 }
179
+
180
+ conditional {
181
+ if ($auth.tier == "premium") {
182
+ var.update $tier_limit { value = 1000 }
183
+ }
184
+ elseif ($auth.tier == "pro") {
185
+ var.update $tier_limit { value = 500 }
163
186
  }
164
187
  }
165
188
  ```
@@ -282,7 +305,7 @@ $db.post.date >=? $input.start_date
282
305
  | `unique` | `[1,1,2]\|unique` | `[1,2]` |
283
306
  | `flatten` | `[[1,2],[3]]\|flatten` | `[1,2,3]` |
284
307
  | `shuffle` | `[1,2,3]\|shuffle` | Random order |
285
- | `slice` | `[1,2,3,4]\|slice:1:2` | `[2,3]` |
308
+ | `slice` | `[1,2,3,4]\|slice:1:2` | `[2,3]` — offset 1, length 2 |
286
309
  | `push` | `[1,2]\|push:3` | `[1,2,3]` |
287
310
  | `pop` | `[1,2,3]\|pop` | `3` |
288
311
  | `shift` | `[1,2,3]\|shift` | `1` |
@@ -328,6 +351,30 @@ Generate numeric ranges with the `..` operator:
328
351
  [1,2,3,4]|reduce:$$+$result:0 // 10
329
352
  ```
330
353
 
354
+ ### Array Element Access: `|get` vs `|slice`
355
+
356
+ > **`|get:N`** returns a **single element** by zero-based index.
357
+ > **`|slice:offset:length`** returns a **sub-array**, skipping `offset` elements and returning the next `length` elements.
358
+
359
+ | Method | Use For | Example | Result |
360
+ |--------|---------|---------|--------|
361
+ | `\|get:N` | Single element by index (0-based) | `[10,20,30]\|get:0` | `10` |
362
+ | `\|slice:offset:length` | Sub-array — skip N, take M | `[10,20,30,40]\|slice:1:2` | `[20,30]` |
363
+ | `\|first` | First element | `[10,20,30]\|first` | `10` |
364
+ | `\|last` | Last element | `[10,20,30]\|last` | `30` |
365
+
366
+ ```xs
367
+ // Single element by index
368
+ var $third { value = $items|get:2 } // 0-based: 3rd element
369
+
370
+ // Sub-array: skip first 10, return the next 5
371
+ var $page { value = $items|slice:10:5 }
372
+
373
+ // ⚠️ slice:offset:length — NOT slice:start:end
374
+ // $arr|slice:2:3 → skip 2, return 3 elements (indices 2, 3, 4)
375
+ // NOT "from index 2 to index 3"
376
+ ```
377
+
331
378
  ### Grouping & Indexing
332
379
  ```xs
333
380
  // Group by property
@@ -35,6 +35,46 @@ function "<name>" {
35
35
 
36
36
  ---
37
37
 
38
+ ## Scope Limitations
39
+
40
+ > **Unit tests can only access `$response`** — the value declared in the construct's `response = ...` field. Tests do **not** have access to intermediate stack variables.
41
+
42
+ ```xs
43
+ function "calculate" {
44
+ input { int x }
45
+ stack {
46
+ var $doubled { value = $input.x * 2 } // NOT accessible in tests
47
+ var $result { value = $doubled + 1 } // NOT accessible in tests
48
+ }
49
+ response = $result // This becomes $response in tests
50
+
51
+ test "doubles and adds one" {
52
+ input = { x: 5 }
53
+ // ✅ Can access $response (the declared response)
54
+ expect.to_equal ($response) { value = 11 }
55
+
56
+ // ❌ Stack variables are not in scope — this will fail
57
+ // expect.to_equal ($doubled) { value = 10 }
58
+ }
59
+ }
60
+ ```
61
+
62
+ To assert on intermediate values, expose them through the response:
63
+
64
+ ```xs
65
+ response = { result: $result, doubled: $doubled }
66
+
67
+ test "intermediate values" {
68
+ input = { x: 5 }
69
+ expect.to_equal ($response.doubled) { value = 10 }
70
+ expect.to_equal ($response.result) { value = 11 }
71
+ }
72
+ ```
73
+
74
+ > **Note:** Function stack variables (variables defined in other functions called via `function.run`) are also not accessible — tests are fully isolated to the construct's own declared response.
75
+
76
+ ---
77
+
38
78
  ## Basic Test
39
79
 
40
80
  ```xs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xano/developer-mcp",
3
- "version": "1.0.46",
3
+ "version": "1.0.48",
4
4
  "description": "MCP server and library for Xano development - XanoScript validation, Meta API, Run API, and CLI documentation",
5
5
  "type": "module",
6
6
  "main": "dist/lib.js",