@xano/developer-mcp 1.0.58 → 1.0.60

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.
Files changed (38) hide show
  1. package/README.md +15 -0
  2. package/dist/tools/index.d.ts +9 -0
  3. package/dist/tools/xanoscript_docs.d.ts +9 -0
  4. package/dist/tools/xanoscript_docs.js +27 -0
  5. package/dist/xanoscript.d.ts +5 -1
  6. package/dist/xanoscript.js +70 -6
  7. package/dist/xanoscript.test.js +5 -3
  8. package/dist/xanoscript_docs/README.md +13 -44
  9. package/dist/xanoscript_docs/addons.md +0 -2
  10. package/dist/xanoscript_docs/agents.md +2 -35
  11. package/dist/xanoscript_docs/apis.md +3 -6
  12. package/dist/xanoscript_docs/branch.md +10 -2
  13. package/dist/xanoscript_docs/database.md +3 -7
  14. package/dist/xanoscript_docs/debugging.md +9 -262
  15. package/dist/xanoscript_docs/docs_index.json +22 -0
  16. package/dist/xanoscript_docs/essentials.md +1 -9
  17. package/dist/xanoscript_docs/frontend.md +1 -138
  18. package/dist/xanoscript_docs/functions.md +4 -8
  19. package/dist/xanoscript_docs/mcp-servers.md +1 -2
  20. package/dist/xanoscript_docs/middleware.md +12 -3
  21. package/dist/xanoscript_docs/performance.md +8 -198
  22. package/dist/xanoscript_docs/realtime.md +21 -161
  23. package/dist/xanoscript_docs/run.md +2 -184
  24. package/dist/xanoscript_docs/schema.md +11 -3
  25. package/dist/xanoscript_docs/security.md +82 -313
  26. package/dist/xanoscript_docs/streaming.md +2 -37
  27. package/dist/xanoscript_docs/survival.md +161 -0
  28. package/dist/xanoscript_docs/syntax.md +0 -6
  29. package/dist/xanoscript_docs/tables.md +3 -5
  30. package/dist/xanoscript_docs/tasks.md +1 -3
  31. package/dist/xanoscript_docs/tools.md +1 -3
  32. package/dist/xanoscript_docs/triggers.md +3 -69
  33. package/dist/xanoscript_docs/types.md +3 -4
  34. package/dist/xanoscript_docs/unit-testing.md +1 -55
  35. package/dist/xanoscript_docs/workflow-tests.md +8 -35
  36. package/dist/xanoscript_docs/working.md +667 -0
  37. package/dist/xanoscript_docs/workspace.md +10 -2
  38. package/package.json +1 -1
@@ -0,0 +1,667 @@
1
+ ---
2
+ applyTo: "**/*.xs"
3
+ ---
4
+
5
+ # XanoScript Working Reference
6
+
7
+ > Complete reference for common XanoScript development. Call `xanoscript_docs({ topic: "<topic>" })` for specialized topics.
8
+
9
+ ## Quick Reference
10
+
11
+ ### Mental Model
12
+
13
+ - Declarative block syntax, not imperative statements
14
+ - One construct per `.xs` file (function, query, table, task, agent, tool, etc.)
15
+ - Pipe `|` applies filters, `~` concatenates strings, `$input.x` accesses parameters
16
+
17
+ ### Type Names (CRITICAL)
18
+
19
+ | Use This | NOT This |
20
+ |----------|----------|
21
+ | `text` | ~~string~~ |
22
+ | `int` | ~~integer~~ |
23
+ | `bool` | ~~boolean~~ |
24
+ | `decimal` | ~~float/number~~ |
25
+ | `type[]` | ~~array/list~~ |
26
+ | `json` | ~~object/any~~ |
27
+
28
+ ### Reserved Names
29
+
30
+ Cannot be used as variable names: `$response`, `$output`, `$input`, `$auth`, `$env`, `$db`, `$this`, `$result`, `$index`
31
+
32
+ ### Variable Access
33
+
34
+ ```
35
+ $input.field — input parameters (ALWAYS use $input. prefix, never bare $field)
36
+ $var.field — stack variables (shorthand $field also works)
37
+ $auth.id — authenticated user
38
+ $env.MY_VAR — environment variable
39
+ $db.table.field — database field reference (in where clauses)
40
+ $this — current item in loops/maps
41
+ ```
42
+
43
+ ### Operators
44
+
45
+ | Category | Operators |
46
+ |----------|-----------|
47
+ | Comparison | `==`, `!=`, `>`, `<`, `>=`, `<=` |
48
+ | Logical | `&&`, `\|\|`, `!` |
49
+ | Math | `+`, `-`, `*`, `/`, `%` |
50
+ | String concat | `~` |
51
+ | Null-safe | `==?`, `!=?`, `>=?`, `<=?` (ignore if null) |
52
+ | Nullish coalescing | `??` |
53
+ | Ternary | `$cond ? "yes" : "no"` |
54
+
55
+ ---
56
+
57
+ ## Common Mistakes
58
+
59
+ ### 1. Using `else if` instead of `elseif`
60
+ ```xs
61
+ // WRONG: conditional { if (...) { } else if (...) { } }
62
+ // RIGHT:
63
+ conditional {
64
+ if (...) { }
65
+ elseif (...) { }
66
+ }
67
+ ```
68
+
69
+ ### 2. Missing parentheses in filter concatenation
70
+ ```xs
71
+ // WRONG: var $msg { value = $status|to_text ~ " - " ~ $data|json_encode }
72
+ // RIGHT:
73
+ var $msg { value = ($status|to_text) ~ " - " ~ ($data|json_encode) }
74
+ ```
75
+
76
+ ### 3. Missing parentheses in filter comparisons
77
+ ```xs
78
+ // WRONG: if ($array|count > 0) { }
79
+ // RIGHT:
80
+ if (($array|count) > 0) { }
81
+ ```
82
+
83
+ ### 4. Using `body` instead of `params` for api.request
84
+ ```xs
85
+ // WRONG: api.request { url = "..." method = "POST" body = $payload }
86
+ // RIGHT:
87
+ api.request { url = "..." method = "POST" params = $payload } as $result
88
+ ```
89
+
90
+ ### 5. Using `default` filter (doesn't exist)
91
+ ```xs
92
+ // WRONG: var $value { value = $input.optional|default:"fallback" }
93
+ // RIGHT:
94
+ var $value { value = $input.optional ?? "fallback" }
95
+ // or: var $value { value = $input.optional|first_notnull:"fallback" }
96
+ ```
97
+
98
+ ### 6. Using reserved variable names
99
+ ```xs
100
+ // WRONG: var $response { value = "test" }
101
+ // RIGHT:
102
+ var $api_response { value = "test" }
103
+ ```
104
+
105
+ ### 7. Wrong type names
106
+ ```xs
107
+ // WRONG: input { boolean active integer count string name }
108
+ // RIGHT:
109
+ input { bool active int count text name }
110
+ ```
111
+
112
+ ### 8. Object literal syntax (using = instead of :)
113
+ ```xs
114
+ // WRONG: var $data { value = { customer = $id } }
115
+ // RIGHT:
116
+ var $data { value = { customer: $id } }
117
+ ```
118
+
119
+ ### 9. Block properties vs object literals
120
+ ```xs
121
+ // Block properties: = with NO commas, separate lines
122
+ throw {
123
+ name = "Error"
124
+ value = "message"
125
+ }
126
+
127
+ // Object literals: : with commas
128
+ var $obj { value = { name: "Alice", age: 30 } }
129
+ ```
130
+
131
+ ### 10. Using $env in run.job input blocks
132
+ ```xs
133
+ // WRONG: run.job "my_job" { input { text api_key = $env.API_KEY } }
134
+ // RIGHT — access $env in the stack:
135
+ run.job "my_job" { stack { var $api_key { value = $env.API_KEY } } }
136
+ ```
137
+
138
+ ### 11. Using `object` type without schema
139
+ ```xs
140
+ // WRONG: input { object data }
141
+ // RIGHT — use json for arbitrary data:
142
+ input { json data }
143
+ // Or define a schema:
144
+ input { object data { schema { text name int id } } }
145
+ ```
146
+
147
+ ### 12. While loop outside of stack block
148
+ ```xs
149
+ // WRONG: while (true) { each { ... } }
150
+ // RIGHT:
151
+ stack { while (true) { each { ... } } }
152
+ ```
153
+
154
+ ### 13. Multiple constructs in one file
155
+ ```
156
+ // WRONG — two constructs in one file:
157
+ // function "helper_a" { ... }
158
+ // function "helper_b" { ... }
159
+ // RIGHT — one construct per file (function/helper_a.xs, function/helper_b.xs)
160
+ ```
161
+
162
+ ---
163
+
164
+ ## Filters Reference
165
+
166
+ ### String Filters
167
+ trim: remove whitespace — `$s|trim`
168
+ ltrim, rtrim: left/right trim — `$s|ltrim`
169
+ to_lower, to_upper: case — `$s|to_lower`
170
+ capitalize: capitalize first — `$s|capitalize`
171
+ strlen: string length — `$s|strlen` (NOT count)
172
+ substr:start:len: substring — `$s|substr:0:5` (NOT slice)
173
+ split:delim: split to array — `$s|split:","`
174
+ replace:old:new: replace — `$s|replace:"foo":"bar"`
175
+ contains:str: check substring — `$s|contains:"@"`
176
+ starts_with, ends_with: prefix/suffix — `$s|starts_with:"http"`
177
+ pad_start:len:char, pad_end: pad — `$s|pad_start:5:"0"`
178
+
179
+ ### Array Filters
180
+ first, last: endpoints — `$arr|first`
181
+ count: array length — `$arr|count` (NOT strlen)
182
+ slice:start:end: sub-array — `$arr|slice:0:3` (NOT substr)
183
+ reverse: reverse — `$arr|reverse`
184
+ sort:field:dir: sort — `$arr|sort:"name":"asc"`
185
+ unique: deduplicate — `$arr|unique`
186
+ flatten: flatten nested — `$arr|flatten`
187
+ map:expr: transform — `$arr|map:$$.name`
188
+ filter:expr: keep matching — `$arr|filter:$$.active`
189
+ find:expr: first match — `$arr|find:$$.id == 5`
190
+ some:expr: any match? — `$arr|some:$$.active`
191
+ every:expr: all match? — `$arr|every:$$.valid`
192
+ reduce:init:expr: accumulate — `$arr|reduce:0:$$ + $$2`
193
+ join:delim: array to string — `$arr|join:","`
194
+ push:val: append — `$arr|push:"new"`
195
+
196
+ ### Object Filters
197
+ get:key: get property — `$obj|get:"key"` or `$obj|get:"key":"default"`
198
+ set:key:val: set property — `$obj|set:"key":"value"`
199
+ has:key: check key — `$obj|has:"key"`
200
+ keys: get keys — `$obj|keys`
201
+ values: get values — `$obj|values`
202
+ set_ifnotnull:key:val: set if not null — `$obj|set_ifnotnull:"k":$val`
203
+
204
+ ### Type Conversion
205
+ to_text: to string — `$n|to_text`
206
+ to_int: to integer — `$s|to_int`
207
+ to_decimal: to float — `$s|to_decimal`
208
+ to_bool: to boolean — `$s|to_bool`
209
+ json_encode: to JSON string — `$obj|json_encode`
210
+ json_decode: from JSON string — `$s|json_decode`
211
+
212
+ ### Null/Empty Handling
213
+ is_null: check null — `$v|is_null`
214
+ is_empty: check empty — `$v|is_empty`
215
+ first_notnull:default: fallback — `$v|first_notnull:"default"`
216
+ first_notempty:default: fallback — `$v|first_notempty:"fallback"`
217
+ Nullish coalescing: `$v ?? "default"`
218
+
219
+ ### Date/Time
220
+ now — current timestamp
221
+ now|format_timestamp:"Y-m-d":"UTC" — format
222
+ now|transform_timestamp:"-1 day" — offset
223
+ now|to_ms — milliseconds
224
+
225
+ ### Math
226
+ round:precision — `$n|round:2`
227
+ ceil, floor — `$n|ceil`
228
+ abs — `$n|abs`
229
+ min:val, max:val — `$n|min:0`
230
+ sum — `$arr|sum` (sum array of numbers)
231
+ avg — `$arr|avg`
232
+
233
+ ---
234
+
235
+ ## Variables & Input Blocks
236
+
237
+ ```xs
238
+ // Declare
239
+ var $name { value = "initial" }
240
+ var $items { value = [] }
241
+ var $data { value = { key: "value" } }
242
+
243
+ // Update
244
+ var.update $name { value = "new value" }
245
+
246
+ // Input block modifiers
247
+ input {
248
+ text name // required, not nullable
249
+ text? bio // required, nullable (can send null)
250
+ text role?="user" // optional with default
251
+ text? note? // optional and nullable
252
+ email contact filters=trim // with validation filter
253
+ text[] tags // array type
254
+ object address { // nested object
255
+ schema {
256
+ text street
257
+ text city
258
+ }
259
+ }
260
+ }
261
+ ```
262
+
263
+ Common validation filters: `trim`, `to_lower`, `to_upper`, `sanitize_html`, `strip_tags`
264
+
265
+ ---
266
+
267
+ ## Conditional Logic
268
+
269
+ ```xs
270
+ // if / elseif / else (MUST use elseif, NOT else if)
271
+ conditional {
272
+ if ($input.age >= 18) { var $status { value = "adult" } }
273
+ elseif ($input.age >= 13) { var $status { value = "teen" } }
274
+ else { var $status { value = "child" } }
275
+ }
276
+
277
+ // switch
278
+ switch ($input.status) {
279
+ case ("active") { var $msg { value = "Active" } } break
280
+ case ("pending") { var $msg { value = "Pending" } } break
281
+ default { var $msg { value = "Unknown" } }
282
+ }
283
+ // Note: break goes AFTER the closing } of each case
284
+
285
+ // precondition (guard clause — stops execution if false)
286
+ precondition ($input.id > 0) {
287
+ error_type = "inputerror"
288
+ error = "ID must be positive"
289
+ }
290
+
291
+ // early return
292
+ conditional {
293
+ if ($input.skip) {
294
+ return { value = null }
295
+ }
296
+ }
297
+ ```
298
+
299
+ ### Error Types
300
+
301
+ | Type | HTTP Status | Use Case |
302
+ |------|-------------|----------|
303
+ | `inputerror` | 400 | Invalid input data |
304
+ | `accessdenied` | 403 | Authorization failure |
305
+ | `notfound` | 404 | Resource doesn't exist |
306
+ | `standard` | 500 | General errors |
307
+
308
+ ---
309
+
310
+ ## Loops
311
+
312
+ ```xs
313
+ // foreach — iterate array
314
+ foreach ($input.items) {
315
+ each as $item {
316
+ debug.log { value = $item.name }
317
+ }
318
+ }
319
+
320
+ // for — iterate N times
321
+ for (10) {
322
+ each as $idx {
323
+ debug.log { value = $idx }
324
+ }
325
+ }
326
+
327
+ // while — MUST be inside stack block
328
+ stack {
329
+ var $counter { value = 0 }
330
+ while ($counter < 10) {
331
+ each {
332
+ var.update $counter { value = $counter + 1 }
333
+ }
334
+ }
335
+ }
336
+
337
+ // map/filter (inline, returns new array)
338
+ var $names { value = $items|map:$$.name }
339
+ var $active { value = $items|filter:$$.is_active }
340
+ var $found { value = $items|find:$$.id == $input.target_id }
341
+
342
+ // break/continue
343
+ foreach ($items) {
344
+ each as $item {
345
+ conditional {
346
+ if ($item.skip) { continue }
347
+ if ($item.done) { break }
348
+ }
349
+ }
350
+ }
351
+ ```
352
+
353
+ ---
354
+
355
+ ## Functions
356
+
357
+ ```xs
358
+ // Define
359
+ function "calculate_total" {
360
+ input {
361
+ decimal[] prices
362
+ decimal tax_rate?=0.1
363
+ }
364
+ stack {
365
+ var $subtotal { value = $input.prices|sum }
366
+ var $tax { value = $subtotal * $input.tax_rate }
367
+ }
368
+ response = { subtotal: $subtotal, tax: $tax, total: $subtotal + $tax }
369
+ }
370
+
371
+ // Call
372
+ function.run "calculate_total" {
373
+ input = { prices: [10, 20, 30], tax_rate: 0.08 }
374
+ } as $result
375
+
376
+ // Async (fire and forget)
377
+ function.run "send_notification" {
378
+ async = true
379
+ input = { user_id: $auth.id, message: "Done" }
380
+ }
381
+ ```
382
+
383
+ > For async patterns, recursion, and advanced function features: `xanoscript_docs({ topic: "functions" })`
384
+
385
+ ---
386
+
387
+ ## Database Operations
388
+
389
+ ### Choosing an Operation
390
+ ```
391
+ Read: db.get (single by ID) | db.has (exists?) | db.query (filtered list)
392
+ Write: db.add (insert) | db.edit (update known fields) | db.patch (dynamic fields) | db.add_or_edit (upsert)
393
+ Delete: db.del (single) | db.truncate (all)
394
+ ```
395
+
396
+ ### db.get — Single record
397
+ ```xs
398
+ db.get "user" { field_name = "id" field_value = $input.user_id } as $user
399
+ db.get "user" { field_name = "email" field_value = $input.email } as $user
400
+ ```
401
+
402
+ ### db.has — Check existence
403
+ ```xs
404
+ db.has "user" { field_name = "email" field_value = $input.email } as $exists
405
+ ```
406
+
407
+ ### db.query — Filtered list
408
+ ```xs
409
+ db.query "product" {
410
+ where = $db.product.is_active == true && $db.product.price > 0
411
+ sort = { created_at: "desc" }
412
+ return = { type: "list", paging: { page: $input.page, per_page: 25, totals: true } }
413
+ } as $products
414
+
415
+ // Return types: "list" (default), "single", "count", "exists"
416
+ // Null-safe where: $db.product.category ==? $input.category (ignores if null)
417
+ // String matching: $db.product.name includes "phone"
418
+ // Array contains: $db.product.tags contains "featured"
419
+ ```
420
+
421
+ ### db.query with join
422
+ ```xs
423
+ db.query "comment" {
424
+ join = {
425
+ post: { table: "post", type: "inner", where: $db.comment.post_id == $db.post.id }
426
+ }
427
+ eval = { post_title: $db.post.title }
428
+ where = $db.post.author_id == $auth.id
429
+ } as $comments
430
+ ```
431
+
432
+ ### db.add — Insert
433
+ ```xs
434
+ db.add "product" {
435
+ data = { name: $input.name, price: $input.price, created_at: now }
436
+ } as $product
437
+ ```
438
+
439
+ ### db.edit — Update (inline data)
440
+ ```xs
441
+ db.edit "product" {
442
+ field_name = "id"
443
+ field_value = $input.product_id
444
+ data = { name: $input.name, price: $input.price, updated_at: now }
445
+ } as $product
446
+ ```
447
+
448
+ ### db.patch — Update (dynamic fields)
449
+ ```xs
450
+ var $updates { value = { updated_at: now } }
451
+ conditional {
452
+ if ($input.name != null) {
453
+ var.update $updates { value = $updates|set:"name":$input.name }
454
+ }
455
+ }
456
+ db.patch "product" { field_name = "id" field_value = $input.id data = $updates } as $product
457
+ ```
458
+
459
+ ### db.del — Delete
460
+ ```xs
461
+ db.del "product" { field_name = "id" field_value = $input.product_id }
462
+ ```
463
+
464
+ ### db.transaction — Atomic operations
465
+ ```xs
466
+ db.transaction {
467
+ stack {
468
+ db.add "order" { data = { user_id: $auth.id, total: $total } } as $order
469
+ foreach ($input.items) {
470
+ each as $item {
471
+ db.add "order_item" { data = { order_id: $order.id, product_id: $item.product_id, quantity: $item.quantity } }
472
+ }
473
+ }
474
+ }
475
+ }
476
+ ```
477
+
478
+ > For bulk operations, direct SQL, vector search, and advanced queries: `xanoscript_docs({ topic: "database" })`
479
+
480
+ ---
481
+
482
+ ## API Endpoints
483
+
484
+ ```xs
485
+ // GET endpoint
486
+ query "users" verb=GET {
487
+ api_group = "users"
488
+ auth = "none" // "none", "user", "session", "api_key"
489
+ input { int page?=1 int per_page?=25 }
490
+ stack {
491
+ db.query "user" {
492
+ where = $db.user.is_active == true
493
+ sort = { created_at: "desc" }
494
+ return = { type: "list", paging: { page: $input.page, per_page: $input.per_page } }
495
+ } as $users
496
+ }
497
+ response = $users
498
+ }
499
+
500
+ // GET single
501
+ query "users/{user_id}" verb=GET {
502
+ api_group = "users"
503
+ auth = "user"
504
+ input { int user_id }
505
+ stack {
506
+ db.get "user" { field_name = "id" field_value = $input.user_id } as $user
507
+ precondition ($user != null) { error_type = "notfound" error = "User not found" }
508
+ }
509
+ response = $user
510
+ }
511
+
512
+ // POST create
513
+ query "users" verb=POST {
514
+ api_group = "users"
515
+ auth = "user"
516
+ input { text name email email }
517
+ stack {
518
+ db.has "user" { field_name = "email" field_value = $input.email } as $exists
519
+ precondition (!$exists) { error_type = "inputerror" error = "Email already taken" }
520
+ db.add "user" { data = { name: $input.name, email: $input.email, created_at: now } } as $user
521
+ }
522
+ response = $user
523
+ }
524
+
525
+ // PATCH update
526
+ query "users/{user_id}" verb=PATCH {
527
+ api_group = "users"
528
+ auth = "user"
529
+ input { int user_id text? name? email? email? }
530
+ stack {
531
+ db.get "user" { field_name = "id" field_value = $input.user_id } as $user
532
+ precondition ($user != null) { error_type = "notfound" error = "Not found" }
533
+ precondition ($user.id == $auth.id) { error_type = "accessdenied" error = "Not your record" }
534
+ var $updates { value = { updated_at: now } }
535
+ conditional {
536
+ if ($input.name != null) { var.update $updates { value = $updates|set:"name":$input.name } }
537
+ }
538
+ db.patch "user" { field_name = "id" field_value = $input.user_id data = $updates } as $updated
539
+ }
540
+ response = $updated
541
+ }
542
+
543
+ // DELETE
544
+ query "users/{user_id}" verb=DELETE {
545
+ api_group = "users"
546
+ auth = "user"
547
+ input { int user_id }
548
+ stack {
549
+ db.get "user" { field_name = "id" field_value = $input.user_id } as $user
550
+ precondition ($user != null) { error_type = "notfound" error = "Not found" }
551
+ precondition ($user.id == $auth.id) { error_type = "accessdenied" error = "Not yours" }
552
+ db.del "user" { field_name = "id" field_value = $input.user_id }
553
+ }
554
+ response = { success: true }
555
+ }
556
+ ```
557
+
558
+ > For API groups, rate limiting, file uploads, and custom headers: `xanoscript_docs({ topic: "apis" })`
559
+
560
+ ---
561
+
562
+ ## Error Handling
563
+
564
+ ```xs
565
+ // Precondition (guard — stops execution if false)
566
+ precondition ($input.email|contains:"@") {
567
+ error_type = "inputerror"
568
+ error = "Invalid email"
569
+ }
570
+
571
+ // Try/catch
572
+ try_catch {
573
+ try {
574
+ api.request { url = "https://api.example.com/data" method = "GET" } as $result
575
+ }
576
+ catch {
577
+ var $result { value = { response: { result: null } } }
578
+ }
579
+ }
580
+
581
+ // Throw custom error
582
+ throw {
583
+ name = "ValidationError"
584
+ value = "Custom error message"
585
+ }
586
+ ```
587
+
588
+ ---
589
+
590
+ ## External API Calls
591
+
592
+ ```xs
593
+ api.request {
594
+ url = "https://api.example.com/endpoint"
595
+ method = "POST"
596
+ params = $payload // NOTE: "params" for body, NOT "body"
597
+ headers = [
598
+ "Content-Type: application/json",
599
+ "Authorization: Bearer " ~ $env.API_KEY
600
+ ]
601
+ timeout = 30
602
+ } as $api_result
603
+
604
+ // Response structure:
605
+ // $api_result.response.status → HTTP status code
606
+ // $api_result.response.result → Parsed response body
607
+ // $api_result.response.headers → Response headers
608
+ ```
609
+
610
+ ---
611
+
612
+ ## Common Patterns
613
+
614
+ ### Authentication Check
615
+ ```xs
616
+ precondition ($auth.id != null) {
617
+ error_type = "accessdenied"
618
+ error = "Authentication required"
619
+ }
620
+ ```
621
+
622
+ ### Optional Field Handling
623
+ ```xs
624
+ var $data {
625
+ value = { required: $input.required }|set_ifnotnull:"optional":$input.optional
626
+ }
627
+ ```
628
+
629
+ ### Building Complex Objects
630
+ ```xs
631
+ var $result { value = { user: $user, posts: $posts, count: ($posts|count) } }
632
+ ```
633
+
634
+ ### String Concatenation with Filters
635
+ ```xs
636
+ var $msg { value = ($status|to_text) ~ ": " ~ ($data|json_encode) }
637
+ ```
638
+
639
+ ---
640
+
641
+ ## Topic Index
642
+
643
+ | Topic | Description | Size |
644
+ |-------|-------------|------|
645
+ | `essentials` | Common patterns and mistakes | ~15KB |
646
+ | `syntax` | Operators, all filters, system variables | ~17KB |
647
+ | `syntax/string-filters` | String filters, regex, encoding | ~6KB |
648
+ | `syntax/array-filters` | Array filters, functional operations | ~7KB |
649
+ | `types` | Type system, input validation | ~11KB |
650
+ | `database` | All db.* operations, joins, transactions | ~13KB |
651
+ | `functions` | Function stacks, async, recursion | ~9KB |
652
+ | `apis` | HTTP endpoints, authentication | ~12KB |
653
+ | `tables` | Database schema definitions | ~7KB |
654
+ | `tasks` | Scheduled/cron jobs | ~6KB |
655
+ | `triggers` | Event handlers (table, realtime, agent, MCP) | ~17KB |
656
+ | `agents` | AI agent configuration | ~9KB |
657
+ | `tools` | AI tools for agents | ~7KB |
658
+ | `mcp-servers` | MCP server definitions | ~4KB |
659
+ | `security` | Auth patterns, encryption | ~12KB |
660
+ | `integrations` | External APIs, cloud storage, Redis | ~varies |
661
+ | `unit-testing` | Unit tests, mocks, assertions | ~10KB |
662
+ | `workflow-tests` | E2E workflow tests | ~11KB |
663
+ | `debugging` | debug.log, debug.stop | ~6KB |
664
+ | `performance` | Caching, query optimization | ~8KB |
665
+ | `middleware` | Request/response interceptors | ~6KB |
666
+ | `realtime` | WebSocket channels and events | ~9KB |
667
+ | `streaming` | Large file/data streaming | ~8KB |
@@ -207,5 +207,13 @@ These variables are automatically available without configuration:
207
207
  1. **Never commit secrets** - Use environment variables for API keys and credentials
208
208
  2. **Use descriptive names** - Environment variable names should be self-documenting
209
209
  3. **Enable performance tracking** - Helps identify bottlenecks in production
210
- 4. **Set meaningful canonical paths** - Makes realtime channel URLs predictable
211
- 5. **Document your workspace** - Use the description field for team reference
210
+
211
+ ---
212
+
213
+ ## Related Topics
214
+
215
+ | Topic | Description |
216
+ |-------|-------------|
217
+ | `branch` | Branch-level configuration |
218
+ | `realtime` | Realtime channel configuration |
219
+ | `security` | Environment variable security practices |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xano/developer-mcp",
3
- "version": "1.0.58",
3
+ "version": "1.0.60",
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",